From 949a3f0c2ea1804577ff69d92f811ced71a79976 Mon Sep 17 00:00:00 2001 From: Jerome Bakker Date: Wed, 3 Oct 2012 14:02:11 +0200 Subject: correct singual usage of type in elgg_get_entities_* functions --- engine/lib/deprecated-1.8.php | 4 ++-- engine/lib/group.php | 2 +- engine/lib/notification.php | 2 +- engine/lib/objects.php | 2 +- engine/lib/sites.php | 2 +- engine/lib/users.php | 6 +++--- engine/tests/api/entity_getter_functions.php | 4 ++-- mod/custom_index/index.php | 2 +- mod/file/pages/file/owner.php | 2 +- mod/file/pages/file/search.php | 2 +- mod/file/pages/file/world.php | 2 +- mod/groups/lib/groups.php | 4 ++-- mod/groups/start.php | 2 +- mod/groups/views/default/groups/sidebar/featured.php | 2 +- mod/groups/views/default/groups/sidebar/members.php | 2 +- mod/notifications/actions/groupsave.php | 2 +- mod/notifications/groups.php | 2 +- mod/notifications/index.php | 2 +- .../views/default/forms/notificationsettings/groupsave.php | 2 +- .../views/default/notifications/subscriptions/forminternals.php | 2 +- mod/pages/pages/pages/owner.php | 2 +- mod/pages/pages/pages/world.php | 2 +- .../views/default/admin/administer_utilities/reportedcontent.php | 2 +- .../views/default/widgets/reportedcontent/content.php | 2 +- mod/thewire/views/default/thewire/profile_status.php | 2 +- 25 files changed, 30 insertions(+), 30 deletions(-) diff --git a/engine/lib/deprecated-1.8.php b/engine/lib/deprecated-1.8.php index 4b9d41543..e967df3dd 100644 --- a/engine/lib/deprecated-1.8.php +++ b/engine/lib/deprecated-1.8.php @@ -2566,7 +2566,7 @@ $owner_guid = "", $owner_relationship = "") { 'relationship' => $owner_relationship, 'relationship_guid' => $owner_guid[0], 'inverse_relationship' => FALSE, - 'types' => 'user', + 'type' => 'user', 'subtypes' => $subtype, 'limit' => 9999)) ) { @@ -2721,7 +2721,7 @@ function get_site_collections($site_guid, $subtype = "", $limit = 10, $offset = 'relationship' => 'member_of_site', 'relationship_guid' => $site_guid, 'inverse_relationship' => TRUE, - 'types' => 'collection', + 'type' => 'collection', 'subtypes' => $subtype, 'limit' => $limit, 'offset' => $offset diff --git a/engine/lib/group.php b/engine/lib/group.php index feb1f1e7f..da5fb8656 100644 --- a/engine/lib/group.php +++ b/engine/lib/group.php @@ -169,7 +169,7 @@ function get_group_members($group_guid, $limit = 10, $offset = 0, $site_guid = 0 'relationship' => 'member', 'relationship_guid' => $group_guid, 'inverse_relationship' => TRUE, - 'types' => 'user', + 'type' => 'user', 'limit' => $limit, 'offset' => $offset, 'count' => $count, diff --git a/engine/lib/notification.php b/engine/lib/notification.php index 18faff27f..738295588 100644 --- a/engine/lib/notification.php +++ b/engine/lib/notification.php @@ -491,7 +491,7 @@ function object_notifications($event, $object_type, $object) { 'relationship' => 'notify' . $method, 'relationship_guid' => $object->container_guid, 'inverse_relationship' => TRUE, - 'types' => 'user', + 'type' => 'user', 'limit' => 99999 )); diff --git a/engine/lib/objects.php b/engine/lib/objects.php index f186c66cb..1dd5c5ec7 100644 --- a/engine/lib/objects.php +++ b/engine/lib/objects.php @@ -92,7 +92,7 @@ function get_object_sites($object_guid, $limit = 10, $offset = 0) { return elgg_get_entities_from_relationship(array( 'relationship' => 'member_of_site', 'relationship_guid' => $object_guid, - 'types' => 'site', + 'type' => 'site', 'limit' => $limit, 'offset' => $offset )); diff --git a/engine/lib/sites.php b/engine/lib/sites.php index 850092cad..805dacd2d 100644 --- a/engine/lib/sites.php +++ b/engine/lib/sites.php @@ -183,7 +183,7 @@ function get_site_objects($site_guid, $subtype = "", $limit = 10, $offset = 0) { 'relationship' => 'member_of_site', 'relationship_guid' => $site_guid, 'inverse_relationship' => TRUE, - 'types' => 'object', + 'type' => 'object', 'subtypes' => $subtype, 'limit' => $limit, 'offset' => $offset diff --git a/engine/lib/users.php b/engine/lib/users.php index 527eff3cd..7dc6b7c2d 100644 --- a/engine/lib/users.php +++ b/engine/lib/users.php @@ -301,7 +301,7 @@ function get_user_sites($user_guid, $limit = 10, $offset = 0) { 'relationship' => 'member_of_site', 'relationship_guid' => $user_guid, 'inverse_relationship' => FALSE, - 'types' => 'site', + 'type' => 'site', 'limit' => $limit, 'offset' => $offset, )); @@ -386,7 +386,7 @@ $offset = 0) { return elgg_get_entities_from_relationship(array( 'relationship' => 'friend', 'relationship_guid' => $user_guid, - 'types' => 'user', + 'type' => 'user', 'subtypes' => $subtype, 'limit' => $limit, 'offset' => $offset @@ -410,7 +410,7 @@ $offset = 0) { 'relationship' => 'friend', 'relationship_guid' => $user_guid, 'inverse_relationship' => TRUE, - 'types' => 'user', + 'type' => 'user', 'subtypes' => $subtype, 'limit' => $limit, 'offset' => $offset diff --git a/engine/tests/api/entity_getter_functions.php b/engine/tests/api/entity_getter_functions.php index 9db248de9..d255c2e67 100644 --- a/engine/tests/api/entity_getter_functions.php +++ b/engine/tests/api/entity_getter_functions.php @@ -2648,7 +2648,7 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest { $name = 'test_annotation_' . rand(0, 9999); $values = array(); $options = array( - 'types' => 'object', + 'type' => 'object', 'subtypes' => $subtypes, 'limit' => 5 ); @@ -2687,7 +2687,7 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest { $order = array_keys($values); $options = array( - 'types' => 'object', + 'type' => 'object', 'subtypes' => $subtypes, 'limit' => 5, 'annotation_name' => $name, 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/file/pages/file/owner.php b/mod/file/pages/file/owner.php index fb87af1b2..1409a404e 100644 --- a/mod/file/pages/file/owner.php +++ b/mod/file/pages/file/owner.php @@ -36,7 +36,7 @@ $title = elgg_echo("file:user", array($owner->name)); // List files $content = elgg_list_entities(array( - 'types' => 'object', + 'type' => 'object', 'subtypes' => 'file', 'container_guid' => $owner->guid, 'limit' => 10, diff --git a/mod/file/pages/file/search.php b/mod/file/pages/file/search.php index 402a28933..35f8a7db7 100644 --- a/mod/file/pages/file/search.php +++ b/mod/file/pages/file/search.php @@ -74,7 +74,7 @@ if ($listtype == "gallery") { } $params = array( - 'types' => 'object', + 'type' => 'object', 'subtypes' => 'file', 'container_guid' => $page_owner_guid, 'limit' => $limit, diff --git a/mod/file/pages/file/world.php b/mod/file/pages/file/world.php index 770dfd6e8..8cf8ad138 100644 --- a/mod/file/pages/file/world.php +++ b/mod/file/pages/file/world.php @@ -14,7 +14,7 @@ $limit = get_input("limit", 10); $title = elgg_echo('file:all'); $content = elgg_list_entities(array( - 'types' => 'object', + 'type' => 'object', 'subtypes' => 'file', 'limit' => $limit, 'full_view' => FALSE diff --git a/mod/groups/lib/groups.php b/mod/groups/lib/groups.php index 505cacd01..a9eb6e843 100644 --- a/mod/groups/lib/groups.php +++ b/mod/groups/lib/groups.php @@ -79,7 +79,7 @@ function groups_search_page() { $params = array( 'metadata_name' => 'interests', 'metadata_value' => $tag, - 'types' => 'group', + 'type' => 'group', 'full_view' => FALSE, ); $content = elgg_list_entities_from_metadata($params); @@ -351,7 +351,7 @@ function groups_handle_members_page($guid) { 'relationship' => 'member', 'relationship_guid' => $group->guid, 'inverse_relationship' => true, - 'types' => 'user', + 'type' => 'user', 'limit' => 20, )); diff --git a/mod/groups/start.php b/mod/groups/start.php index c591410c5..9689802eb 100644 --- a/mod/groups/start.php +++ b/mod/groups/start.php @@ -979,7 +979,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/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/notifications/actions/groupsave.php b/mod/notifications/actions/groupsave.php index 7838f7e63..c646c1885 100644 --- a/mod/notifications/actions/groupsave.php +++ b/mod/notifications/actions/groupsave.php @@ -21,7 +21,7 @@ $groups = array(); $options = array( 'relationship' => 'member', 'relationship_guid' => $user->guid, - 'types' => 'group', + 'type' => 'group', 'limit' => 9999, ); if ($groupmemberships = elgg_get_entities_from_relationship($options)) { diff --git a/mod/notifications/groups.php b/mod/notifications/groups.php index 3347d4054..d29c43e1f 100644 --- a/mod/notifications/groups.php +++ b/mod/notifications/groups.php @@ -28,7 +28,7 @@ $people = array(); $groupmemberships = elgg_get_entities_from_relationship(array( 'relationship' => 'member', 'relationship_guid' => $user->guid, - 'types' => 'group', + 'type' => 'group', 'limit' => 9999, )); diff --git a/mod/notifications/index.php b/mod/notifications/index.php index cd1857f04..ff03cb274 100644 --- a/mod/notifications/index.php +++ b/mod/notifications/index.php @@ -27,7 +27,7 @@ $people = array(); if ($people_ents = elgg_get_entities_from_relationship(array( 'relationship' => 'notify', 'relationship_guid' => $user->guid, - 'types' => 'user', + 'type' => 'user', 'limit' => 99999, ))) { diff --git a/mod/notifications/views/default/forms/notificationsettings/groupsave.php b/mod/notifications/views/default/forms/notificationsettings/groupsave.php index 168639ab2..64db8f533 100644 --- a/mod/notifications/views/default/forms/notificationsettings/groupsave.php +++ b/mod/notifications/views/default/forms/notificationsettings/groupsave.php @@ -15,7 +15,7 @@ foreach ($NOTIFICATION_HANDLERS as $method => $foo) { $subsbig[$method] = elgg_get_entities_from_relationship(array( 'relationship' => 'notify' . $method, 'relationship_guid' => $user->guid, - 'types' => 'group', + 'type' => 'group', 'limit' => 99999, )); $tmparray = array(); diff --git a/mod/notifications/views/default/notifications/subscriptions/forminternals.php b/mod/notifications/views/default/notifications/subscriptions/forminternals.php index 11f266303..57fa62405 100644 --- a/mod/notifications/views/default/notifications/subscriptions/forminternals.php +++ b/mod/notifications/views/default/notifications/subscriptions/forminternals.php @@ -31,7 +31,7 @@ foreach($NOTIFICATION_HANDLERS as $method => $foo) { $subsbig[$method] = elgg_get_entities_from_relationship(array( 'relationship' => 'notify' . $method, 'relationship_guid' => $user->guid, - 'types' => 'user', + 'type' => 'user', 'limit' => 99999, )); } diff --git a/mod/pages/pages/pages/owner.php b/mod/pages/pages/pages/owner.php index b29332ee1..a061225e2 100644 --- a/mod/pages/pages/pages/owner.php +++ b/mod/pages/pages/pages/owner.php @@ -20,7 +20,7 @@ elgg_push_breadcrumb($owner->name); elgg_register_title_button(); $content = elgg_list_entities(array( - 'types' => 'object', + 'type' => 'object', 'subtypes' => '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..e5a29eb63 100644 --- a/mod/pages/pages/pages/world.php +++ b/mod/pages/pages/pages/world.php @@ -13,7 +13,7 @@ elgg_push_breadcrumb(elgg_echo('pages')); elgg_register_title_button(); $content = elgg_list_entities(array( - 'types' => 'object', + 'type' => 'object', 'subtypes' => 'page_top', 'full_view' => false, )); diff --git a/mod/reportedcontent/views/default/admin/administer_utilities/reportedcontent.php b/mod/reportedcontent/views/default/admin/administer_utilities/reportedcontent.php index 32f108312..c37761f32 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', 'subtypes' => 'reported_content')); if (!$list) { $list = '

' . elgg_echo('reportedcontent:none') . '

'; } diff --git a/mod/reportedcontent/views/default/widgets/reportedcontent/content.php b/mod/reportedcontent/views/default/widgets/reportedcontent/content.php index 4c6595653..d6af8e87b 100644 --- a/mod/reportedcontent/views/default/widgets/reportedcontent/content.php +++ b/mod/reportedcontent/views/default/widgets/reportedcontent/content.php @@ -4,7 +4,7 @@ */ $list = elgg_list_entities(array( - 'types' => 'object', + 'type' => 'object', 'subtypes' => 'reported_content', 'limit' => $vars['entity']->num_display, 'pagination' => false, diff --git a/mod/thewire/views/default/thewire/profile_status.php b/mod/thewire/views/default/thewire/profile_status.php index b5d9dbd80..ef0d550d2 100644 --- a/mod/thewire/views/default/thewire/profile_status.php +++ b/mod/thewire/views/default/thewire/profile_status.php @@ -9,7 +9,7 @@ $owner = $vars['entity']->guid; //grab the user's latest from the wire $params = array( - 'types' => 'object', + 'type' => 'object', 'subtypes' => 'thewire', 'owner_guid' => $owner, 'limit' => 1, -- cgit v1.2.3 From e33d1eba6a158699174de2747de6d6e654c03407 Mon Sep 17 00:00:00 2001 From: Jerome Bakker Date: Wed, 3 Oct 2012 14:11:41 +0200 Subject: correct singual usage of subtype in elgg_get_entities_* functions --- engine/lib/deprecated-1.8.php | 10 +++++----- engine/lib/sites.php | 2 +- engine/lib/users.php | 4 ++-- engine/tests/api/entity_getter_functions.php | 2 +- mod/categories/pages/categories/listing.php | 2 +- mod/file/pages/file/owner.php | 2 +- mod/file/pages/file/search.php | 2 +- mod/file/pages/file/world.php | 2 +- mod/pages/pages/pages/owner.php | 2 +- mod/pages/pages/pages/world.php | 2 +- .../default/admin/administer_utilities/reportedcontent.php | 2 +- .../views/default/widgets/reportedcontent/content.php | 2 +- mod/thewire/views/default/thewire/profile_status.php | 2 +- 13 files changed, 18 insertions(+), 18 deletions(-) diff --git a/engine/lib/deprecated-1.8.php b/engine/lib/deprecated-1.8.php index e967df3dd..622b58212 100644 --- a/engine/lib/deprecated-1.8.php +++ b/engine/lib/deprecated-1.8.php @@ -87,7 +87,7 @@ function list_entities_from_access_id($access_id, $entity_type = "", $entity_sub elgg_deprecated_notice("All list_entities* functions were deprecated in 1.8. Use elgg_list_entities* instead.", 1.8); echo elgg_list_entities_from_access_id(array('access_id' => $access_id, - 'types' => $entity_type, 'subtypes' => $entity_subtype, 'owner_guids' => $owner_guid, + 'types' => $entity_type, 'subtype' => $entity_subtype, 'owner_guids' => $owner_guid, 'limit' => $limit, 'full_view' => $fullview, 'list_type_toggle' => $listtypetoggle, 'pagination' => $pagination,)); } @@ -1315,7 +1315,7 @@ function list_entities_from_metadata($meta_name, $meta_value = "", $entity_type 'metadata_name' => $meta_name, 'metadata_value' => $meta_value, 'types' => $entity_type, - 'subtypes' => $entity_subtype, + 'subtype' => $entity_subtype, 'limit' => $limit, 'offset' => $offset, 'count' => TRUE, @@ -2121,7 +2121,7 @@ $fullview = true, $listtypetoggle = false, $pagination = true, $order_by = '') { 'relationship_guid' => $relationship_guid, 'inverse_relationship' => $inverse_relationship, 'types' => $type, - 'subtypes' => $subtype, + 'subtype' => $subtype, 'owner_guid' => $owner_guid, 'order_by' => $order_by, 'limit' => $limit, @@ -2567,7 +2567,7 @@ $owner_guid = "", $owner_relationship = "") { 'relationship_guid' => $owner_guid[0], 'inverse_relationship' => FALSE, 'type' => 'user', - 'subtypes' => $subtype, + 'subtype' => $subtype, 'limit' => 9999)) ) { @@ -2722,7 +2722,7 @@ function get_site_collections($site_guid, $subtype = "", $limit = 10, $offset = 'relationship_guid' => $site_guid, 'inverse_relationship' => TRUE, 'type' => 'collection', - 'subtypes' => $subtype, + 'subtype' => $subtype, 'limit' => $limit, 'offset' => $offset )); diff --git a/engine/lib/sites.php b/engine/lib/sites.php index 805dacd2d..236fbc28e 100644 --- a/engine/lib/sites.php +++ b/engine/lib/sites.php @@ -184,7 +184,7 @@ function get_site_objects($site_guid, $subtype = "", $limit = 10, $offset = 0) { 'relationship_guid' => $site_guid, 'inverse_relationship' => TRUE, 'type' => 'object', - 'subtypes' => $subtype, + 'subtype' => $subtype, 'limit' => $limit, 'offset' => $offset )); diff --git a/engine/lib/users.php b/engine/lib/users.php index 7dc6b7c2d..6b0b1429b 100644 --- a/engine/lib/users.php +++ b/engine/lib/users.php @@ -387,7 +387,7 @@ $offset = 0) { 'relationship' => 'friend', 'relationship_guid' => $user_guid, 'type' => 'user', - 'subtypes' => $subtype, + 'subtype' => $subtype, 'limit' => $limit, 'offset' => $offset )); @@ -411,7 +411,7 @@ $offset = 0) { 'relationship_guid' => $user_guid, 'inverse_relationship' => TRUE, 'type' => 'user', - 'subtypes' => $subtype, + 'subtype' => $subtype, 'limit' => $limit, 'offset' => $offset )); diff --git a/engine/tests/api/entity_getter_functions.php b/engine/tests/api/entity_getter_functions.php index d255c2e67..6f7a6145e 100644 --- a/engine/tests/api/entity_getter_functions.php +++ b/engine/tests/api/entity_getter_functions.php @@ -426,7 +426,7 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest { $options = array( 'types' => $types, - 'subtypes' => $subtype + 'subtype' => $subtype ); $es = elgg_get_entities($options); diff --git a/mod/categories/pages/categories/listing.php b/mod/categories/pages/categories/listing.php index 8924506e9..4e677d5c4 100644 --- a/mod/categories/pages/categories/listing.php +++ b/mod/categories/pages/categories/listing.php @@ -16,7 +16,7 @@ $params = array( 'metadata_name' => 'universal_categories', 'metadata_value' => $category, 'types' => $type, - 'subtypes' => $subtype, + 'subtype' => $subtype, 'owner_guid' => $owner_guid, 'limit' => $limit, 'full_view' => FALSE, diff --git a/mod/file/pages/file/owner.php b/mod/file/pages/file/owner.php index 1409a404e..d7f057f2a 100644 --- a/mod/file/pages/file/owner.php +++ b/mod/file/pages/file/owner.php @@ -37,7 +37,7 @@ $title = elgg_echo("file:user", array($owner->name)); // List files $content = elgg_list_entities(array( 'type' => 'object', - 'subtypes' => 'file', + 'subtype' => 'file', 'container_guid' => $owner->guid, 'limit' => 10, 'full_view' => FALSE, diff --git a/mod/file/pages/file/search.php b/mod/file/pages/file/search.php index 35f8a7db7..d60dfb755 100644 --- a/mod/file/pages/file/search.php +++ b/mod/file/pages/file/search.php @@ -75,7 +75,7 @@ if ($listtype == "gallery") { $params = array( 'type' => 'object', - 'subtypes' => 'file', + '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 8cf8ad138..8e6c87f26 100644 --- a/mod/file/pages/file/world.php +++ b/mod/file/pages/file/world.php @@ -15,7 +15,7 @@ $title = elgg_echo('file:all'); $content = elgg_list_entities(array( 'type' => 'object', - 'subtypes' => 'file', + 'subtype' => 'file', 'limit' => $limit, 'full_view' => FALSE )); diff --git a/mod/pages/pages/pages/owner.php b/mod/pages/pages/pages/owner.php index a061225e2..48199368c 100644 --- a/mod/pages/pages/pages/owner.php +++ b/mod/pages/pages/pages/owner.php @@ -21,7 +21,7 @@ elgg_register_title_button(); $content = elgg_list_entities(array( 'type' => 'object', - 'subtypes' => 'page_top', + '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 e5a29eb63..c130a6bd6 100644 --- a/mod/pages/pages/pages/world.php +++ b/mod/pages/pages/pages/world.php @@ -14,7 +14,7 @@ elgg_register_title_button(); $content = elgg_list_entities(array( 'type' => 'object', - 'subtypes' => 'page_top', + 'subtype' => 'page_top', 'full_view' => false, )); if (!$content) { diff --git a/mod/reportedcontent/views/default/admin/administer_utilities/reportedcontent.php b/mod/reportedcontent/views/default/admin/administer_utilities/reportedcontent.php index c37761f32..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('type' => 'object', 'subtypes' => 'reported_content')); +$list = elgg_list_entities(array('type' => 'object', 'subtype' => 'reported_content')); if (!$list) { $list = '

' . elgg_echo('reportedcontent:none') . '

'; } diff --git a/mod/reportedcontent/views/default/widgets/reportedcontent/content.php b/mod/reportedcontent/views/default/widgets/reportedcontent/content.php index d6af8e87b..0095decca 100644 --- a/mod/reportedcontent/views/default/widgets/reportedcontent/content.php +++ b/mod/reportedcontent/views/default/widgets/reportedcontent/content.php @@ -5,7 +5,7 @@ $list = elgg_list_entities(array( 'type' => 'object', - 'subtypes' => 'reported_content', + 'subtype' => 'reported_content', 'limit' => $vars['entity']->num_display, 'pagination' => false, )); diff --git a/mod/thewire/views/default/thewire/profile_status.php b/mod/thewire/views/default/thewire/profile_status.php index ef0d550d2..26e1403fe 100644 --- a/mod/thewire/views/default/thewire/profile_status.php +++ b/mod/thewire/views/default/thewire/profile_status.php @@ -10,7 +10,7 @@ $owner = $vars['entity']->guid; //grab the user's latest from the wire $params = array( 'type' => 'object', - 'subtypes' => 'thewire', + 'subtype' => 'thewire', 'owner_guid' => $owner, 'limit' => 1, ); -- cgit v1.2.3 From 2b4ba38bf78e90cbf8c153676c95f562bb500204 Mon Sep 17 00:00:00 2001 From: Jerome Bakker Date: Wed, 3 Oct 2012 14:17:27 +0200 Subject: correct singual usage of type in elgg_get_entities_* functions - found more bad usage --- engine/lib/deprecated-1.8.php | 6 +++--- engine/lib/relationships.php | 2 +- mod/categories/pages/categories/listing.php | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/engine/lib/deprecated-1.8.php b/engine/lib/deprecated-1.8.php index 622b58212..033af90fd 100644 --- a/engine/lib/deprecated-1.8.php +++ b/engine/lib/deprecated-1.8.php @@ -87,7 +87,7 @@ function list_entities_from_access_id($access_id, $entity_type = "", $entity_sub elgg_deprecated_notice("All list_entities* functions were deprecated in 1.8. Use elgg_list_entities* instead.", 1.8); echo elgg_list_entities_from_access_id(array('access_id' => $access_id, - 'types' => $entity_type, 'subtype' => $entity_subtype, 'owner_guids' => $owner_guid, + 'type' => $entity_type, 'subtype' => $entity_subtype, 'owner_guids' => $owner_guid, 'limit' => $limit, 'full_view' => $fullview, 'list_type_toggle' => $listtypetoggle, 'pagination' => $pagination,)); } @@ -1314,7 +1314,7 @@ function list_entities_from_metadata($meta_name, $meta_value = "", $entity_type $options = array( 'metadata_name' => $meta_name, 'metadata_value' => $meta_value, - 'types' => $entity_type, + 'type' => $entity_type, 'subtype' => $entity_subtype, 'limit' => $limit, 'offset' => $offset, @@ -2120,7 +2120,7 @@ $fullview = true, $listtypetoggle = false, $pagination = true, $order_by = '') { 'relationship' => $relationship, 'relationship_guid' => $relationship_guid, 'inverse_relationship' => $inverse_relationship, - 'types' => $type, + 'type' => $type, 'subtype' => $subtype, 'owner_guid' => $owner_guid, 'order_by' => $order_by, diff --git a/engine/lib/relationships.php b/engine/lib/relationships.php index 09d541e22..7b42ed5be 100644 --- a/engine/lib/relationships.php +++ b/engine/lib/relationships.php @@ -507,7 +507,7 @@ function get_attachments($guid, $type = "") { 'relationship' => 'attached', 'relationship_guid' => $guid, 'inverse_relationship' => false, - 'types' => $type, + 'type' => $type, 'subtypes' => '', 'owner_guid' => 0, 'order_by' => 'time_created desc', diff --git a/mod/categories/pages/categories/listing.php b/mod/categories/pages/categories/listing.php index 4e677d5c4..d51e6c19e 100644 --- a/mod/categories/pages/categories/listing.php +++ b/mod/categories/pages/categories/listing.php @@ -15,7 +15,7 @@ $type = get_input("type", 'object'); $params = array( 'metadata_name' => 'universal_categories', 'metadata_value' => $category, - 'types' => $type, + 'type' => $type, 'subtype' => $subtype, 'owner_guid' => $owner_guid, 'limit' => $limit, -- cgit v1.2.3 From f8bee7421d1c3880ba7acb3f8fd16042d598a95f Mon Sep 17 00:00:00 2001 From: Jerome Bakker Date: Wed, 3 Oct 2012 14:29:14 +0200 Subject: better usage of 'limit' in cases where this is irrelevant --- engine/lib/deprecated-1.8.php | 2 +- engine/lib/notification.php | 2 +- mod/groups/start.php | 2 +- mod/notifications/actions/groupsave.php | 2 +- mod/notifications/groups.php | 2 +- mod/notifications/index.php | 2 +- .../views/default/forms/notificationsettings/groupsave.php | 2 +- .../views/default/notifications/subscriptions/forminternals.php | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/engine/lib/deprecated-1.8.php b/engine/lib/deprecated-1.8.php index 033af90fd..2b4ffcc4f 100644 --- a/engine/lib/deprecated-1.8.php +++ b/engine/lib/deprecated-1.8.php @@ -2568,7 +2568,7 @@ $owner_guid = "", $owner_relationship = "") { 'inverse_relationship' => FALSE, 'type' => 'user', 'subtype' => $subtype, - 'limit' => 9999)) + 'limit' => false)) ) { $friendsarray = array(); diff --git a/engine/lib/notification.php b/engine/lib/notification.php index 738295588..20e32ae55 100644 --- a/engine/lib/notification.php +++ b/engine/lib/notification.php @@ -492,7 +492,7 @@ function object_notifications($event, $object_type, $object) { 'relationship_guid' => $object->container_guid, 'inverse_relationship' => TRUE, 'type' => 'user', - 'limit' => 99999 + 'limit' => false )); if ($interested_users && is_array($interested_users)) { diff --git a/mod/groups/start.php b/mod/groups/start.php index 9689802eb..6bdf04d2b 100644 --- a/mod/groups/start.php +++ b/mod/groups/start.php @@ -538,7 +538,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) { diff --git a/mod/notifications/actions/groupsave.php b/mod/notifications/actions/groupsave.php index c646c1885..d77af41cc 100644 --- a/mod/notifications/actions/groupsave.php +++ b/mod/notifications/actions/groupsave.php @@ -22,7 +22,7 @@ $options = array( 'relationship' => 'member', 'relationship_guid' => $user->guid, 'type' => 'group', - 'limit' => 9999, + '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 d29c43e1f..973f3493c 100644 --- a/mod/notifications/groups.php +++ b/mod/notifications/groups.php @@ -29,7 +29,7 @@ $groupmemberships = elgg_get_entities_from_relationship(array( 'relationship' => 'member', 'relationship_guid' => $user->guid, 'type' => 'group', - 'limit' => 9999, + 'limit' => false, )); $body = elgg_view_form('notificationsettings/groupsave', array(), array( diff --git a/mod/notifications/index.php b/mod/notifications/index.php index ff03cb274..a99622efd 100644 --- a/mod/notifications/index.php +++ b/mod/notifications/index.php @@ -28,7 +28,7 @@ if ($people_ents = elgg_get_entities_from_relationship(array( 'relationship' => 'notify', 'relationship_guid' => $user->guid, 'type' => 'user', - 'limit' => 99999, + '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 64db8f533..f3e5f693a 100644 --- a/mod/notifications/views/default/forms/notificationsettings/groupsave.php +++ b/mod/notifications/views/default/forms/notificationsettings/groupsave.php @@ -16,7 +16,7 @@ foreach ($NOTIFICATION_HANDLERS as $method => $foo) { 'relationship' => 'notify' . $method, 'relationship_guid' => $user->guid, 'type' => 'group', - 'limit' => 99999, + '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 57fa62405..79a7959ac 100644 --- a/mod/notifications/views/default/notifications/subscriptions/forminternals.php +++ b/mod/notifications/views/default/notifications/subscriptions/forminternals.php @@ -32,7 +32,7 @@ foreach($NOTIFICATION_HANDLERS as $method => $foo) { 'relationship' => 'notify' . $method, 'relationship_guid' => $user->guid, 'type' => 'user', - 'limit' => 99999, + 'limit' => false, )); } -- cgit v1.2.3 From b2724f7300902034398f534e55975b3487e3a235 Mon Sep 17 00:00:00 2001 From: Paweł Sroka Date: Mon, 31 Dec 2012 18:22:42 +0100 Subject: Fixes #2554 - Adds search support for sorting and order --- mod/search/pages/search/index.php | 2 +- mod/search/search_hooks.php | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/mod/search/pages/search/index.php b/mod/search/pages/search/index.php index fcd95c43e..ede09329b 100644 --- a/mod/search/pages/search/index.php +++ b/mod/search/pages/search/index.php @@ -63,7 +63,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 2143a0d24..245234e0e 100644 --- a/mod/search/search_hooks.php +++ b/mod/search/search_hooks.php @@ -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. @@ -89,6 +90,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. @@ -148,6 +150,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. @@ -234,6 +237,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. @@ -371,6 +375,11 @@ function search_comments_hook($hook, $type, $value, $params) { return array ('entities' => array(), 'count' => 0); } + $order_by = search_get_order_by_sql('e', 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 @@ -380,7 +389,8 @@ function search_comments_hook($hook, $type, $value, $params) { AND $e_access AND $a_access $container_and - + + $order_by LIMIT $offset, $limit "; -- cgit v1.2.3 From 2d365ba900e40494abeb306e3a881c91e2099ba6 Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Thu, 17 Jan 2013 11:05:16 -0500 Subject: Allow friend collection names to store arbitrary plain text --- actions/friends/collections/add.php | 2 +- views/default/output/access.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actions/friends/collections/add.php b/actions/friends/collections/add.php index 9dc17b37e..e63a149f7 100644 --- a/actions/friends/collections/add.php +++ b/actions/friends/collections/add.php @@ -6,7 +6,7 @@ * @subpackage Friends.Collections */ -$collection_name = get_input('collection_name'); +$collection_name = htmlspecialchars(get_input('collection_name', '', false), ENT_QUOTES, 'UTF-8'); $friends = get_input('friends_collection'); if (!$collection_name) { diff --git a/views/default/output/access.php b/views/default/output/access.php index 91c5c721e..5c8d62c4d 100644 --- a/views/default/output/access.php +++ b/views/default/output/access.php @@ -11,7 +11,7 @@ if (isset($vars['entity']) && elgg_instanceof($vars['entity'])) { $access_id = $vars['entity']->access_id; $access_class = 'elgg-access'; $access_id_string = get_readable_access_level($access_id); - $access_id_string = htmlentities($access_id_string, ENT_QUOTES, 'UTF-8'); + $access_id_string = htmlspecialchars($access_id_string, ENT_QUOTES, 'UTF-8', false); // if within a group or shared access collection display group name and open/closed membership status // @todo have a better way to do this instead of checking against subtype / class. -- cgit v1.2.3 From ef19671ba2c154449cef16bae25814e70e8384f3 Mon Sep 17 00:00:00 2001 From: Paweł Sroka Date: Thu, 17 Jan 2013 22:07:06 +0100 Subject: Fixes #3123 - Correct name length control in datalist_get and set_config --- engine/lib/configuration.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/engine/lib/configuration.php b/engine/lib/configuration.php index b10e51130..385021581 100644 --- a/engine/lib/configuration.php +++ b/engine/lib/configuration.php @@ -138,7 +138,7 @@ function elgg_set_config($name, $value) { /** * Save a configuration setting * - * @param string $name Configuration name (cannot be greater than 32 characters) + * @param string $name Configuration name (cannot be greater than 255 characters) * @param mixed $value Configuration value. Should be string for installation setting * @param int $site_guid NULL for installation setting, 0 for default site * @@ -227,9 +227,9 @@ function datalist_get($name) { $name = trim($name); - // cannot store anything longer than 32 characters in db, so catch here - if (elgg_strlen($name) > 32) { - elgg_log("The name length for configuration variables cannot be greater than 32", "ERROR"); + // cannot store anything longer than 255 characters in db, so catch here + if (elgg_strlen($name) > 255) { + elgg_log("The name length for configuration variables cannot be greater than 255", "ERROR"); return false; } @@ -286,7 +286,7 @@ function datalist_get($name) { function datalist_set($name, $value) { global $CONFIG, $DATALIST_CACHE; - // cannot store anything longer than 32 characters in db, so catch before we set + // cannot store anything longer than 255 characters in db, so catch before we set if (elgg_strlen($name) > 255) { elgg_log("The name length for configuration variables cannot be greater than 255", "ERROR"); return false; @@ -332,7 +332,7 @@ function datalist_set($name, $value) { * This will cause the run once function to be run on all installations. To perform * additional upgrades, create new functions for each release. * - * @warning The function name cannot be longer than 32 characters long due to + * @warning The function name cannot be longer than 255 characters long due to * the current schema for the datalist table. * * @internal A datalist entry $functioname is created with the value of time(). @@ -419,9 +419,9 @@ function set_config($name, $value, $site_guid = 0) { $name = trim($name); - // cannot store anything longer than 32 characters in db, so catch before we set - if (elgg_strlen($name) > 32) { - elgg_log("The name length for configuration variables cannot be greater than 32", "ERROR"); + // cannot store anything longer than 255 characters in db, so catch before we set + if (elgg_strlen($name) > 255) { + elgg_log("The name length for configuration variables cannot be greater than 255", "ERROR"); return false; } -- cgit v1.2.3 From 5079efbd59324e28aeb29b7ebbe5c4e069885bb4 Mon Sep 17 00:00:00 2001 From: Luciano Lima Date: Tue, 29 Jan 2013 23:32:39 -0300 Subject: Check if the is a valid viewtype Signed-off-by: Luciano Lima --- engine/lib/web_services.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/lib/web_services.php b/engine/lib/web_services.php index c8e4a13cc..1a15ecc15 100644 --- a/engine/lib/web_services.php +++ b/engine/lib/web_services.php @@ -1268,10 +1268,10 @@ function service_handler($handler, $request) { // after the handler, the first identifier is response format // ex) http://example.org/services/api/rest/xml/?method=test - $reponse_format = array_shift($request); + $response_format = array_shift($request); // Which view - xml, json, ... - if ($reponse_format) { - elgg_set_viewtype($reponse_format); + if ($response_format && elgg_is_valid_view_type($response_format)) { + elgg_set_viewtype($response_format); } else { // default to xml elgg_set_viewtype("xml"); -- cgit v1.2.3 From c2aab4e6df792dced8ce014c8d9c6feee3b24f39 Mon Sep 17 00:00:00 2001 From: Cash Costello Date: Wed, 30 Jan 2013 07:32:52 -0500 Subject: updated the changes file based on development so far --- CHANGES.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 13c30ae3e..fd9d0eef6 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,16 @@ +Version 1.8.14 +(X xx, 2013 from https://github.com/Elgg/Elgg/tree/1.8) + Contributing Developers: + * Luciano Lima + * Paweł Sroka + + Bugfixes: + * + + Enhancements: + * Web services fall back to xml if the viewtype is invalid + + Version 1.8.13 (January 29, 2013 from https://github.com/Elgg/Elgg/tree/1.8) Contributing Developers: -- cgit v1.2.3 From 6a73322353db0e17bdf86a9db8d7d3f2582740de Mon Sep 17 00:00:00 2001 From: Luciano Lima Date: Wed, 30 Jan 2013 00:05:38 -0300 Subject: Set the error code when user authentication fail Signed-off-by: Luciano Lima --- engine/lib/web_services.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/lib/web_services.php b/engine/lib/web_services.php index 1a15ecc15..b6289184a 100644 --- a/engine/lib/web_services.php +++ b/engine/lib/web_services.php @@ -178,7 +178,7 @@ function authenticate_method($method) { // check if user authentication is required if ($API_METHODS[$method]["require_user_auth"] == true) { if ($user_auth_result == false) { - throw new APIException($user_pam->getFailureMessage()); + throw new APIException($user_pam->getFailureMessage(), ErrorResult::$RESULT_FAIL_AUTHTOKEN); } } -- cgit v1.2.3 From feed34eaf567924394bdcc0463c4d21231ad38d3 Mon Sep 17 00:00:00 2001 From: Cash Costello Date: Fri, 1 Feb 2013 07:22:03 -0500 Subject: Fixes #5022 not setting forward on ajax calls for walled garden --- engine/classes/ElggSite.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/classes/ElggSite.php b/engine/classes/ElggSite.php index 1fe49b85c..1a34df195 100644 --- a/engine/classes/ElggSite.php +++ b/engine/classes/ElggSite.php @@ -381,7 +381,9 @@ class ElggSite extends ElggEntity { elgg_register_plugin_hook_handler('index', 'system', 'elgg_walled_garden_index', 1); if (!$this->isPublicPage()) { - $_SESSION['last_forward_from'] = current_page_url(); + if (!elgg_is_xhr()) { + $_SESSION['last_forward_from'] = current_page_url(); + } register_error(elgg_echo('loggedinrequired')); forward(); } @@ -443,8 +445,6 @@ class ElggSite extends ElggEntity { // include a hook for plugin authors to include public pages $plugins = elgg_trigger_plugin_hook('public_pages', 'walled_garden', NULL, array()); - // lookup admin-specific public pages - // allow public pages foreach (array_merge($defaults, $plugins) as $public) { $pattern = "`^{$CONFIG->url}$public/*$`i"; -- cgit v1.2.3 From 90ba43e54728982a23dd78dbbb07f5a1d88d9d10 Mon Sep 17 00:00:00 2001 From: German Bortoli Date: Fri, 14 Dec 2012 16:52:35 -0200 Subject: Add a lightobx alike instead use popups. Add a lightobx alike instead use popups while using the TinyMCE controls, for example, when you try to insert an image, watch the HTML content, etc. --- mod/tinymce/views/default/js/tinymce.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/tinymce/views/default/js/tinymce.php b/mod/tinymce/views/default/js/tinymce.php index 51e99c223..b4db43cee 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 : "", - 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, -- cgit v1.2.3 From 035f68a467ab50776c3f52af0cceb750d60cb4a9 Mon Sep 17 00:00:00 2001 From: Ed Lyons Date: Sat, 2 Feb 2013 17:58:59 -0500 Subject: Update mod/messages/start.php We had an Elgg user named Chris Read with username 'read'. Once he registered, people's messages stopped working because hitting a message in your inbox was a url like: [site_name]/messages/read/459 - and the message code, supporting the old URL format, looked up the parameter right after messages and did a lookup on that word. So, since it got a user, redirected to his inbox. Yipes! So I put in some code checking that the parameter really is your username, so it would work for Chris, but not for anyone else. It works fine now. --- mod/messages/start.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/mod/messages/start.php b/mod/messages/start.php index e17640098..95ebffbdb 100644 --- a/mod/messages/start.php +++ b/mod/messages/start.php @@ -85,8 +85,17 @@ function messages_page_handler($page) { // supporting the old inbox url /messages/ $user = get_user_by_username($page[0]); if ($user) { - $page[1] = $page[0]; - $page[0] = 'inbox'; + // Need to make sure that the username of the parameter is actually + // the username of the logged in user. This will prevent strange + // errors like grabbing the 'read' parameter and looking up + // a user with username 'read' and finding it and redirecting + // to that other person's inbox. + + if ($user->username == elgg_get_logged_in_user_entity()->username) { + // OK, so it is our username and not someone else's + $page[1] = $page[0]; + $page[0] = 'inbox'; + } } if (!isset($page[1])) { -- cgit v1.2.3 From a72b4bce06062fa0a6f2fbd7489fac3474c3dc24 Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Sat, 2 Feb 2013 21:19:22 -0500 Subject: Handle logged out case, simplify logic --- mod/messages/start.php | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/mod/messages/start.php b/mod/messages/start.php index 95ebffbdb..5503a675a 100644 --- a/mod/messages/start.php +++ b/mod/messages/start.php @@ -74,32 +74,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/ - $user = get_user_by_username($page[0]); - if ($user) { - // Need to make sure that the username of the parameter is actually - // the username of the logged in user. This will prevent strange - // errors like grabbing the 'read' parameter and looking up - // a user with username 'read' and finding it and redirecting - // to that other person's inbox. - - if ($user->username == elgg_get_logged_in_user_entity()->username) { - // OK, so it is our username and not someone else's - $page[1] = $page[0]; - $page[0] = 'inbox'; - } + // Support the old inbox url /messages/, 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'; -- cgit v1.2.3 From 85e4c16f39a8b00b229644bcd175663541dfd51a Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Mon, 4 Feb 2013 20:37:25 -0500 Subject: Doc fixes and inline type hints for variables (big static analysis cleanup) --- engine/classes/ElggAnnotation.php | 5 +++++ engine/classes/ElggAutoP.php | 10 +++++++--- engine/classes/ElggData.php | 17 +++++++++-------- engine/classes/ElggDiskFilestore.php | 9 +++++---- engine/classes/ElggEntity.php | 7 +++++-- engine/classes/ElggFile.php | 4 ++++ engine/classes/ElggFileCache.php | 2 ++ engine/classes/ElggGroup.php | 2 +- engine/classes/ElggMemcache.php | 2 ++ engine/classes/ElggMenuBuilder.php | 7 ++++++- engine/classes/ElggMetadata.php | 4 ++++ engine/classes/ElggPlugin.php | 2 +- engine/classes/ElggPluginPackage.php | 5 ++++- engine/classes/ElggRelationship.php | 3 ++- engine/classes/ElggWidget.php | 5 +++++ engine/classes/ElggXMLElement.php | 4 ++-- engine/classes/ODDMetaData.php | 14 +++++++------- engine/classes/ODDRelationship.php | 8 ++++---- engine/lib/access.php | 11 +++++++++++ engine/lib/admin.php | 11 +++++++++-- engine/lib/annotations.php | 11 +++++++++-- engine/lib/cache.php | 2 +- engine/lib/configuration.php | 5 +++-- engine/lib/cron.php | 1 + engine/lib/database.php | 22 ++++++++++++++-------- engine/lib/elgglib.php | 34 +++++++++++++++++++--------------- engine/lib/entities.php | 4 +++- engine/lib/export.php | 8 ++++---- engine/lib/extender.php | 2 ++ engine/lib/filestore.php | 4 ++-- engine/lib/input.php | 4 ++++ engine/lib/languages.php | 4 ++-- engine/lib/mb_wrapper.php | 2 +- engine/lib/metadata.php | 6 +++--- engine/lib/metastrings.php | 5 +++-- engine/lib/navigation.php | 6 ++++++ engine/lib/notification.php | 10 +++++++--- engine/lib/objects.php | 2 +- engine/lib/output.php | 4 ++-- engine/lib/pageowner.php | 2 +- engine/lib/plugins.php | 11 ++++++----- engine/lib/relationships.php | 20 +++++++++++--------- engine/lib/river.php | 2 +- engine/lib/sites.php | 2 +- engine/lib/system_log.php | 3 ++- engine/lib/tags.php | 6 +++--- engine/lib/upgrade.php | 1 + engine/lib/users.php | 22 +++++++++++++++++----- engine/lib/views.php | 2 +- engine/lib/widgets.php | 3 ++- engine/lib/xml.php | 2 +- 51 files changed, 229 insertions(+), 115 deletions(-) diff --git a/engine/classes/ElggAnnotation.php b/engine/classes/ElggAnnotation.php index 511b5151f..175e7049d 100644 --- a/engine/classes/ElggAnnotation.php +++ b/engine/classes/ElggAnnotation.php @@ -11,6 +11,9 @@ * @package Elgg.Core * @subpackage DataModel.Annotations * @link http://docs.elgg.org/DataModel/Annotations + * + * @property string $value_type + * @property string $enabled */ class ElggAnnotation extends ElggExtender { @@ -56,6 +59,8 @@ class ElggAnnotation extends ElggExtender { * Save this instance * * @return int an object id + * + * @throws IOException */ function save() { if ($this->id > 0) { diff --git a/engine/classes/ElggAutoP.php b/engine/classes/ElggAutoP.php index 89d77e583..40600aa13 100644 --- a/engine/classes/ElggAutoP.php +++ b/engine/classes/ElggAutoP.php @@ -117,6 +117,8 @@ class ElggAutoP { // serialize back to HTML $html = $this->_doc->saveHTML(); + // Note: we create elements, which will later be converted to paragraphs + // split AUTOPs into multiples at /\n\n+/ $html = preg_replace('/(' . $this->_unique . 'NL){2,}/', '', $html); $html = str_replace(array($this->_unique . 'BR', $this->_unique . 'NL', '
'), @@ -134,6 +136,7 @@ class ElggAutoP { // strip AUTOPs that only have comments/whitespace foreach ($this->_xpath->query('//autop') as $autop) { + /* @var DOMElement $autop */ $hasContent = false; if (trim($autop->textContent) !== '') { $hasContent = true; @@ -146,13 +149,14 @@ class ElggAutoP { } } if (!$hasContent) { - // strip w/ preg_replace later (faster than moving nodes out) + // mark to be later replaced w/ preg_replace (faster than moving nodes out) $autop->setAttribute("r", "1"); } } - // remove a single AUTOP inside certain elements + // If a DIV contains a single AUTOP, remove it foreach ($this->_xpath->query('//div') as $el) { + /* @var DOMElement $el */ $autops = $this->_xpath->query('./autop', $el); if ($autops->length === 1) { // strip w/ preg_replace later (faster than moving nodes out) @@ -185,7 +189,7 @@ class ElggAutoP { * @param DOMElement $el */ protected function _addParagraphs(DOMElement $el) { - // no need to recurse, just queue up + // no need to call recursively, just queue up $elsToProcess = array($el); $inlinesToProcess = array(); while ($el = array_shift($elsToProcess)) { diff --git a/engine/classes/ElggData.php b/engine/classes/ElggData.php index 3470ee1cf..a0df3c924 100644 --- a/engine/classes/ElggData.php +++ b/engine/classes/ElggData.php @@ -5,6 +5,9 @@ * * @package Elgg.Core * @subpackage DataModel + * + * @property int $owner_guid + * @property int $time_created */ abstract class ElggData implements Loggable, // Can events related to this object class be logged @@ -33,14 +36,12 @@ abstract class ElggData implements * Passing false returns false. Core constructors always pass false. * Does nothing either way since attributes are initialized by the time * this is called. - * @return false|void False is + * @return void * @deprecated 1.8 Use initializeAttributes() */ protected function initialise_attributes($pre18_api = true) { if ($pre18_api) { elgg_deprecated_notice('initialise_attributes() is deprecated by initializeAttributes()', 1.8); - } else { - return false; } } @@ -111,7 +112,7 @@ abstract class ElggData implements * @param string $name The attribute to set * @param mixed $value The value to set it to * - * @return The success of your set funtion? + * @return bool The success of your set function? */ abstract protected function set($name, $value); @@ -195,7 +196,7 @@ abstract class ElggData implements * * @see Iterator::current() * - * @return void + * @return mixed */ public function current() { return current($this->attributes); @@ -206,7 +207,7 @@ abstract class ElggData implements * * @see Iterator::key() * - * @return void + * @return string */ public function key() { return key($this->attributes); @@ -228,7 +229,7 @@ abstract class ElggData implements * * @see Iterator::valid() * - * @return void + * @return bool */ public function valid() { return $this->valid; @@ -266,7 +267,7 @@ abstract class ElggData implements * * @param mixed $key Name * - * @return void + * @return mixed */ public function offsetGet($key) { if (array_key_exists($key, $this->attributes)) { diff --git a/engine/classes/ElggDiskFilestore.php b/engine/classes/ElggDiskFilestore.php index 7aace43ba..5483eed81 100644 --- a/engine/classes/ElggDiskFilestore.php +++ b/engine/classes/ElggDiskFilestore.php @@ -108,7 +108,7 @@ class ElggDiskFilestore extends ElggFilestore { * * @param resource $f File pointer resource * @param int $length The number of bytes to read - * @param inf $offset The number of bytes to start after + * @param int $offset The number of bytes to start after * * @return mixed Contents of file or false on fail. */ @@ -198,6 +198,7 @@ class ElggDiskFilestore extends ElggFilestore { * @param ElggFile $file File object * * @return string The full path of where the file is stored + * @throws InvalidParameterException */ public function getFilenameOnFilestore(ElggFile $file) { $owner_guid = $file->getOwnerGuid(); @@ -324,7 +325,7 @@ class ElggDiskFilestore extends ElggFilestore { * * @param int $identifier The guide of the entity to store the data under. * - * @return str The path where the entity's data will be stored. + * @return string The path where the entity's data will be stored. * @deprecated 1.8 Use ElggDiskFilestore::makeFileMatrix() */ protected function make_file_matrix($identifier) { @@ -338,7 +339,7 @@ class ElggDiskFilestore extends ElggFilestore { * * @param int $guid The guide of the entity to store the data under. * - * @return str The path where the entity's data will be stored. + * @return string The path where the entity's data will be stored. */ protected function makeFileMatrix($guid) { $entity = get_entity($guid); @@ -363,7 +364,7 @@ class ElggDiskFilestore extends ElggFilestore { * * @param int $guid The entity to contrust a matrix for * - * @return str The + * @return string The */ protected function user_file_matrix($guid) { elgg_deprecated_notice('ElggDiskFilestore::user_file_matrix() is deprecated by ::makeFileMatrix()', 1.8); diff --git a/engine/classes/ElggEntity.php b/engine/classes/ElggEntity.php index 929abceb2..0cb8e5f7c 100644 --- a/engine/classes/ElggEntity.php +++ b/engine/classes/ElggEntity.php @@ -34,6 +34,7 @@ * @property int $access_id Specifies the visibility level of this entity * @property int $time_created A UNIX timestamp of when the entity was created (read-only, set on first save) * @property int $time_updated A UNIX timestamp of when the entity was last updated (automatically updated on save) + * @property-read string $enabled */ abstract class ElggEntity extends ElggData implements Notable, // Calendar interface @@ -940,7 +941,7 @@ abstract class ElggEntity extends ElggData implements * @param ElggMetadata $metadata The piece of metadata to specifically check * @param int $user_guid The user GUID, optionally (default: logged in user) * - * @return true|false + * @return bool */ function canEditMetadata($metadata = null, $user_guid = 0) { return can_edit_entity_metadata($this->getGUID(), $user_guid, $metadata); @@ -1668,9 +1669,11 @@ abstract class ElggEntity extends ElggData implements /** * Import data from an parsed ODD xml data array. * - * @param array $data XML data + * @param ODD $data XML data * * @return true + * + * @throws InvalidParameterException */ public function import(ODD $data) { if (!($data instanceof ODDEntity)) { diff --git a/engine/classes/ElggFile.php b/engine/classes/ElggFile.php index f21621ffd..86406d108 100644 --- a/engine/classes/ElggFile.php +++ b/engine/classes/ElggFile.php @@ -178,6 +178,8 @@ class ElggFile extends ElggObject { * @param string $mode Either read/write/append * * @return resource File handler + * + * @throws IOException|InvalidParameterException */ public function open($mode) { if (!$this->getFilename()) { @@ -347,6 +349,8 @@ class ElggFile extends ElggObject { * a filestore as recorded in metadata or the system default. * * @return ElggFilestore + * + * @throws ClassNotFoundException */ protected function getFilestore() { // Short circuit if already set. diff --git a/engine/classes/ElggFileCache.php b/engine/classes/ElggFileCache.php index 34178d452..e654f1db2 100644 --- a/engine/classes/ElggFileCache.php +++ b/engine/classes/ElggFileCache.php @@ -13,6 +13,8 @@ class ElggFileCache extends ElggCache { * @param string $cache_path The cache path. * @param int $max_age Maximum age in seconds, 0 if no limit. * @param int $max_size Maximum size of cache in seconds, 0 if no limit. + * + * @throws ConfigurationException */ function __construct($cache_path, $max_age = 0, $max_size = 0) { $this->setVariable("cache_path", $cache_path); diff --git a/engine/classes/ElggGroup.php b/engine/classes/ElggGroup.php index ea257f368..d17fa4f1a 100644 --- a/engine/classes/ElggGroup.php +++ b/engine/classes/ElggGroup.php @@ -32,7 +32,7 @@ class ElggGroup extends ElggEntity * @param mixed $guid If an int, load that GUID. * If an entity table db row, then will load the rest of the data. * - * @throws Exception if there was a problem creating the group. + * @throws IOException|InvalidParameterException if there was a problem creating the group. */ function __construct($guid = null) { $this->initializeAttributes(); diff --git a/engine/classes/ElggMemcache.php b/engine/classes/ElggMemcache.php index d9539b9cb..91d50ab89 100644 --- a/engine/classes/ElggMemcache.php +++ b/engine/classes/ElggMemcache.php @@ -32,6 +32,8 @@ class ElggMemcache extends ElggSharedMemoryCache { * * @param string $namespace The namespace for this cache to write to - * note, namespaces of the same name are shared! + * + * @throws ConfigurationException */ function __construct($namespace = 'default') { global $CONFIG; diff --git a/engine/classes/ElggMenuBuilder.php b/engine/classes/ElggMenuBuilder.php index d7f85685c..2fd5ad9c4 100644 --- a/engine/classes/ElggMenuBuilder.php +++ b/engine/classes/ElggMenuBuilder.php @@ -8,6 +8,9 @@ */ class ElggMenuBuilder { + /** + * @var ElggMenuItem[] + */ protected $menu = array(); protected $selected = null; @@ -15,7 +18,7 @@ class ElggMenuBuilder { /** * ElggMenuBuilder constructor * - * @param array $menu Array of ElggMenuItem objects + * @param ElggMenuItem[] $menu Array of ElggMenuItem objects */ public function __construct(array $menu) { $this->menu = $menu; @@ -107,6 +110,7 @@ class ElggMenuBuilder { $children = array(); // divide base nodes from children foreach ($section as $menu_item) { + /* @var ElggMenuItem $menu_item */ $parent_name = $menu_item->getParentName(); if (!$parent_name) { $parents[$menu_item->getName()] = $menu_item; @@ -216,6 +220,7 @@ class ElggMenuBuilder { array_push($stack, $root); while (!empty($stack)) { $node = array_pop($stack); + /* @var ElggMenuItem $node */ $node->sortChildren($sort_callback); $children = $node->getChildren(); if ($children) { diff --git a/engine/classes/ElggMetadata.php b/engine/classes/ElggMetadata.php index 7f45dc3ea..3a8e2d817 100644 --- a/engine/classes/ElggMetadata.php +++ b/engine/classes/ElggMetadata.php @@ -6,6 +6,10 @@ * * @package Elgg.Core * @subpackage Metadata + * + * @property string $value_type + * @property int $owner_guid + * @property string $enabled */ class ElggMetadata extends ElggExtender { diff --git a/engine/classes/ElggPlugin.php b/engine/classes/ElggPlugin.php index 8f71b79a8..066fd9a79 100644 --- a/engine/classes/ElggPlugin.php +++ b/engine/classes/ElggPlugin.php @@ -145,7 +145,7 @@ class ElggPlugin extends ElggObject { /** * Sets the location of this plugin. * - * @param path $id The path to the plugin's dir. + * @param string $id The path to the plugin's dir. * @return bool */ public function setID($id) { diff --git a/engine/classes/ElggPluginPackage.php b/engine/classes/ElggPluginPackage.php index 2dc4bdb3d..209242288 100644 --- a/engine/classes/ElggPluginPackage.php +++ b/engine/classes/ElggPluginPackage.php @@ -100,7 +100,6 @@ class ElggPluginPackage { * @param string $plugin The ID (directory name) or full path of the plugin. * @param bool $validate Automatically run isValid()? * - * @return true * @throws PluginException */ public function __construct($plugin, $validate = true) { @@ -213,6 +212,7 @@ class ElggPluginPackage { return false; } + // Note: $conflicts and $requires are not unused. They're called dynamically $conflicts = $this->getManifest()->getConflicts(); $requires = $this->getManifest()->getRequires(); $provides = $this->getManifest()->getProvides(); @@ -330,8 +330,10 @@ class ElggPluginPackage { * @return bool|array */ public function checkDependencies($full_report = false) { + // Note: $conflicts and $requires are not unused. They're called dynamically $requires = $this->getManifest()->getRequires(); $conflicts = $this->getManifest()->getConflicts(); + $enabled_plugins = elgg_get_plugins('active'); $this_id = $this->getID(); $report = array(); @@ -368,6 +370,7 @@ class ElggPluginPackage { $check_types = array('requires', 'conflicts'); if ($full_report) { + // Note: $suggests is not unused. It's called dynamically $suggests = $this->getManifest()->getSuggests(); $check_types[] = 'suggests'; } diff --git a/engine/classes/ElggRelationship.php b/engine/classes/ElggRelationship.php index efc0f7eff..377a41093 100644 --- a/engine/classes/ElggRelationship.php +++ b/engine/classes/ElggRelationship.php @@ -71,6 +71,7 @@ class ElggRelationship extends ElggData implements * Save the relationship * * @return int the relationship id + * @throws IOException */ public function save() { if ($this->id > 0) { @@ -145,7 +146,7 @@ class ElggRelationship extends ElggData implements * @param ODD $data ODD data * @return bool - * @throws ImportException + * @throws ImportException|InvalidParameterException */ public function import(ODD $data) { if (!($data instanceof ODDRelationship)) { diff --git a/engine/classes/ElggWidget.php b/engine/classes/ElggWidget.php index 99708f66a..c123e5032 100644 --- a/engine/classes/ElggWidget.php +++ b/engine/classes/ElggWidget.php @@ -7,6 +7,11 @@ * * @package Elgg.Core * @subpackage Widgets + * + * @property-read string $handler internal, do not use + * @property-read string $column internal, do not use + * @property-read string $order internal, do not use + * @property-read string $context internal, do not use */ class ElggWidget extends ElggObject { diff --git a/engine/classes/ElggXMLElement.php b/engine/classes/ElggXMLElement.php index 65a13912c..4e4b7e63c 100644 --- a/engine/classes/ElggXMLElement.php +++ b/engine/classes/ElggXMLElement.php @@ -32,7 +32,7 @@ class ElggXMLElement { } /** - * @return array:string The attributes + * @return string[] The attributes */ public function getAttributes() { //include namespace declarations as attributes @@ -64,7 +64,7 @@ class ElggXMLElement { } /** - * @return array:ElggXMLElement Child elements + * @return ElggXMLElement[] Child elements */ public function getChildren() { $children = $this->_element->children(); diff --git a/engine/classes/ODDMetaData.php b/engine/classes/ODDMetaData.php index 58862e0fb..09b653582 100644 --- a/engine/classes/ODDMetaData.php +++ b/engine/classes/ODDMetaData.php @@ -10,12 +10,12 @@ class ODDMetaData extends ODD { /** * New ODD metadata * - * @param unknown_type $uuid Unique ID - * @param unknown_type $entity_uuid Another unique ID - * @param unknown_type $name Name - * @param unknown_type $value Value - * @param unknown_type $type Type - * @param unknown_type $owner_uuid Owner ID + * @param string $uuid Unique ID + * @param string $entity_uuid Another unique ID + * @param string $name Name + * @param string $value Value + * @param string $type Type + * @param string $owner_uuid Owner ID */ function __construct($uuid, $entity_uuid, $name, $value, $type = "", $owner_uuid = "") { parent::__construct(); @@ -31,7 +31,7 @@ class ODDMetaData extends ODD { /** * Returns 'metadata' * - * @return 'metadata' + * @return string 'metadata' */ protected function getTagName() { return "metadata"; diff --git a/engine/classes/ODDRelationship.php b/engine/classes/ODDRelationship.php index 2906b1c73..8b1fe217b 100644 --- a/engine/classes/ODDRelationship.php +++ b/engine/classes/ODDRelationship.php @@ -10,9 +10,9 @@ class ODDRelationship extends ODD { /** * New ODD Relationship * - * @param unknown_type $uuid1 First UUID - * @param unknown_type $type Type of telationship - * @param unknown_type $uuid2 Second UUId + * @param string $uuid1 First UUID + * @param string $type Type of telationship + * @param string $uuid2 Second UUId */ function __construct($uuid1, $type, $uuid2) { parent::__construct(); @@ -25,7 +25,7 @@ class ODDRelationship extends ODD { /** * Returns 'relationship' * - * @return 'relationship' + * @return string 'relationship' */ protected function getTagName() { return "relationship"; diff --git a/engine/lib/access.php b/engine/lib/access.php index f7d3bf7ea..de0693ea8 100644 --- a/engine/lib/access.php +++ b/engine/lib/access.php @@ -1015,6 +1015,10 @@ function access_init() { * * Returns true to override the access system or null if no change is needed. * + * @param string $hook + * @param string $type + * @param bool $value + * @param array $params * @return true|null * @access private */ @@ -1047,6 +1051,13 @@ function elgg_override_permissions($hook, $type, $value, $params) { /** * Runs unit tests for the entities object. + * + * @param string $hook + * @param string $type + * @param array $value + * @param array $params + * @return array + * * @access private */ function access_test($hook, $type, $value, $params) { diff --git a/engine/lib/admin.php b/engine/lib/admin.php index 35ab5599d..3677a3b69 100644 --- a/engine/lib/admin.php +++ b/engine/lib/admin.php @@ -380,6 +380,7 @@ function elgg_admin_add_plugin_settings_menu() { */ function elgg_admin_sort_page_menu($hook, $type, $return, $params) { $configure_items = $return['configure']; + /* @var ElggMenuItem[] $configure_items */ foreach ($configure_items as $menu_item) { if ($menu_item->getName() == 'settings') { $settings = $menu_item; @@ -387,6 +388,7 @@ function elgg_admin_sort_page_menu($hook, $type, $return, $params) { } // keep the basic and advanced settings at the top + /* @var ElggMenuItem $settings */ $children = $settings->getChildren(); $site_settings = array_splice($children, 0, 2); usort($children, array('ElggMenuBuilder', 'compareByText')); @@ -552,7 +554,7 @@ function admin_plugin_screenshot_page_handler($pages) { * * COPYRIGHT.txt * * LICENSE.txt * - * @param type $page + * @param array $pages * @return bool * @access private */ @@ -615,7 +617,11 @@ function admin_markdown_page_handler($pages) { /** * Adds default admin widgets to the admin dashboard. * - * @return void + * @param string $event + * @param string $type + * @param ElggUser $user + * + * @return null|true * @access private */ function elgg_add_admin_widgets($event, $type, $user) { @@ -637,6 +643,7 @@ function elgg_add_admin_widgets($event, $type, $user) { $guid = elgg_create_widget($user->getGUID(), $handler, 'admin'); if ($guid) { $widget = get_entity($guid); + /* @var ElggWidget $widget */ $widget->move($column, $position); } } diff --git a/engine/lib/annotations.php b/engine/lib/annotations.php index 3b9f84703..9ba0491b2 100644 --- a/engine/lib/annotations.php +++ b/engine/lib/annotations.php @@ -30,7 +30,7 @@ function row_to_elggannotation($row) { * * @param int $id The id of the annotation object being retrieved. * - * @return false|ElggAnnotation + * @return ElggAnnotation|false */ function elgg_get_annotation_from_id($id) { return elgg_get_metastring_based_object_from_id($id, 'annotations'); @@ -195,7 +195,7 @@ function update_annotation($annotation_id, $name, $value, $value_type, $owner_gu * for the proper use of the "calculation" option. * * - * @return mixed + * @return ElggAnnotation[]|mixed * @since 1.8.0 */ function elgg_get_annotations(array $options = array()) { @@ -451,6 +451,7 @@ function elgg_list_entities_from_annotation_calculation($options) { * @elgg_plugin_hook export all * * @return mixed + * @throws InvalidParameterException * @access private */ function export_annotation_plugin_hook($hook, $entity_type, $returnvalue, $params) { @@ -557,6 +558,12 @@ function elgg_register_annotation_url_handler($extender_name = "all", $function_ /** * Register annotation unit tests + * + * @param string $hook + * @param string $type + * @param array $value + * @param array $params + * @return array * @access private */ function annotations_test($hook, $type, $value, $params) { diff --git a/engine/lib/cache.php b/engine/lib/cache.php index be1c43e14..5c917bb18 100644 --- a/engine/lib/cache.php +++ b/engine/lib/cache.php @@ -222,7 +222,7 @@ function elgg_get_simplecache_url($type, $view) { /** * Regenerates the simple cache. * - * @warning This does not invalidate the cache, but actively resets it. + * @warning This does not invalidate the cache, but actively rebuilds it. * * @param string $viewtype Optional viewtype to regenerate. Defaults to all valid viewtypes. * diff --git a/engine/lib/configuration.php b/engine/lib/configuration.php index 385021581..851430127 100644 --- a/engine/lib/configuration.php +++ b/engine/lib/configuration.php @@ -36,6 +36,7 @@ function elgg_get_site_url($site_guid = 0) { if (!$site instanceof ElggSite) { return false; } + /* @var ElggSite $site */ return $site->url; } @@ -173,7 +174,7 @@ function elgg_save_config($name, $value, $site_guid = 0) { /** * Check that installation has completed and the database is populated. * - * @throws InstallationException + * @throws InstallationException|DatabaseException * @return void * @access private */ @@ -407,7 +408,7 @@ function unset_config($name, $site_guid = 0) { * @param string $value Its value * @param int $site_guid Optionally, the GUID of the site (current site is assumed by default) * - * @return 0 + * @return bool * @todo The config table doens't have numeric primary keys so insert_data returns 0. * @todo Use "INSERT ... ON DUPLICATE KEY UPDATE" instead of trying to delete then add. * @see unset_config() diff --git a/engine/lib/cron.php b/engine/lib/cron.php index f7a032f4a..f2939bdd6 100644 --- a/engine/lib/cron.php +++ b/engine/lib/cron.php @@ -26,6 +26,7 @@ function cron_init() { * @param array $page Pages * * @return bool + * @throws CronException * @access private */ function cron_page_handler($page) { diff --git a/engine/lib/database.php b/engine/lib/database.php index 7d90b30b8..18c30cb8e 100644 --- a/engine/lib/database.php +++ b/engine/lib/database.php @@ -17,7 +17,9 @@ * $DB_QUERY_CACHE[$query] => array(result1, result2, ... resultN) * * - * @global array $DB_QUERY_CACHE + * @warning be array this var may be an array or ElggStaticVariableCache depending on when called :( + * + * @global ElggStaticVariableCache|array $DB_QUERY_CACHE */ global $DB_QUERY_CACHE; $DB_QUERY_CACHE = array(); @@ -48,7 +50,7 @@ $DB_DELAYED_QUERIES = array(); * Each database link created with establish_db_link($name) is stored in * $dblink as $dblink[$name] => resource. Use get_db_link($name) to retrieve it. * - * @global array $dblink + * @global resource[] $dblink */ global $dblink; $dblink = array(); @@ -72,6 +74,7 @@ $dbcalls = 0; * resource. eg "read", "write", or "readwrite". * * @return void + * @throws DatabaseException * @access private */ function establish_db_link($dblinkname = "readwrite") { @@ -197,7 +200,7 @@ function db_delayedexecution_shutdown_hook() { * * @param string $dblinktype The type of link we want: "read", "write" or "readwrite". * - * @return object Database link + * @return resource Database link * @access private */ function get_db_link($dblinktype) { @@ -216,7 +219,7 @@ function get_db_link($dblinktype) { /** * Execute an EXPLAIN for $query. * - * @param str $query The query to explain + * @param string $query The query to explain * @param mixed $link The database link resource to user. * * @return mixed An object of the query's result, or FALSE @@ -240,9 +243,9 @@ function explain_query($query, $link) { * {@link $dbcalls} is incremented and the query is saved into the {@link $DB_QUERY_CACHE}. * * @param string $query The query - * @param link $dblink The DB link + * @param resource $dblink The DB link * - * @return The result of mysql_query() + * @return resource result of mysql_query() * @throws DatabaseException * @access private */ @@ -275,7 +278,7 @@ function execute_query($query, $dblink) { * the raw result from {@link mysql_query()}. * * @param string $query The query to execute - * @param resource $dblink The database link to use or the link type (read | write) + * @param resource|string $dblink The database link to use or the link type (read | write) * @param string $handler A callback function to pass the results array to * * @return true @@ -410,7 +413,7 @@ function elgg_query_runner($query, $callback = null, $single = false) { // test for callback once instead of on each iteration. // @todo check profiling to see if this needs to be broken out into - // explicit cases instead of checking in the interation. + // explicit cases instead of checking in the iteration. $is_callable = is_callable($callback); while ($row = mysql_fetch_object($result)) { if ($is_callable) { @@ -459,6 +462,7 @@ function insert_data($query) { // Invalidate query cache if ($DB_QUERY_CACHE) { + /* @var ElggStaticVariableCache $DB_QUERY_CACHE */ $DB_QUERY_CACHE->clear(); } @@ -490,6 +494,7 @@ function update_data($query) { // Invalidate query cache if ($DB_QUERY_CACHE) { + /* @var ElggStaticVariableCache $DB_QUERY_CACHE */ $DB_QUERY_CACHE->clear(); elgg_log("Query cache invalidated", 'NOTICE'); } @@ -520,6 +525,7 @@ function delete_data($query) { // Invalidate query cache if ($DB_QUERY_CACHE) { + /* @var ElggStaticVariableCache $DB_QUERY_CACHE */ $DB_QUERY_CACHE->clear(); elgg_log("Query cache invalidated", 'NOTICE'); } diff --git a/engine/lib/elgglib.php b/engine/lib/elgglib.php index 540605876..746fd8aa9 100644 --- a/engine/lib/elgglib.php +++ b/engine/lib/elgglib.php @@ -124,7 +124,8 @@ function elgg_load_library($name) { * @param string $location URL to forward to browser to. Can be path relative to the network's URL. * @param string $reason Short explanation for why we're forwarding * - * @return False False if headers have been sent. Terminates execution if forwarding. + * @return false False if headers have been sent. Terminates execution if forwarding. + * @throws SecurityException */ function forward($location = "", $reason = 'system') { global $CONFIG; @@ -528,7 +529,7 @@ function sanitise_filepath($path, $append_slash = TRUE) { * @param string $register Types of message: "error", "success" (default: success) * @param bool $count Count the number of messages (default: false) * - * @return true|false|array Either the array of messages, or a response regarding + * @return bool|array Either the array of messages, or a response regarding * whether the message addition was successful. * @todo Clean up. Separate registering messages and retrieving them. */ @@ -839,7 +840,7 @@ function elgg_trigger_event($event, $object_type, $object = null) { * * @param string $hook The name of the hook * @param string $type The type of the hook - * @param callback $callback The name of a valid function or an array with object and method + * @param callable $callback The name of a valid function or an array with object and method * @param int $priority The priority - 500 is default, lower numbers called first * * @return bool @@ -885,7 +886,7 @@ function elgg_register_plugin_hook_handler($hook, $type, $callback, $priority = * * @param string $hook The name of the hook * @param string $entity_type The name of the type of entity (eg "user", "object" etc) - * @param callback $callback The PHP callback to be removed + * @param callable $callback The PHP callback to be removed * * @return void * @since 1.8.0 @@ -1060,6 +1061,7 @@ function _elgg_php_exception_handler($exception) { * @param array $vars An array that points to the active symbol table where error occurred * * @return true + * @throws Exception * @access private * @todo Replace error_log calls with elgg_log calls. */ @@ -1354,7 +1356,7 @@ function full_url() { * @param array $parts Associative array of URL components like parse_url() returns * @param bool $html_encode HTML Encode the url? * - * @return str Full URL + * @return string Full URL * @since 1.7.0 */ function elgg_http_build_url(array $parts, $html_encode = TRUE) { @@ -1385,10 +1387,10 @@ function elgg_http_build_url(array $parts, $html_encode = TRUE) { * add tokens to the action. The form view automatically handles * tokens. * - * @param str $url Full action URL + * @param string $url Full action URL * @param bool $html_encode HTML encode the url? (default: false) * - * @return str URL with action tokens + * @return string URL with action tokens * @since 1.7.0 * @link http://docs.elgg.org/Tutorials/Actions */ @@ -1447,10 +1449,10 @@ function elgg_http_remove_url_query_element($url, $element) { /** * Adds an element or elements to a URL's query string. * - * @param str $url The URL + * @param string $url The URL * @param array $elements Key/value pairs to add to the URL * - * @return str The new URL with the query strings added + * @return string The new URL with the query strings added * @since 1.7.0 */ function elgg_http_add_url_query_elements($url, array $elements) { @@ -1646,7 +1648,7 @@ $sort_type = SORT_LOCALE_STRING) { * * @param string $ini_get_arg The INI setting * - * @return true|false Depending on whether it's on or off + * @return bool Depending on whether it's on or off */ function ini_get_bool($ini_get_arg) { $temp = strtolower(ini_get($ini_get_arg)); @@ -1662,7 +1664,7 @@ function ini_get_bool($ini_get_arg) { * * @tip Use this for arithmetic when determining if a file can be uploaded. * - * @param str $setting The php.ini setting + * @param string $setting The php.ini setting * * @return int * @since 1.7.0 @@ -1677,8 +1679,10 @@ function elgg_get_ini_setting_in_bytes($setting) { switch($last) { case 'g': $val *= 1024; + // fallthrough intentional case 'm': $val *= 1024; + // fallthrough intentional case 'k': $val *= 1024; } @@ -1835,7 +1839,7 @@ function elgg_ajax_page_handler($page) { * * @param array $page The page array * - * @return void + * @return bool * @elgg_pagehandler css * @access private */ @@ -2220,7 +2224,7 @@ function elgg_init() { * @param array $params empty * * @elgg_plugin_hook unit_tests system - * @return void + * @return array * @access private */ function elgg_api_test($hook, $type, $value, $params) { @@ -2232,7 +2236,7 @@ function elgg_api_test($hook, $type, $value, $params) { } /**#@+ - * Controlls access levels on ElggEntity entities, metadata, and annotations. + * Controls access levels on ElggEntity entities, metadata, and annotations. * * @var int */ @@ -2266,7 +2270,7 @@ define('ELGG_ENTITIES_NO_VALUE', 0); * referring page. * * @see forward - * @var unknown_type + * @var int -1 */ define('REFERRER', -1); diff --git a/engine/lib/entities.php b/engine/lib/entities.php index ce736ce05..0361ae195 100644 --- a/engine/lib/entities.php +++ b/engine/lib/entities.php @@ -531,6 +531,7 @@ function can_write_to_container($user_guid = 0, $container_guid = 0, $type = 'al // If still not approved, see if the user is a member of the group // @todo this should be moved to the groups plugin/library if (!$return && $user && $container instanceof ElggGroup) { + /* @var ElggGroup $container */ if ($container->isMember($user)) { $return = true; } @@ -1463,7 +1464,7 @@ function elgg_list_entities(array $options = array(), $getter = 'elgg_get_entiti * * @param string $type The type of entity * @param string $subtype The subtype of entity - * @param int $container_guid The container GUID that the entinties belong to + * @param int $container_guid The container GUID that the entities belong to * @param int $site_guid The site GUID * @param string $order_by Order_by SQL order by clause * @@ -2413,6 +2414,7 @@ function elgg_instanceof($entity, $type = NULL, $subtype = NULL, $class = NULL) $return = ($entity instanceof ElggEntity); if ($type) { + /* @var ElggEntity $entity */ $return = $return && ($entity->getType() == $type); } diff --git a/engine/lib/export.php b/engine/lib/export.php index ae9be95ce..31afc1b74 100644 --- a/engine/lib/export.php +++ b/engine/lib/export.php @@ -11,7 +11,7 @@ * * @param mixed $object The object either an ElggEntity, ElggRelationship or ElggExtender * - * @return the UUID or false + * @return string|false the UUID or false */ function get_uuid_from_object($object) { if ($object instanceof ElggEntity) { @@ -67,7 +67,7 @@ function is_uuid_this_domain($uuid) { * * @param string $uuid A unique ID * - * @return mixed + * @return ElggEntity|false */ function get_entity_from_uuid($uuid) { $uuid = sanitise_string($uuid); @@ -167,7 +167,7 @@ function exportAsArray($guid) { * * @param int $guid The GUID. * - * @return xml + * @return string XML * @see ElggEntity for an example of its usage. * @access private */ @@ -184,7 +184,7 @@ function export($guid) { * @param string $xml XML string * * @return bool - * @throws Exception if there was a problem importing the data. + * @throws ImportException if there was a problem importing the data. * @access private */ function import($xml) { diff --git a/engine/lib/extender.php b/engine/lib/extender.php index 538f601e1..8756e051b 100644 --- a/engine/lib/extender.php +++ b/engine/lib/extender.php @@ -86,6 +86,7 @@ function oddmetadata_to_elggextender(ElggEntity $entity, ODDMetaData $element) { * @return null * @elgg_plugin_hook_handler volatile metadata * @todo investigate more. + * @throws ImportException * @access private */ function import_extender_plugin_hook($hook, $entity_type, $returnvalue, $params) { @@ -94,6 +95,7 @@ function import_extender_plugin_hook($hook, $entity_type, $returnvalue, $params) $tmp = NULL; if ($element instanceof ODDMetaData) { + /* @var ODDMetaData $element */ // Recall entity $entity_uuid = $element->getAttribute('entity_uuid'); $entity = get_entity_from_uuid($entity_uuid); diff --git a/engine/lib/filestore.php b/engine/lib/filestore.php index 93a127257..677fac931 100644 --- a/engine/lib/filestore.php +++ b/engine/lib/filestore.php @@ -383,7 +383,7 @@ function file_get_general_file_type($mimetype) { /** * Delete a directory and all its contents * - * @param str $directory Directory to delete + * @param string $directory Directory to delete * * @return bool */ @@ -500,7 +500,7 @@ function filestore_init() { /** * Unit tests for files * - * @param sting $hook unit_test + * @param string $hook unit_test * @param string $type system * @param mixed $value Array of tests * @param mixed $params Params diff --git a/engine/lib/input.php b/engine/lib/input.php index 6d1646e1a..2d71fb77e 100644 --- a/engine/lib/input.php +++ b/engine/lib/input.php @@ -233,6 +233,7 @@ function elgg_clear_sticky_value($form_name, $variable) { * match_owner int 0/1 * limit int default is 10 * + * @param array $page * @return string JSON string is returned and then exit * @access private */ @@ -290,6 +291,7 @@ function input_livesearch_page_handler($page) { if ($entities = get_data($query)) { foreach ($entities as $entity) { $entity = get_entity($entity->guid); + /* @var ElggUser $entity */ if (!$entity) { continue; } @@ -339,6 +341,7 @@ function input_livesearch_page_handler($page) { if ($entities = get_data($query)) { foreach ($entities as $entity) { $entity = get_entity($entity->guid); + /* @var ElggGroup $entity */ if (!$entity) { continue; } @@ -386,6 +389,7 @@ function input_livesearch_page_handler($page) { if ($entities = get_data($query)) { foreach ($entities as $entity) { $entity = get_entity($entity->guid); + /* @var ElggUser $entity */ if (!$entity) { continue; } diff --git a/engine/lib/languages.php b/engine/lib/languages.php index 98006f7cd..11722f744 100644 --- a/engine/lib/languages.php +++ b/engine/lib/languages.php @@ -77,7 +77,7 @@ function elgg_echo($message_key, $args = array(), $language = "") { * @param string $country_code Standard country code (eg 'en', 'nl', 'es') * @param array $language_array Formatted array of strings * - * @return true|false Depending on success + * @return bool Depending on success */ function add_translation($country_code, $language_array) { global $CONFIG; @@ -177,7 +177,7 @@ function _elgg_load_translations() { * @param bool $load_all If true all languages are loaded, if * false only the current language + en are loaded * - * @return void + * @return bool success */ function register_translations($path, $load_all = false) { global $CONFIG; diff --git a/engine/lib/mb_wrapper.php b/engine/lib/mb_wrapper.php index c2f5503e0..68fa69005 100644 --- a/engine/lib/mb_wrapper.php +++ b/engine/lib/mb_wrapper.php @@ -11,7 +11,7 @@ if (is_callable('mb_internal_encoding')) { * NOTE: This differs from parse_str() by returning the results * instead of placing them in the local scope! * - * @param str $str The string + * @param string $str The string * * @return array * @since 1.7.0 diff --git a/engine/lib/metadata.php b/engine/lib/metadata.php index f76c20f24..2fa491963 100644 --- a/engine/lib/metadata.php +++ b/engine/lib/metadata.php @@ -277,7 +277,7 @@ $access_id = ACCESS_PRIVATE, $allow_multiple = false) { * all metadata that match the query instead of returning * ElggMetadata objects. * - * @return mixed + * @return ElggMetadata[]|mixed * @since 1.8.0 */ function elgg_get_metadata(array $options = array()) { @@ -412,7 +412,7 @@ function elgg_enable_metadata(array $options) { * * metadata_owner_guids => NULL|ARR guids for metadata owners * - * @return mixed If count, int. If not count, array. false on errors. + * @return ElggEntity[]|mixed If count, int. If not count, array. false on errors. * @since 1.7.0 */ function elgg_get_entities_from_metadata(array $options = array()) { @@ -461,7 +461,7 @@ function elgg_get_entities_from_metadata(array $options = array()) { * @param array|null $order_by_metadata Array of names / direction * @param array|null $owner_guids Array of owner GUIDs * - * @return FALSE|array False on fail, array('joins', 'wheres') + * @return false|array False on fail, array('joins', 'wheres') * @since 1.7.0 * @access private */ diff --git a/engine/lib/metastrings.php b/engine/lib/metastrings.php index cf6dd4d98..264d2ba2c 100644 --- a/engine/lib/metastrings.php +++ b/engine/lib/metastrings.php @@ -663,9 +663,10 @@ function elgg_normalize_metastrings_options(array $options = array()) { * * @param int $id The object's ID * @param string $enabled Value to set to: yes or no - * @param string $type The type of table to use: metadata or anntations + * @param string $type The type of table to use: metadata or annotations * * @return bool + * @throws InvalidParameterException * @since 1.8.0 * @access private */ @@ -740,7 +741,7 @@ function elgg_batch_metastring_based_objects(array $options, $callback, $inc_off * * @param int $id The metastring-based object's ID * @param string $type The type: annotation or metadata - * @return mixed + * @return ElggMetadata|ElggAnnotation * * @since 1.8.0 * @access private diff --git a/engine/lib/navigation.php b/engine/lib/navigation.php index 86624cd7c..0e8914bbb 100644 --- a/engine/lib/navigation.php +++ b/engine/lib/navigation.php @@ -126,6 +126,7 @@ function elgg_unregister_menu_item($menu_name, $item_name) { } foreach ($CONFIG->menus[$menu_name] as $index => $menu_object) { + /* @var ElggMenuItem $menu_object */ if ($menu_object->getName() == $item_name) { unset($CONFIG->menus[$menu_name][$index]); return true; @@ -152,6 +153,7 @@ function elgg_is_menu_item_registered($menu_name, $item_name) { } foreach ($CONFIG->menus[$menu_name] as $index => $menu_object) { + /* @var ElggMenuItem $menu_object */ if ($menu_object->getName() == $item_name) { return true; } @@ -345,6 +347,7 @@ function elgg_site_menu_setup($hook, $type, $return, $params) { function elgg_river_menu_setup($hook, $type, $return, $params) { if (elgg_is_logged_in()) { $item = $params['item']; + /* @var ElggRiverItem $item */ $object = $item->getObjectEntity(); // comments and non-objects cannot be commented on or liked if (!elgg_in_context('widgets') && $item->annotation_id == 0) { @@ -388,6 +391,7 @@ function elgg_entity_menu_setup($hook, $type, $return, $params) { } $entity = $params['entity']; + /* @var ElggEntity $entity */ $handler = elgg_extract('handler', $params, false); // access @@ -433,6 +437,7 @@ function elgg_entity_menu_setup($hook, $type, $return, $params) { function elgg_widget_menu_setup($hook, $type, $return, $params) { $widget = $params['entity']; + /* @var ElggWidget $widget */ $show_edit = elgg_extract('show_edit', $params, true); $collapse = array( @@ -481,6 +486,7 @@ function elgg_widget_menu_setup($hook, $type, $return, $params) { */ function elgg_annotation_menu_setup($hook, $type, $return, $params) { $annotation = $params['annotation']; + /* @var ElggAnnotation $annotation */ if ($annotation->name == 'generic_comment' && $annotation->canEdit()) { $url = elgg_http_add_url_query_elements('action/comments/delete', array( diff --git a/engine/lib/notification.php b/engine/lib/notification.php index 09ebc9541..4191407fc 100644 --- a/engine/lib/notification.php +++ b/engine/lib/notification.php @@ -237,6 +237,7 @@ function set_user_notification_setting($user_guid, $method, $value) { * @param array $params Optional parameters (none taken in this instance) * * @return bool + * @throws NotificationException * @access private */ function email_notify_handler(ElggEntity $from, ElggUser $to, $subject, $message, @@ -288,6 +289,7 @@ array $params = NULL) { * @param array $params Optional parameters (none used in this function) * * @return bool + * @throws NotificationException * @since 1.7.2 */ function elgg_send_email($from, $to, $subject, $body, array $params = NULL) { @@ -422,7 +424,7 @@ function register_notification_object($entity_type, $object_subtype, $language_n * @param int $user_guid The GUID of the user who wants to follow a user's content * @param int $author_guid The GUID of the user whose content the user wants to follow * - * @return true|false Depending on success + * @return bool Depending on success */ function register_notification_interest($user_guid, $author_guid) { return add_entity_relationship($user_guid, 'notify', $author_guid); @@ -434,7 +436,7 @@ function register_notification_interest($user_guid, $author_guid) { * @param int $user_guid The GUID of the user who is following a user's content * @param int $author_guid The GUID of the user whose content the user wants to unfollow * - * @return true|false Depending on success + * @return bool Depending on success */ function remove_notification_interest($user_guid, $author_guid) { return remove_entity_relationship($user_guid, 'notify', $author_guid); @@ -450,12 +452,13 @@ function remove_notification_interest($user_guid, $author_guid) { * @param string $object_type mixed * @param mixed $object The object created * - * @return void + * @return bool * @access private */ function object_notifications($event, $object_type, $object) { // We only want to trigger notification events for ElggEntities if ($object instanceof ElggEntity) { + /* @var ElggEntity $object */ // Get config data global $CONFIG, $SESSION, $NOTIFICATION_HANDLERS; @@ -495,6 +498,7 @@ function object_notifications($event, $object_type, $object) { 'type' => 'user', 'limit' => false )); + /* @var ElggUser[] $interested_users */ if ($interested_users && is_array($interested_users)) { foreach ($interested_users as $user) { diff --git a/engine/lib/objects.php b/engine/lib/objects.php index 852bdb793..81b6b7f12 100644 --- a/engine/lib/objects.php +++ b/engine/lib/objects.php @@ -102,7 +102,7 @@ function get_object_sites($object_guid, $limit = 10, $offset = 0) { /** * Runs unit tests for ElggObject * - * @param sting $hook unit_test + * @param string $hook unit_test * @param string $type system * @param mixed $value Array of tests * @param mixed $params Params diff --git a/engine/lib/output.php b/engine/lib/output.php index 9295f2173..fefb94b3e 100644 --- a/engine/lib/output.php +++ b/engine/lib/output.php @@ -12,7 +12,7 @@ * * @param string $text The input string * - * @return string The output stirng with formatted links + * @return string The output string with formatted links **/ function parse_urls($text) { // @todo this causes problems with @@ -421,7 +421,7 @@ function _elgg_html_decode($string) { /** * Unit tests for Output * - * @param sting $hook unit_test + * @param string $hook unit_test * @param string $type system * @param mixed $value Array of tests * @param mixed $params Params diff --git a/engine/lib/pageowner.php b/engine/lib/pageowner.php index 94765feee..bf5901aad 100644 --- a/engine/lib/pageowner.php +++ b/engine/lib/pageowner.php @@ -39,7 +39,7 @@ function elgg_get_page_owner_guid($guid = 0) { * * @note Access is disabled when getting the page owner entity. * - * @return ElggEntity|false The current page owner or false if none. + * @return ElggUser|ElggGroup|false The current page owner or false if none. * * @since 1.8.0 */ diff --git a/engine/lib/plugins.php b/engine/lib/plugins.php index 94aff277e..3a42cb9b8 100644 --- a/engine/lib/plugins.php +++ b/engine/lib/plugins.php @@ -107,6 +107,7 @@ function elgg_generate_plugin_entities() { $old_access = access_get_show_hidden_status(); access_show_hidden_entities(true); $known_plugins = elgg_get_entities_from_relationship($options); + /* @var ElggPlugin[] $known_plugins */ if (!$known_plugins) { $known_plugins = array(); @@ -192,7 +193,7 @@ function _elgg_cache_plugin_by_id(ElggPlugin $plugin) { * Returns an ElggPlugin object with the path $path. * * @param string $plugin_id The id (dir name) of the plugin. NOT the guid. - * @return mixed ElggPlugin or false. + * @return ElggPlugin|false * @since 1.8.0 */ function elgg_get_plugin_from_id($plugin_id) { @@ -360,7 +361,7 @@ function elgg_load_plugins() { * * @param string $status The status of the plugins. active, inactive, or all. * @param mixed $site_guid Optional site guid - * @return array + * @return ElggPlugin[] * @since 1.8.0 * @access private */ @@ -861,9 +862,9 @@ function elgg_set_plugin_user_setting($name, $value, $user_guid = null, $plugin_ /** * Unsets a user-specific plugin setting * - * @param str $name Name of the setting + * @param string $name Name of the setting * @param int $user_guid Defaults to logged in user - * @param str $plugin_id Defaults to contextual plugin name + * @param string $plugin_id Defaults to contextual plugin name * * @return bool * @since 1.8.0 @@ -1087,7 +1088,7 @@ function plugin_run_once() { /** * Runs unit tests for the entity objects. * - * @param sting $hook unit_test + * @param string $hook unit_test * @param string $type system * @param mixed $value Array of tests * @param mixed $params Params diff --git a/engine/lib/relationships.php b/engine/lib/relationships.php index 41bfa4e73..74954b4ff 100644 --- a/engine/lib/relationships.php +++ b/engine/lib/relationships.php @@ -12,7 +12,7 @@ * * @param stdClass $row Database row from the relationship table * - * @return stdClass or ElggMetadata + * @return ElggRelationship|stdClass * @access private */ function row_to_elggrelationship($row) { @@ -28,7 +28,7 @@ function row_to_elggrelationship($row) { * * @param int $id The ID of a relationship * - * @return mixed + * @return ElggRelationship|false */ function get_relationship($id) { global $CONFIG; @@ -220,7 +220,7 @@ function remove_entity_relationships($guid_one, $relationship = "", $inverse = f * @param int $guid The GUID of the relationship owner * @param bool $inverse_relationship Inverse relationship owners? * - * @return mixed + * @return ElggRelationship[] */ function get_entity_relationships($guid, $inverse_relationship = FALSE) { global $CONFIG; @@ -259,7 +259,7 @@ function get_entity_relationships($guid, $inverse_relationship = FALSE) { * * inverse_relationship => BOOL Inverse the relationship * - * @return mixed If count, int. If not count, array. false on errors. + * @return ElggEntity[]|mixed If count, int. If not count, array. false on errors. * @since 1.7.0 */ function elgg_get_entities_from_relationship($options) { @@ -316,7 +316,7 @@ function elgg_get_entities_from_relationship($options) { * Provide in table.column format. * @param string $relationship Relationship string * @param int $relationship_guid Entity guid to check - * @param string $inverse_relationship Inverse relationship check? + * @param bool $inverse_relationship Inverse relationship check? * * @return mixed * @since 1.7.0 @@ -381,7 +381,7 @@ function elgg_list_entities_from_relationship(array $options = array()) { * * @param array $options An options array compatible with * elgg_get_entities_from_relationship() - * @return mixed int If count, int. If not count, array. false on errors. + * @return ElggEntity[]|mixed int If count, int. If not count, array. false on errors. * @since 1.8.0 */ function elgg_get_entities_from_relationship_count(array $options = array()) { @@ -398,7 +398,7 @@ function elgg_get_entities_from_relationship_count(array $options = array()) { * * @param array $options Options array * - * @return array + * @return string * @since 1.8.0 */ function elgg_list_entities_from_relationship_count($options) { @@ -499,7 +499,7 @@ function already_attached($guid_one, $guid_two) { * @param int $guid Entity GUID * @param string $type The type of object to return e.g. 'file', 'friend_of' etc * - * @return an array of objects + * @return ElggEntity[] * @access private */ function get_attachments($guid, $type = "") { @@ -586,6 +586,7 @@ function import_relationship_plugin_hook($hook, $entity_type, $returnvalue, $par * * @elgg_event_handler export all * @return mixed + * @throws InvalidParameterException * @access private */ function export_relationship_plugin_hook($hook, $entity_type, $returnvalue, $params) { @@ -624,9 +625,10 @@ function export_relationship_plugin_hook($hook, $entity_type, $returnvalue, $par * @access private */ function relationship_notification_hook($event, $type, $object) { - + /* @var ElggRelationship $object */ $user_one = get_entity($object->guid_one); $user_two = get_entity($object->guid_two); + /* @var ElggUser $user_one */ return notify_user($object->guid_two, $object->guid_one, diff --git a/engine/lib/river.php b/engine/lib/river.php index 33f34360e..148fee051 100644 --- a/engine/lib/river.php +++ b/engine/lib/river.php @@ -644,7 +644,7 @@ function update_river_access_by_object($object_guid, $access_id) { } /** - * Page handler for activiy + * Page handler for activity * * @param array $page * @return bool diff --git a/engine/lib/sites.php b/engine/lib/sites.php index 5a390167d..fe9af8c7a 100644 --- a/engine/lib/sites.php +++ b/engine/lib/sites.php @@ -242,7 +242,7 @@ function get_site_domain($guid) { /** * Unit tests for sites * - * @param sting $hook unit_test + * @param string $hook unit_test * @param string $type system * @param mixed $value Array of tests * @param mixed $params Params diff --git a/engine/lib/system_log.php b/engine/lib/system_log.php index 53fa24557..8149d3fac 100644 --- a/engine/lib/system_log.php +++ b/engine/lib/system_log.php @@ -22,7 +22,7 @@ * @param int $timebefore Lower time limit * @param int $timeafter Upper time limit * @param int $object_id GUID of an object - * @param str $ip_address The IP address. + * @param string $ip_address The IP address. * @return mixed */ function get_system_log($by_user = "", $event = "", $class = "", $type = "", $subtype = "", @@ -166,6 +166,7 @@ function system_log($object, $event) { if ($object instanceof Loggable) { + /* @var ElggEntity|ElggExtender $object */ if (datalist_get('version') < 2012012000) { // this is a site that doesn't have the ip_address column yet return; diff --git a/engine/lib/tags.php b/engine/lib/tags.php index a0887d0f3..a25bd7d33 100644 --- a/engine/lib/tags.php +++ b/engine/lib/tags.php @@ -48,7 +48,7 @@ function calculate_tag_size($min, $max, $number_of_tags, $buckets = 6) { * @param array $tags The array of tags. * @param int $buckets The number of buckets * - * @return An associated array of tags with a weighting, this can then be mapped to a display class. + * @return array An associated array of tags with a weighting, this can then be mapped to a display class. * @access private */ function generate_tag_cloud(array $tags, $buckets = 6) { @@ -114,8 +114,8 @@ function generate_tag_cloud(array $tags, $buckets = 6) { * * joins => array() Additional joins * - * @return false/array - if no tags or error, false - * otherwise, array of objects with ->tag and ->total values + * @return object[]|false If no tags or error, false + * otherwise, array of objects with ->tag and ->total values * @since 1.7.1 */ function elgg_get_tags(array $options = array()) { diff --git a/engine/lib/upgrade.php b/engine/lib/upgrade.php index f4f4b16f5..ae4591b0b 100644 --- a/engine/lib/upgrade.php +++ b/engine/lib/upgrade.php @@ -360,6 +360,7 @@ function _elgg_upgrade_is_locked() { // Invalidate query cache if ($DB_QUERY_CACHE) { + /* @var ElggStaticVariableCache $DB_QUERY_CACHE */ $DB_QUERY_CACHE->clear(); elgg_log("Query cache invalidated", 'NOTICE'); } diff --git a/engine/lib/users.php b/engine/lib/users.php index 9d1fba2d4..22ce08e10 100644 --- a/engine/lib/users.php +++ b/engine/lib/users.php @@ -290,7 +290,7 @@ function remove_user_admin($user_guid) { * @param int $limit Number of results to return * @param int $offset Any indexing offset * - * @return false|array On success, an array of ElggSites + * @return ElggSite[]|false On success, an array of ElggSites */ function get_user_sites($user_guid, $limit = 10, $offset = 0) { $user_guid = (int)$user_guid; @@ -379,7 +379,7 @@ function user_is_friend($user_guid, $friend_guid) { * @param int $limit Number of results to return (default 10) * @param int $offset Indexing offset, if any * - * @return false|array Either an array of ElggUsers or false, depending on success + * @return ElggUser[]|false Either an array of ElggUsers or false, depending on success */ function get_user_friends($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $limit = 10, $offset = 0) { @@ -402,7 +402,7 @@ $offset = 0) { * @param int $limit Number of results to return (default 10) * @param int $offset Indexing offset, if any * - * @return false|array Either an array of ElggUsers or false, depending on success + * @return ElggUser[]|false Either an array of ElggUsers or false, depending on success */ function get_user_friends_of($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $limit = 10, $offset = 0) { @@ -428,7 +428,7 @@ $offset = 0) { * @param int $timelower The earliest time the entity can have been created. Default: all * @param int $timeupper The latest time the entity can have been created. Default: all * - * @return false|array An array of ElggObjects or false, depending on success + * @return ElggObject[]|false An array of ElggObjects or false, depending on success */ function get_user_friends_objects($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $limit = 10, $offset = 0, $timelower = 0, $timeupper = 0) { @@ -681,6 +681,8 @@ function send_new_password_request($user_guid) { $user = get_entity($user_guid); if ($user) { + /* @var ElggUser $user */ + // generate code $code = generate_random_cleartext_password(); $user->setPrivateSetting('passwd_conf_code', $code); @@ -713,6 +715,7 @@ function force_user_password_reset($user_guid, $password) { global $CONFIG; $user = get_entity($user_guid); + /* @var ElggUser $user */ if ($user) { $salt = generate_random_cleartext_password(); // Reset the salt @@ -743,6 +746,7 @@ function execute_new_password_request($user_guid, $conf_code) { $user = get_entity($user_guid); if ($user) { + /* @var ElggUser $user */ $saved_code = $user->getPrivateSetting('passwd_conf_code'); if ($saved_code && $saved_code == $conf_code) { @@ -908,6 +912,7 @@ function validate_email_address($address) { * @param string $invitecode An invite code from a friend * * @return int|false The new user's GUID; false on failure + * @throws RegistrationException */ function register_user($username, $password, $name, $email, $allow_multiple_emails = false, $friend_guid = 0, $invitecode = '') { @@ -1233,6 +1238,7 @@ function user_avatar_hook($hook, $entity_type, $returnvalue, $params) { */ function elgg_user_hover_menu($hook, $type, $return, $params) { $user = $params['entity']; + /* @var ElggUser $user */ if (elgg_is_logged_in()) { if (elgg_get_logged_in_user_guid() != $user->guid) { @@ -1309,7 +1315,12 @@ function elgg_user_hover_menu($hook, $type, $return, $params) { /** * Setup the menu shown with an entity * + * @param string $hook + * @param string $type + * @param array $return + * @param array $params * @return array + * * @access private */ function elgg_users_setup_entity_menu($hook, $type, $return, $params) { @@ -1321,6 +1332,7 @@ function elgg_users_setup_entity_menu($hook, $type, $return, $params) { if (!elgg_instanceof($entity, 'user')) { return $return; } + /* @var ElggUser $entity */ if ($entity->isBanned()) { $banned = elgg_echo('banned'); @@ -1587,7 +1599,7 @@ function users_init() { /** * Runs unit tests for ElggObject * - * @param sting $hook unit_test + * @param string $hook unit_test * @param string $type system * @param mixed $value Array of tests * @param mixed $params Params diff --git a/engine/lib/views.php b/engine/lib/views.php index 8618c2997..9ee7ae00c 100644 --- a/engine/lib/views.php +++ b/engine/lib/views.php @@ -1308,7 +1308,7 @@ function elgg_view_form($action, $form_vars = array(), $body_vars = array()) { /** * View an item in a list * - * @param object $item ElggEntity or ElggAnnotation + * @param ElggEntity|ElggAnnotation $item * @param array $vars Additional parameters for the rendering * * @return string diff --git a/engine/lib/widgets.php b/engine/lib/widgets.php index d73dd6330..699462a1b 100644 --- a/engine/lib/widgets.php +++ b/engine/lib/widgets.php @@ -336,7 +336,7 @@ function elgg_default_widgets_init() { * * @param string $event The event * @param string $type The type of object - * @param object $entity The entity being created + * @param ElggEntity $entity The entity being created * @return void * @access private */ @@ -372,6 +372,7 @@ function elgg_create_default_widgets($event, $type, $entity) { ); $widgets = elgg_get_entities_from_private_settings($options); + /* @var ElggWidget[] $widgets */ foreach ($widgets as $widget) { // change the container and owner diff --git a/engine/lib/xml.php b/engine/lib/xml.php index ff82d7e8a..497459d83 100644 --- a/engine/lib/xml.php +++ b/engine/lib/xml.php @@ -104,7 +104,7 @@ function serialise_array_to_xml(array $data, $n = 0) { * * @param string $xml The XML * - * @return object + * @return ElggXMLElement */ function xml_to_object($xml) { return new ElggXMLElement($xml); -- cgit v1.2.3 From 707df9db27cf2154910978427b9844448bcf931a Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Mon, 4 Feb 2013 20:51:07 -0500 Subject: Small changes to remove static analysis noise and simplify flow --- engine/classes/ElggAutoP.php | 11 ++++++----- engine/classes/ElggData.php | 1 + engine/classes/ElggDiskFilestore.php | 2 -- engine/classes/ElggFile.php | 4 +++- engine/classes/ElggGroup.php | 2 +- engine/classes/ElggMenuBuilder.php | 1 + engine/classes/ElggRelationship.php | 2 ++ engine/lib/admin.php | 2 +- engine/lib/annotations.php | 1 + engine/lib/cache.php | 6 +++--- engine/lib/configuration.php | 2 +- engine/lib/cron.php | 1 - engine/lib/elgglib.php | 1 + engine/lib/export.php | 17 +++++++++-------- engine/lib/location.php | 2 +- engine/lib/plugins.php | 16 +++++++++------- engine/lib/relationships.php | 3 +-- engine/lib/river.php | 1 + engine/lib/sessions.php | 2 +- engine/lib/sites.php | 2 +- engine/lib/statistics.php | 3 ++- engine/lib/system_log.php | 6 +++--- engine/lib/tags.php | 1 + engine/lib/upgrades/create_upgrade.php | 3 ++- engine/lib/user_settings.php | 1 + engine/lib/users.php | 16 ++++++++-------- engine/lib/views.php | 6 +----- 27 files changed, 62 insertions(+), 53 deletions(-) diff --git a/engine/classes/ElggAutoP.php b/engine/classes/ElggAutoP.php index 40600aa13..f3c7cc972 100644 --- a/engine/classes/ElggAutoP.php +++ b/engine/classes/ElggAutoP.php @@ -159,8 +159,9 @@ class ElggAutoP { /* @var DOMElement $el */ $autops = $this->_xpath->query('./autop', $el); if ($autops->length === 1) { - // strip w/ preg_replace later (faster than moving nodes out) - $autops->item(0)->setAttribute("r", "1"); + $firstAutop = $autops->item(0); + /* @var DOMElement $firstAutop */ + $firstAutop->setAttribute("r", "1"); } } @@ -220,12 +221,12 @@ class ElggAutoP { $isElement = ($node->nodeType === XML_ELEMENT_NODE); if ($isElement) { - $elName = $node->nodeName; + $isBlock = in_array($node->nodeName, $this->_blocks); + } else { + $isBlock = false; } - $isBlock = ($isElement && in_array($elName, $this->_blocks)); if ($alterInline) { - $isInline = $isElement && ! $isBlock; $isText = ($node->nodeType === XML_TEXT_NODE); $isLastInline = (! $node->nextSibling || ($node->nextSibling->nodeType === XML_ELEMENT_NODE diff --git a/engine/classes/ElggData.php b/engine/classes/ElggData.php index a0df3c924..426248ca3 100644 --- a/engine/classes/ElggData.php +++ b/engine/classes/ElggData.php @@ -273,6 +273,7 @@ abstract class ElggData implements if (array_key_exists($key, $this->attributes)) { return $this->attributes[$key]; } + return null; } /** diff --git a/engine/classes/ElggDiskFilestore.php b/engine/classes/ElggDiskFilestore.php index 5483eed81..2d86e7c2d 100644 --- a/engine/classes/ElggDiskFilestore.php +++ b/engine/classes/ElggDiskFilestore.php @@ -316,8 +316,6 @@ class ElggDiskFilestore extends ElggFilestore { } else { return str_split($string); } - - return false; } /** diff --git a/engine/classes/ElggFile.php b/engine/classes/ElggFile.php index 86406d108..3f652ff19 100644 --- a/engine/classes/ElggFile.php +++ b/engine/classes/ElggFile.php @@ -127,9 +127,11 @@ class ElggFile extends ElggObject { * @param mixed $default A default. Useful to pass what the browser thinks it is. * @since 1.7.12 * + * @note If $file is provided, this may be called statically + * * @return mixed Detected type on success, false on failure. */ - static function detectMimeType($file = null, $default = null) { + public function detectMimeType($file = null, $default = null) { if (!$file) { if (isset($this) && $this->filename) { $file = $this->filename; diff --git a/engine/classes/ElggGroup.php b/engine/classes/ElggGroup.php index d17fa4f1a..24bdee79e 100644 --- a/engine/classes/ElggGroup.php +++ b/engine/classes/ElggGroup.php @@ -284,7 +284,7 @@ class ElggGroup extends ElggEntity * * @return bool */ - public function isMember($user = 0) { + public function isMember($user = null) { if (!($user instanceof ElggUser)) { $user = elgg_get_logged_in_user_entity(); } diff --git a/engine/classes/ElggMenuBuilder.php b/engine/classes/ElggMenuBuilder.php index 2fd5ad9c4..028eef15f 100644 --- a/engine/classes/ElggMenuBuilder.php +++ b/engine/classes/ElggMenuBuilder.php @@ -122,6 +122,7 @@ class ElggMenuBuilder { // attach children to parents $iteration = 0; $current_gen = $parents; + $next_gen = null; while (count($children) && $iteration < 5) { foreach ($children as $index => $menu_item) { $parent_name = $menu_item->getParentName(); diff --git a/engine/classes/ElggRelationship.php b/engine/classes/ElggRelationship.php index 377a41093..d2e88882a 100644 --- a/engine/classes/ElggRelationship.php +++ b/engine/classes/ElggRelationship.php @@ -180,6 +180,8 @@ class ElggRelationship extends ElggData implements return true; } } + + return false; } // SYSTEM LOG INTERFACE //////////////////////////////////////////////////////////// diff --git a/engine/lib/admin.php b/engine/lib/admin.php index 3677a3b69..ec19a5476 100644 --- a/engine/lib/admin.php +++ b/engine/lib/admin.php @@ -346,7 +346,7 @@ function elgg_admin_add_plugin_settings_menu() { $active_plugins = elgg_get_plugins('active'); if (!$active_plugins) { // nothing added because no items - return FALSE; + return; } foreach ($active_plugins as $plugin) { diff --git a/engine/lib/annotations.php b/engine/lib/annotations.php index 9ba0491b2..3d94a1d55 100644 --- a/engine/lib/annotations.php +++ b/engine/lib/annotations.php @@ -542,6 +542,7 @@ function elgg_comment_url_handler(ElggAnnotation $comment) { if ($entity) { return $entity->getURL() . '#item-annotation-' . $comment->id; } + return ""; } /** diff --git a/engine/lib/cache.php b/engine/lib/cache.php index 5c917bb18..74644019c 100644 --- a/engine/lib/cache.php +++ b/engine/lib/cache.php @@ -125,7 +125,7 @@ function elgg_get_filepath_cache() { * @access private */ function elgg_filepath_cache_reset() { - return elgg_reset_system_cache(); + elgg_reset_system_cache(); } /** * @access private @@ -143,13 +143,13 @@ function elgg_filepath_cache_load($type) { * @access private */ function elgg_enable_filepath_cache() { - return elgg_enable_system_cache(); + elgg_enable_system_cache(); } /** * @access private */ function elgg_disable_filepath_cache() { - return elgg_disable_system_cache(); + elgg_disable_system_cache(); } /* Simplecache */ diff --git a/engine/lib/configuration.php b/engine/lib/configuration.php index 851430127..a0f297f0c 100644 --- a/engine/lib/configuration.php +++ b/engine/lib/configuration.php @@ -182,7 +182,7 @@ function verify_installation() { global $CONFIG; if (isset($CONFIG->installed)) { - return $CONFIG->installed; + return; } try { diff --git a/engine/lib/cron.php b/engine/lib/cron.php index f2939bdd6..dd83a9849 100644 --- a/engine/lib/cron.php +++ b/engine/lib/cron.php @@ -52,7 +52,6 @@ function cron_page_handler($page) { $params['time'] = time(); // Data to return to - $std_out = ""; $old_stdout = ""; ob_start(); diff --git a/engine/lib/elgglib.php b/engine/lib/elgglib.php index 746fd8aa9..6a2ebf97b 100644 --- a/engine/lib/elgglib.php +++ b/engine/lib/elgglib.php @@ -1903,6 +1903,7 @@ function elgg_cacheable_view_page_handler($page, $type) { echo $return; return true; } + return false; } /** diff --git a/engine/lib/export.php b/engine/lib/export.php index 31afc1b74..1996bc885 100644 --- a/engine/lib/export.php +++ b/engine/lib/export.php @@ -117,18 +117,19 @@ function _process_element(ODD $odd) { global $IMPORTED_DATA, $IMPORTED_OBJECT_COUNTER; // See if anyone handles this element, return true if it is. + $to_be_serialised = null; if ($odd) { $handled = elgg_trigger_plugin_hook("import", "all", array("element" => $odd), $to_be_serialised); - } - // If not, then see if any of its sub elements are handled - if ($handled) { - // Increment validation counter - $IMPORTED_OBJECT_COUNTER ++; - // Return the constructed object - $IMPORTED_DATA[] = $handled; + // If not, then see if any of its sub elements are handled + if ($handled) { + // Increment validation counter + $IMPORTED_OBJECT_COUNTER ++; + // Return the constructed object + $IMPORTED_DATA[] = $handled; - return true; + return true; + } } return false; diff --git a/engine/lib/location.php b/engine/lib/location.php index 5b889509b..b319bb3bb 100644 --- a/engine/lib/location.php +++ b/engine/lib/location.php @@ -101,7 +101,7 @@ function elgg_get_entities_from_location(array $options = array()) { $long_min = $long - $long_distance; $long_max = $long + $long_distance; - $where = array(); + $wheres = array(); $wheres[] = "lat_name.string='geo:lat'"; $wheres[] = "lat_value.string >= $lat_min"; $wheres[] = "lat_value.string <= $lat_max"; diff --git a/engine/lib/plugins.php b/engine/lib/plugins.php index 3a42cb9b8..973ca4026 100644 --- a/engine/lib/plugins.php +++ b/engine/lib/plugins.php @@ -261,6 +261,8 @@ function elgg_get_max_plugin_priority() { $data = get_data($q); if ($data) { $max = $data[0]->max; + } else { + $max = 1; } // can't have a priority of 0. @@ -442,6 +444,7 @@ function elgg_set_plugin_priorities(array $order) { // though we do start with 1 $order = array_values($order); + $missing_plugins = array(); foreach ($plugins as $plugin) { $plugin_id = $plugin->getID(); @@ -640,19 +643,18 @@ function elgg_get_plugins_provides($type = null, $name = null) { * @access private */ function elgg_check_plugins_provides($type, $name, $version = null, $comparison = 'ge') { - if (!$provided = elgg_get_plugins_provides($type, $name)) { + $provided = elgg_get_plugins_provides($type, $name); + if (!$provided) { return array( 'status' => false, 'version' => '' ); } - if ($provided) { - if ($version) { - $status = version_compare($provided['version'], $version, $comparison); - } else { - $status = true; - } + if ($version) { + $status = version_compare($provided['version'], $version, $comparison); + } else { + $status = true; } return array( diff --git a/engine/lib/relationships.php b/engine/lib/relationships.php index 74954b4ff..6f18bda93 100644 --- a/engine/lib/relationships.php +++ b/engine/lib/relationships.php @@ -571,9 +571,8 @@ function import_relationship_plugin_hook($hook, $entity_type, $returnvalue, $par if ($element instanceof ODDRelationship) { $tmp = new ElggRelationship(); $tmp->import($element); - - return $tmp; } + return $tmp; } /** diff --git a/engine/lib/river.php b/engine/lib/river.php index 148fee051..6274887b5 100644 --- a/engine/lib/river.php +++ b/engine/lib/river.php @@ -500,6 +500,7 @@ function elgg_get_river_type_subtype_where_sql($table, $types, $subtypes, $pairs return ''; } + $wheres = array(); $types_wheres = array(); $subtypes_wheres = array(); diff --git a/engine/lib/sessions.php b/engine/lib/sessions.php index 72ca0a1c2..eb451f05a 100644 --- a/engine/lib/sessions.php +++ b/engine/lib/sessions.php @@ -616,7 +616,7 @@ function _elgg_session_destroy($id) { global $sess_save_path; $sess_file = "$sess_save_path/sess_$id"; - return(@unlink($sess_file)); + return (@unlink($sess_file)); } return false; diff --git a/engine/lib/sites.php b/engine/lib/sites.php index fe9af8c7a..8ee3213d3 100644 --- a/engine/lib/sites.php +++ b/engine/lib/sites.php @@ -26,7 +26,7 @@ function elgg_get_site_entity($site_guid = 0) { $site = get_entity($site_guid); } - if($site instanceof ElggSite){ + if ($site instanceof ElggSite) { $result = $site; } diff --git a/engine/lib/statistics.php b/engine/lib/statistics.php index 5ee640549..0c9a3c945 100644 --- a/engine/lib/statistics.php +++ b/engine/lib/statistics.php @@ -101,9 +101,10 @@ function get_online_users() { if ($objects) { return elgg_view_entity_list($objects, array( 'count' => $count, - 'limit' => 10 + 'limit' => 10, )); } + return ''; } /** diff --git a/engine/lib/system_log.php b/engine/lib/system_log.php index 8149d3fac..38bc4ba96 100644 --- a/engine/lib/system_log.php +++ b/engine/lib/system_log.php @@ -25,9 +25,9 @@ * @param string $ip_address The IP address. * @return mixed */ -function get_system_log($by_user = "", $event = "", $class = "", $type = "", $subtype = "", -$limit = 10, $offset = 0, $count = false, $timebefore = 0, $timeafter = 0, $object_id = 0, -$ip_address = false) { +function get_system_log($by_user = "", $event = "", $class = "", $type = "", $subtype = "", $limit = 10, + $offset = 0, $count = false, $timebefore = 0, $timeafter = 0, $object_id = 0, + $ip_address = "") { global $CONFIG; diff --git a/engine/lib/tags.php b/engine/lib/tags.php index a25bd7d33..586a9b9e4 100644 --- a/engine/lib/tags.php +++ b/engine/lib/tags.php @@ -172,6 +172,7 @@ function elgg_get_tags(array $options = array()) { // catch for tags that were spaces $wheres[] = "msv.string != ''"; + $sanitised_tags = array(); foreach ($options['tag_names'] as $tag) { $sanitised_tags[] = '"' . sanitise_string($tag) . '"'; } diff --git a/engine/lib/upgrades/create_upgrade.php b/engine/lib/upgrades/create_upgrade.php index 3652e18a2..f0fa9b594 100644 --- a/engine/lib/upgrades/create_upgrade.php +++ b/engine/lib/upgrades/create_upgrade.php @@ -93,7 +93,7 @@ if (!$h) { die("Could not open file $upgrade_file"); } -if (!fputs($h, $upgrade_code)) { +if (!fwrite($h, $upgrade_code)) { die("Could not write to $upgrade_file"); } else { elgg_set_version_dot_php_version($upgrade_version); @@ -130,6 +130,7 @@ function elgg_set_version_dot_php_version($version) { fputs($h, $out); fclose($h); + return true; } /** diff --git a/engine/lib/user_settings.php b/engine/lib/user_settings.php index e4069fb53..cca5359a4 100644 --- a/engine/lib/user_settings.php +++ b/engine/lib/user_settings.php @@ -332,6 +332,7 @@ function usersettings_page_handler($page) { require $path; return true; } + return false; } /** diff --git a/engine/lib/users.php b/engine/lib/users.php index 22ce08e10..7a6245261 100644 --- a/engine/lib/users.php +++ b/engine/lib/users.php @@ -560,7 +560,7 @@ function get_user_by_username($username) { // Caching if ((isset($USERNAME_TO_GUID_MAP_CACHE[$username])) - && (retrieve_cached_entity($USERNAME_TO_GUID_MAP_CACHE[$username]))) { + && (retrieve_cached_entity($USERNAME_TO_GUID_MAP_CACHE[$username]))) { return retrieve_cached_entity($USERNAME_TO_GUID_MAP_CACHE[$username]); } @@ -687,15 +687,14 @@ function send_new_password_request($user_guid) { $code = generate_random_cleartext_password(); $user->setPrivateSetting('passwd_conf_code', $code); - // generate link - $link = $CONFIG->site->url . "resetpassword?u=$user_guid&c=$code"; + $link = elgg_get_site_url() . "resetpassword?u=$user_guid&c=$code"; // generate email $email = elgg_echo('email:resetreq:body', array($user->name, $_SERVER['REMOTE_ADDR'], $link)); - return notify_user($user->guid, $CONFIG->site->guid, - elgg_echo('email:resetreq:subject'), $email, NULL, 'email'); + return notify_user($user->guid, elgg_get_site_entity()->guid, + elgg_echo('email:resetreq:subject'), $email, array(), 'email'); } return false; @@ -760,7 +759,7 @@ function execute_new_password_request($user_guid, $conf_code) { $email = elgg_echo('email:resetpassword:body', array($user->name, $password)); return notify_user($user->guid, $CONFIG->site->guid, - elgg_echo('email:resetpassword:subject'), $email, NULL, 'email'); + elgg_echo('email:resetpassword:subject'), $email, array(), 'email'); } } } @@ -1036,7 +1035,7 @@ function elgg_get_user_validation_status($user_guid) { 'metadata_name' => 'validated' )); if ($md == false) { - return; + return null; } if ($md[0]->value) { @@ -1208,7 +1207,8 @@ function set_last_login($user_guid) { function user_create_hook_add_site_relationship($event, $object_type, $object) { global $CONFIG; - add_entity_relationship($object->getGUID(), 'member_of_site', $CONFIG->site->getGUID()); + add_entity_relationship($object->getGUID(), 'member_of_site', elgg_get_site_entity()->guid); + return true; } /** diff --git a/engine/lib/views.php b/engine/lib/views.php index 9ee7ae00c..7d8347863 100644 --- a/engine/lib/views.php +++ b/engine/lib/views.php @@ -1451,17 +1451,13 @@ function elgg_get_views($dir, $base) { */ function elgg_view_tree($view_root, $viewtype = "") { global $CONFIG; - static $treecache; + static $treecache = array(); // Get viewtype if (!$viewtype) { $viewtype = elgg_get_viewtype(); } - // Has the treecache been initialised? - if (!isset($treecache)) { - $treecache = array(); - } // A little light internal caching if (!empty($treecache[$view_root])) { return $treecache[$view_root]; -- cgit v1.2.3 From f416d7ba66000d485260a8e63962541afa248320 Mon Sep 17 00:00:00 2001 From: Cash Costello Date: Sat, 9 Feb 2013 08:01:12 -0500 Subject: small changes to previous commit from Steve's cleanup --- engine/lib/sessions.php | 2 +- engine/lib/upgrades/create_upgrade.php | 2 +- engine/lib/users.php | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/engine/lib/sessions.php b/engine/lib/sessions.php index eb451f05a..3dd95aa2f 100644 --- a/engine/lib/sessions.php +++ b/engine/lib/sessions.php @@ -616,7 +616,7 @@ function _elgg_session_destroy($id) { global $sess_save_path; $sess_file = "$sess_save_path/sess_$id"; - return (@unlink($sess_file)); + return @unlink($sess_file); } return false; diff --git a/engine/lib/upgrades/create_upgrade.php b/engine/lib/upgrades/create_upgrade.php index f0fa9b594..b34f31b7e 100644 --- a/engine/lib/upgrades/create_upgrade.php +++ b/engine/lib/upgrades/create_upgrade.php @@ -128,7 +128,7 @@ function elgg_set_version_dot_php_version($version) { rewind($h); - fputs($h, $out); + fwrite($h, $out); fclose($h); return true; } diff --git a/engine/lib/users.php b/engine/lib/users.php index 7a6245261..7365a6b68 100644 --- a/engine/lib/users.php +++ b/engine/lib/users.php @@ -1201,14 +1201,13 @@ function set_last_login($user_guid) { * @param string $object_type user * @param ElggUser $object User object * - * @return bool + * @return void * @access private */ function user_create_hook_add_site_relationship($event, $object_type, $object) { global $CONFIG; add_entity_relationship($object->getGUID(), 'member_of_site', elgg_get_site_entity()->guid); - return true; } /** -- cgit v1.2.3 From 78e0ec583619cbb728cbe03df857c8219acaac7e Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Mon, 4 Feb 2013 20:55:04 -0500 Subject: simplify flow, style --- engine/classes/ElggAttributeLoader.php | 8 ++++---- engine/lib/annotations.php | 2 +- engine/lib/entities.php | 21 +++++++++++---------- engine/lib/notification.php | 2 +- engine/lib/objects.php | 2 +- engine/lib/plugins.php | 2 +- engine/lib/users.php | 4 ++-- 7 files changed, 21 insertions(+), 20 deletions(-) diff --git a/engine/classes/ElggAttributeLoader.php b/engine/classes/ElggAttributeLoader.php index 602bb8bae..2d1c1abde 100644 --- a/engine/classes/ElggAttributeLoader.php +++ b/engine/classes/ElggAttributeLoader.php @@ -148,11 +148,11 @@ class ElggAttributeLoader { if (!is_callable($this->primary_loader)) { throw new LogicException('Primary attribute loader must be callable'); } - if (!$this->requires_access_control) { + if ($this->requires_access_control) { + $fetched = (array) call_user_func($this->primary_loader, $row['guid']); + } else { $ignoring_access = elgg_set_ignore_access(); - } - $fetched = (array) call_user_func($this->primary_loader, $row['guid']); - if (!$this->requires_access_control) { + $fetched = (array) call_user_func($this->primary_loader, $row['guid']); elgg_set_ignore_access($ignoring_access); } if (!$fetched) { diff --git a/engine/lib/annotations.php b/engine/lib/annotations.php index 3d94a1d55..a95aba8f8 100644 --- a/engine/lib/annotations.php +++ b/engine/lib/annotations.php @@ -469,7 +469,7 @@ function export_annotation_plugin_hook($hook, $entity_type, $returnvalue, $param $result = elgg_get_annotations(array( 'guid' => $guid, - 'limit' => 0 + 'limit' => 0, )); if ($result) { diff --git a/engine/lib/entities.php b/engine/lib/entities.php index 0361ae195..e3535c741 100644 --- a/engine/lib/entities.php +++ b/engine/lib/entities.php @@ -1120,16 +1120,17 @@ function _elgg_fetch_entities_from_sql($sql) { // Do secondary queries and merge rows if ($lookup_types) { $dbprefix = elgg_get_config('dbprefix'); - } - foreach ($lookup_types as $type => $guids) { - $set = "(" . implode(',', $guids) . ")"; - $sql = "SELECT * FROM {$dbprefix}{$type}s_entity WHERE guid IN $set"; - $secondary_rows = get_data($sql); - if ($secondary_rows) { - foreach ($secondary_rows as $secondary_row) { - $key = $guid_to_key[$secondary_row->guid]; - // cast to arrays to merge then cast back - $rows[$key] = (object)array_merge((array)$rows[$key], (array)$secondary_row); + + foreach ($lookup_types as $type => $guids) { + $set = "(" . implode(',', $guids) . ")"; + $sql = "SELECT * FROM {$dbprefix}{$type}s_entity WHERE guid IN $set"; + $secondary_rows = get_data($sql); + if ($secondary_rows) { + foreach ($secondary_rows as $secondary_row) { + $key = $guid_to_key[$secondary_row->guid]; + // cast to arrays to merge then cast back + $rows[$key] = (object)array_merge((array)$rows[$key], (array)$secondary_row); + } } } } diff --git a/engine/lib/notification.php b/engine/lib/notification.php index 4191407fc..d72b1352a 100644 --- a/engine/lib/notification.php +++ b/engine/lib/notification.php @@ -264,7 +264,7 @@ array $params = NULL) { $to = $to->email; // From - $site = get_entity($CONFIG->site_guid); + $site = elgg_get_site_entity(); // If there's an email address, use it - but only if its not from a user. if (!($from instanceof ElggUser) && $from->email) { $from = $from->email; diff --git a/engine/lib/objects.php b/engine/lib/objects.php index 81b6b7f12..ff3cc733f 100644 --- a/engine/lib/objects.php +++ b/engine/lib/objects.php @@ -95,7 +95,7 @@ function get_object_sites($object_guid, $limit = 10, $offset = 0) { 'relationship_guid' => $object_guid, 'type' => 'site', 'limit' => $limit, - 'offset' => $offset + 'offset' => $offset, )); } diff --git a/engine/lib/plugins.php b/engine/lib/plugins.php index 973ca4026..d6f03c795 100644 --- a/engine/lib/plugins.php +++ b/engine/lib/plugins.php @@ -139,7 +139,7 @@ function elgg_generate_plugin_entities() { $index = $id_map[$plugin_id]; $plugin = $known_plugins[$index]; // was this plugin deleted and its entity disabled? - if ($plugin->enabled != 'yes') { + if (!$plugin->isEnabled()) { $plugin->enable(); $plugin->deactivate(); $plugin->setPriority('last'); diff --git a/engine/lib/users.php b/engine/lib/users.php index 7365a6b68..c4e06895d 100644 --- a/engine/lib/users.php +++ b/engine/lib/users.php @@ -1071,10 +1071,10 @@ function collections_submenu_items() { * @return bool * @access private */ -function friends_page_handler($page_elements, $handler) { +function friends_page_handler($segments, $handler) { elgg_set_context('friends'); - if (isset($page_elements[0]) && $user = get_user_by_username($page_elements[0])) { + if (isset($segments[0]) && $user = get_user_by_username($segments[0])) { elgg_set_page_owner_guid($user->getGUID()); } if (elgg_get_logged_in_user_guid() == elgg_get_page_owner_guid()) { -- cgit v1.2.3 From 269d3621531a80b13221a2f6e663efe2ad20ccaa Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Mon, 4 Feb 2013 20:59:06 -0500 Subject: remove unused code: vars, foreach keys, etc --- engine/classes/ElggEntity.php | 2 -- engine/classes/ElggFile.php | 1 - engine/classes/ElggMenuBuilder.php | 1 - engine/lib/annotations.php | 1 - engine/lib/cron.php | 2 -- engine/lib/database.php | 16 ++++++++-------- engine/lib/elgglib.php | 12 +++--------- engine/lib/export.php | 4 ---- engine/lib/filestore.php | 2 -- engine/lib/input.php | 2 -- engine/lib/languages.php | 2 -- engine/lib/metastrings.php | 3 --- engine/lib/navigation.php | 6 +++--- engine/lib/notification.php | 2 +- engine/lib/output.php | 1 - engine/lib/plugins.php | 2 -- engine/lib/relationships.php | 3 --- engine/lib/river.php | 4 ---- engine/lib/sessions.php | 6 ------ engine/lib/sites.php | 4 ---- engine/lib/upgrade.php | 3 --- engine/lib/users.php | 9 --------- 22 files changed, 15 insertions(+), 73 deletions(-) diff --git a/engine/classes/ElggEntity.php b/engine/classes/ElggEntity.php index 0cb8e5f7c..f44e73023 100644 --- a/engine/classes/ElggEntity.php +++ b/engine/classes/ElggEntity.php @@ -1735,8 +1735,6 @@ abstract class ElggEntity extends ElggData implements * @return array */ public function getTags($tag_names = NULL) { - global $CONFIG; - if ($tag_names && !is_array($tag_names)) { $tag_names = array($tag_names); } diff --git a/engine/classes/ElggFile.php b/engine/classes/ElggFile.php index 3f652ff19..532db3617 100644 --- a/engine/classes/ElggFile.php +++ b/engine/classes/ElggFile.php @@ -365,7 +365,6 @@ class ElggFile extends ElggObject { // need to get all filestore::* metadata because the rest are "parameters" that // get passed to filestore::setParameters() if ($this->guid) { - $db_prefix = elgg_get_config('dbprefix'); $options = array( 'guid' => $this->guid, 'where' => array("n.string LIKE 'filestore::%'"), diff --git a/engine/classes/ElggMenuBuilder.php b/engine/classes/ElggMenuBuilder.php index 028eef15f..6da951597 100644 --- a/engine/classes/ElggMenuBuilder.php +++ b/engine/classes/ElggMenuBuilder.php @@ -227,7 +227,6 @@ class ElggMenuBuilder { if ($children) { $stack = array_merge($stack, $children); } - $p = count($stack); } } } diff --git a/engine/lib/annotations.php b/engine/lib/annotations.php index a95aba8f8..e5566a00a 100644 --- a/engine/lib/annotations.php +++ b/engine/lib/annotations.php @@ -465,7 +465,6 @@ function export_annotation_plugin_hook($hook, $entity_type, $returnvalue, $param } $guid = (int)$params['guid']; - $name = $params['name']; $result = elgg_get_annotations(array( 'guid' => $guid, diff --git a/engine/lib/cron.php b/engine/lib/cron.php index dd83a9849..4f3d05b93 100644 --- a/engine/lib/cron.php +++ b/engine/lib/cron.php @@ -30,8 +30,6 @@ function cron_init() { * @access private */ function cron_page_handler($page) { - global $CONFIG; - if (!isset($page[0])) { forward(); } diff --git a/engine/lib/database.php b/engine/lib/database.php index 18c30cb8e..dcf50545d 100644 --- a/engine/lib/database.php +++ b/engine/lib/database.php @@ -79,7 +79,7 @@ $dbcalls = 0; */ function establish_db_link($dblinkname = "readwrite") { // Get configuration, and globalise database link - global $CONFIG, $dblink, $DB_QUERY_CACHE, $dbcalls; + global $CONFIG, $dblink, $DB_QUERY_CACHE; if ($dblinkname != "readwrite" && isset($CONFIG->db[$dblinkname])) { if (is_array($CONFIG->db[$dblinkname])) { @@ -137,7 +137,7 @@ function establish_db_link($dblinkname = "readwrite") { * @access private */ function setup_db_connections() { - global $CONFIG, $dblink; + global $CONFIG; if (!empty($CONFIG->db->split)) { establish_db_link('read'); @@ -250,7 +250,7 @@ function explain_query($query, $link) { * @access private */ function execute_query($query, $dblink) { - global $CONFIG, $dbcalls; + global $dbcalls; if ($query == NULL) { throw new DatabaseException(elgg_echo('DatabaseException:InvalidQuery')); @@ -389,7 +389,7 @@ function get_data_row($query, $callback = "") { * @access private */ function elgg_query_runner($query, $callback = null, $single = false) { - global $CONFIG, $DB_QUERY_CACHE; + global $DB_QUERY_CACHE; // Since we want to cache results of running the callback, we need to // need to namespace the query with the callback and single result request. @@ -454,7 +454,7 @@ function elgg_query_runner($query, $callback = null, $single = false) { * @access private */ function insert_data($query) { - global $CONFIG, $DB_QUERY_CACHE; + global $DB_QUERY_CACHE; elgg_log("DB query $query", 'NOTICE'); @@ -486,7 +486,7 @@ function insert_data($query) { * @access private */ function update_data($query) { - global $CONFIG, $DB_QUERY_CACHE; + global $DB_QUERY_CACHE; elgg_log("DB query $query", 'NOTICE'); @@ -517,7 +517,7 @@ function update_data($query) { * @access private */ function delete_data($query) { - global $CONFIG, $DB_QUERY_CACHE; + global $DB_QUERY_CACHE; elgg_log("DB query $query", 'NOTICE'); @@ -644,7 +644,7 @@ function run_sql_script($scriptlocation) { $statement = str_replace("prefix_", $CONFIG->dbprefix, $statement); if (!empty($statement)) { try { - $result = update_data($statement); + update_data($statement); } catch (DatabaseException $e) { $errors[] = $e->getMessage(); } diff --git a/engine/lib/elgglib.php b/engine/lib/elgglib.php index 6a2ebf97b..4cac79a22 100644 --- a/engine/lib/elgglib.php +++ b/engine/lib/elgglib.php @@ -128,8 +128,6 @@ function elgg_load_library($name) { * @throws SecurityException */ function forward($location = "", $reason = 'system') { - global $CONFIG; - if (!headers_sent()) { if ($location === REFERER) { $location = $_SERVER['HTTP_REFERER']; @@ -385,7 +383,7 @@ function elgg_load_external_file($type, $name) { $item->url = ''; $item->location = ''; - $priority = $CONFIG->externals[$type]->add($item); + $CONFIG->externals[$type]->add($item); $CONFIG->externals_map[$type][$name] = $item; } } @@ -563,7 +561,7 @@ function system_messages($message = null, $register = "success", $count = false) return sizeof($_SESSION['msg'][$register]); } else { $count = 0; - foreach ($_SESSION['msg'] as $register => $submessages) { + foreach ($_SESSION['msg'] as $submessages) { $count += sizeof($submessages); } return $count; @@ -1294,8 +1292,6 @@ function elgg_deprecated_notice($msg, $dep_version, $backtrace_level = 1) { * @return string The current page URL. */ function current_page_url() { - global $CONFIG; - $url = parse_url(elgg_get_site_url()); $page = $url['scheme'] . "://"; @@ -1489,8 +1485,6 @@ function elgg_http_add_url_query_elements($url, array $elements) { * @since 1.8.0 */ function elgg_http_url_is_identical($url1, $url2, $ignore_params = array('offset', 'limit')) { - global $CONFIG; - // if the server portion is missing but it starts with / then add the url in. // @todo use elgg_normalize_url() if (elgg_substr($url1, 0, 1) == '/') { @@ -1629,7 +1623,7 @@ $sort_type = SORT_LOCALE_STRING) { $sort = array(); - foreach ($array as $k => $v) { + foreach ($array as $v) { if (isset($v[$element])) { $sort[] = strtolower($v[$element]); } else { diff --git a/engine/lib/export.php b/engine/lib/export.php index 1996bc885..ecc894e63 100644 --- a/engine/lib/export.php +++ b/engine/lib/export.php @@ -40,8 +40,6 @@ function get_uuid_from_object($object) { * @return string */ function guid_to_uuid($guid) { - global $CONFIG; - return elgg_get_site_url() . "export/opendd/$guid/"; } @@ -53,8 +51,6 @@ function guid_to_uuid($guid) { * @return bool */ function is_uuid_this_domain($uuid) { - global $CONFIG; - if (strpos($uuid, elgg_get_site_url()) === 0) { return true; } diff --git a/engine/lib/filestore.php b/engine/lib/filestore.php index 677fac931..a3c7ba439 100644 --- a/engine/lib/filestore.php +++ b/engine/lib/filestore.php @@ -308,8 +308,6 @@ function get_image_resize_parameters($width, $height, $options) { function file_delete($guid) { if ($file = get_entity($guid)) { if ($file->canEdit()) { - $container = get_entity($file->container_guid); - $thumbnail = $file->thumbnail; $smallthumb = $file->smallthumb; $largethumb = $file->largethumb; diff --git a/engine/lib/input.php b/engine/lib/input.php index 2d71fb77e..bbb9ff94d 100644 --- a/engine/lib/input.php +++ b/engine/lib/input.php @@ -266,10 +266,8 @@ function input_livesearch_page_handler($page) { } if (get_input('match_owner', false)) { - $owner_guid = $user->getGUID(); $owner_where = 'AND e.owner_guid = ' . $user->getGUID(); } else { - $owner_guid = null; $owner_where = ''; } diff --git a/engine/lib/languages.php b/engine/lib/languages.php index 11722f744..3c231d964 100644 --- a/engine/lib/languages.php +++ b/engine/lib/languages.php @@ -104,8 +104,6 @@ function add_translation($country_code, $language_array) { * @return string The language code for the site/user or "en" if not set */ function get_current_language() { - global $CONFIG; - $language = get_language(); if (!$language) { diff --git a/engine/lib/metastrings.php b/engine/lib/metastrings.php index 264d2ba2c..698c8c1a9 100644 --- a/engine/lib/metastrings.php +++ b/engine/lib/metastrings.php @@ -520,9 +520,6 @@ function elgg_get_metastring_sql($table, $names = null, $values = null, $db_prefix = elgg_get_config('dbprefix'); - // join counter for incremental joins. - $i = 1; - // binary forces byte-to-byte comparision of strings, making // it case- and diacritical-mark- sensitive. // only supported on values. diff --git a/engine/lib/navigation.php b/engine/lib/navigation.php index 0e8914bbb..118a7214c 100644 --- a/engine/lib/navigation.php +++ b/engine/lib/navigation.php @@ -152,7 +152,7 @@ function elgg_is_menu_item_registered($menu_name, $item_name) { return false; } - foreach ($CONFIG->menus[$menu_name] as $index => $menu_object) { + foreach ($CONFIG->menus[$menu_name] as $menu_object) { /* @var ElggMenuItem $menu_object */ if ($menu_object->getName() == $item_name) { return true; @@ -313,8 +313,8 @@ function elgg_site_menu_setup($hook, $type, $return, $params) { // check if we have anything selected $selected = false; - foreach ($return as $section_name => $section) { - foreach ($section as $key => $item) { + foreach ($return as $section) { + foreach ($section as $item) { if ($item->getSelected()) { $selected = true; break 2; diff --git a/engine/lib/notification.php b/engine/lib/notification.php index d72b1352a..f0aff84e6 100644 --- a/engine/lib/notification.php +++ b/engine/lib/notification.php @@ -86,7 +86,7 @@ function unregister_notification_handler($method) { * @throws NotificationException */ function notify_user($to, $from, $subject, $message, array $params = NULL, $methods_override = "") { - global $NOTIFICATION_HANDLERS, $CONFIG; + global $NOTIFICATION_HANDLERS; // Sanitise if (!is_array($to)) { diff --git a/engine/lib/output.php b/engine/lib/output.php index fefb94b3e..da8e1ab86 100644 --- a/engine/lib/output.php +++ b/engine/lib/output.php @@ -224,7 +224,6 @@ function elgg_normalize_url($url) { $php_5_3_0_to_5_3_2 = version_compare(PHP_VERSION, '5.3.0', '>=') && version_compare(PHP_VERSION, '5.3.3', '<'); - $validated = false; if ($php_5_2_13_and_below || $php_5_3_0_to_5_3_2) { $tmp_address = str_replace("-", "", $url); $validated = filter_var($tmp_address, FILTER_VALIDATE_URL); diff --git a/engine/lib/plugins.php b/engine/lib/plugins.php index d6f03c795..0ea4404f3 100644 --- a/engine/lib/plugins.php +++ b/engine/lib/plugins.php @@ -309,8 +309,6 @@ function elgg_is_active_plugin($plugin_id, $site_guid = null) { * @access private */ function elgg_load_plugins() { - global $CONFIG; - $plugins_path = elgg_get_plugins_path(); $start_flags = ELGG_PLUGIN_INCLUDE_START | ELGG_PLUGIN_REGISTER_VIEWS diff --git a/engine/lib/relationships.php b/engine/lib/relationships.php index 6f18bda93..c1a7cc080 100644 --- a/engine/lib/relationships.php +++ b/engine/lib/relationships.php @@ -589,8 +589,6 @@ function import_relationship_plugin_hook($hook, $entity_type, $returnvalue, $par * @access private */ function export_relationship_plugin_hook($hook, $entity_type, $returnvalue, $params) { - global $CONFIG; - // Sanity check values if ((!is_array($params)) && (!isset($params['guid']))) { throw new InvalidParameterException(elgg_echo('InvalidParameterException:GUIDNotForExport')); @@ -626,7 +624,6 @@ function export_relationship_plugin_hook($hook, $entity_type, $returnvalue, $par function relationship_notification_hook($event, $type, $object) { /* @var ElggRelationship $object */ $user_one = get_entity($object->guid_one); - $user_two = get_entity($object->guid_two); /* @var ElggUser $user_one */ return notify_user($object->guid_two, diff --git a/engine/lib/river.php b/engine/lib/river.php index 6274887b5..f2ec1e101 100644 --- a/engine/lib/river.php +++ b/engine/lib/river.php @@ -664,10 +664,6 @@ function elgg_river_page_handler($page) { } set_input('page_type', $page_type); - // content filter code here - $entity_type = ''; - $entity_subtype = ''; - require_once("{$CONFIG->path}pages/river.php"); return true; } diff --git a/engine/lib/sessions.php b/engine/lib/sessions.php index 3dd95aa2f..a34c2045b 100644 --- a/engine/lib/sessions.php +++ b/engine/lib/sessions.php @@ -286,8 +286,6 @@ function check_rate_limit_exceeded($user_guid) { * @throws LoginException */ function login(ElggUser $user, $persistent = false) { - global $CONFIG; - // User is banned, return false. if ($user->isBanned()) { throw new LoginException(elgg_echo('LoginException:BannedUser')); @@ -334,8 +332,6 @@ function login(ElggUser $user, $persistent = false) { * @return bool */ function logout() { - global $CONFIG; - if (isset($_SESSION['user'])) { if (!elgg_trigger_event('logout', 'user', $_SESSION['user'])) { return false; @@ -618,8 +614,6 @@ function _elgg_session_destroy($id) { $sess_file = "$sess_save_path/sess_$id"; return @unlink($sess_file); } - - return false; } /** diff --git a/engine/lib/sites.php b/engine/lib/sites.php index 8ee3213d3..3de0eccc2 100644 --- a/engine/lib/sites.php +++ b/engine/lib/sites.php @@ -118,8 +118,6 @@ function create_site_entity($guid, $name, $description, $url) { * @return bool */ function add_site_user($site_guid, $user_guid) { - global $CONFIG; - $site_guid = (int)$site_guid; $user_guid = (int)$user_guid; @@ -150,8 +148,6 @@ function remove_site_user($site_guid, $user_guid) { * @return mixed */ function add_site_object($site_guid, $object_guid) { - global $CONFIG; - $site_guid = (int)$site_guid; $object_guid = (int)$object_guid; diff --git a/engine/lib/upgrade.php b/engine/lib/upgrade.php index ae4591b0b..2883dc509 100644 --- a/engine/lib/upgrade.php +++ b/engine/lib/upgrade.php @@ -17,8 +17,6 @@ * @access private */ function upgrade_code($version, $quiet = FALSE) { - global $CONFIG; - $version = (int) $version; $upgrade_path = elgg_get_config('path') . 'engine/lib/upgrades/'; $processed_upgrades = elgg_get_processed_upgrades(); @@ -291,7 +289,6 @@ function elgg_upgrade_bootstrap_17_to_18() { '2011010101.php', ); - $upgrades_17 = array(); $upgrade_files = elgg_get_upgrade_files(); $processed_upgrades = array(); diff --git a/engine/lib/users.php b/engine/lib/users.php index c4e06895d..8e9086f57 100644 --- a/engine/lib/users.php +++ b/engine/lib/users.php @@ -343,8 +343,6 @@ function user_add_friend($user_guid, $friend_guid) { * @return bool Depending on success */ function user_remove_friend($user_guid, $friend_guid) { - global $CONFIG; - $user_guid = (int) $user_guid; $friend_guid = (int) $friend_guid; @@ -675,8 +673,6 @@ function find_active_users($seconds = 600, $limit = 10, $offset = 0, $count = fa * @return bool */ function send_new_password_request($user_guid) { - global $CONFIG; - $user_guid = (int)$user_guid; $user = get_entity($user_guid); @@ -916,9 +912,6 @@ function validate_email_address($address) { function register_user($username, $password, $name, $email, $allow_multiple_emails = false, $friend_guid = 0, $invitecode = '') { - // Load the configuration - global $CONFIG; - // no need to trim password. $username = trim($username); $name = trim(strip_tags($name)); @@ -1205,8 +1198,6 @@ function set_last_login($user_guid) { * @access private */ function user_create_hook_add_site_relationship($event, $object_type, $object) { - global $CONFIG; - add_entity_relationship($object->getGUID(), 'member_of_site', elgg_get_site_entity()->guid); } -- cgit v1.2.3 From 359571fec63b62a666413f507775aeb17cd55f37 Mon Sep 17 00:00:00 2001 From: Cash Costello Date: Sat, 9 Feb 2013 08:43:52 -0500 Subject: Fixes #5052 using name for annotations and using correct class for export --- engine/classes/ElggExtender.php | 2 +- engine/lib/annotations.php | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/engine/classes/ElggExtender.php b/engine/classes/ElggExtender.php index d94bad837..25aba354f 100644 --- a/engine/classes/ElggExtender.php +++ b/engine/classes/ElggExtender.php @@ -171,7 +171,7 @@ abstract class ElggExtender extends ElggData { public function export() { $uuid = get_uuid_from_object($this); - $meta = new ODDMetadata($uuid, guid_to_uuid($this->entity_guid), $this->attributes['name'], + $meta = new ODDMetaData($uuid, guid_to_uuid($this->entity_guid), $this->attributes['name'], $this->attributes['value'], $this->attributes['type'], guid_to_uuid($this->owner_guid)); $meta->setAttribute('published', date("r", $this->time_created)); diff --git a/engine/lib/annotations.php b/engine/lib/annotations.php index e5566a00a..41a736aa1 100644 --- a/engine/lib/annotations.php +++ b/engine/lib/annotations.php @@ -441,20 +441,20 @@ function elgg_list_entities_from_annotation_calculation($options) { } /** - * Handler called by trigger_plugin_hook on the "export" event. + * Export the annotations for the specified entity * * @param string $hook 'export' - * @param string $entity_type 'all' + * @param string $type 'all' * @param mixed $returnvalue Default return value - * @param mixed $params List of params to export + * @param mixed $params Parameters determining what annotations to export * * @elgg_plugin_hook export all * - * @return mixed + * @return array * @throws InvalidParameterException * @access private */ -function export_annotation_plugin_hook($hook, $entity_type, $returnvalue, $params) { +function export_annotation_plugin_hook($hook, $type, $returnvalue, $params) { // Sanity check values if ((!is_array($params)) && (!isset($params['guid']))) { throw new InvalidParameterException(elgg_echo('InvalidParameterException:GUIDNotForExport')); @@ -465,11 +465,12 @@ function export_annotation_plugin_hook($hook, $entity_type, $returnvalue, $param } $guid = (int)$params['guid']; + $options = array('guid' => $guid, 'limit' => 0); + if (isset($params['name'])) { + $options['annotation_name'] = $params['name']; + } - $result = elgg_get_annotations(array( - 'guid' => $guid, - 'limit' => 0, - )); + $result = elgg_get_annotations($options); if ($result) { foreach ($result as $r) { -- cgit v1.2.3 From b78bbc030f432276516d693de7cddcea05eff807 Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Mon, 4 Feb 2013 21:01:57 -0500 Subject: Check for ElggUser in functions that pass in user's GUID --- engine/lib/users.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/lib/users.php b/engine/lib/users.php index 8e9086f57..ffff4aaa5 100644 --- a/engine/lib/users.php +++ b/engine/lib/users.php @@ -676,7 +676,7 @@ function send_new_password_request($user_guid) { $user_guid = (int)$user_guid; $user = get_entity($user_guid); - if ($user) { + if ($user instanceof ElggUser) { /* @var ElggUser $user */ // generate code @@ -710,9 +710,9 @@ function force_user_password_reset($user_guid, $password) { global $CONFIG; $user = get_entity($user_guid); + if ($user instanceof ElggUser) { /* @var ElggUser $user */ - if ($user) { $salt = generate_random_cleartext_password(); // Reset the salt $user->salt = $salt; @@ -740,7 +740,7 @@ function execute_new_password_request($user_guid, $conf_code) { $user_guid = (int)$user_guid; $user = get_entity($user_guid); - if ($user) { + if ($user instanceof ElggUser) { /* @var ElggUser $user */ $saved_code = $user->getPrivateSetting('passwd_conf_code'); -- cgit v1.2.3 From 54e9ea8423085ac50f0955480b1671295e851eef Mon Sep 17 00:00:00 2001 From: cash Date: Sat, 9 Feb 2013 09:31:31 -0500 Subject: removed unnecessary @var since we have instanceof test --- engine/lib/users.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/engine/lib/users.php b/engine/lib/users.php index ffff4aaa5..19f4e434d 100644 --- a/engine/lib/users.php +++ b/engine/lib/users.php @@ -677,8 +677,6 @@ function send_new_password_request($user_guid) { $user = get_entity($user_guid); if ($user instanceof ElggUser) { - /* @var ElggUser $user */ - // generate code $code = generate_random_cleartext_password(); $user->setPrivateSetting('passwd_conf_code', $code); @@ -711,8 +709,6 @@ function force_user_password_reset($user_guid, $password) { $user = get_entity($user_guid); if ($user instanceof ElggUser) { - /* @var ElggUser $user */ - $salt = generate_random_cleartext_password(); // Reset the salt $user->salt = $salt; @@ -741,7 +737,6 @@ function execute_new_password_request($user_guid, $conf_code) { $user = get_entity($user_guid); if ($user instanceof ElggUser) { - /* @var ElggUser $user */ $saved_code = $user->getPrivateSetting('passwd_conf_code'); if ($saved_code && $saved_code == $conf_code) { -- cgit v1.2.3 From 17d6bd620d5b4712ca01f5318170384804004a9e Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Mon, 4 Feb 2013 21:03:03 -0500 Subject: Normalized escaping, escape $user->location just in case --- engine/lib/users.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/engine/lib/users.php b/engine/lib/users.php index 19f4e434d..4a585c07f 100644 --- a/engine/lib/users.php +++ b/engine/lib/users.php @@ -835,7 +835,7 @@ function validate_username($username) { for ($n = 0; $n < strlen($blacklist2); $n++) { if (strpos($username, $blacklist2[$n]) !== false) { $msg = elgg_echo('registration:invalidchars', array($blacklist2[$n], $blacklist2)); - $msg = htmlentities($msg, ENT_COMPAT, 'UTF-8'); + $msg = htmlspecialchars($msg, ENT_QUOTES, 'UTF-8'); throw new RegistrationException($msg); } } @@ -1331,9 +1331,10 @@ function elgg_users_setup_entity_menu($hook, $type, $return, $params) { } else { $return = array(); if (isset($entity->location)) { + $location = htmlspecialchars($entity->location, ENT_QUOTES, 'UTF-8', false); $options = array( 'name' => 'location', - 'text' => "$entity->location", + 'text' => "$location", 'href' => false, 'priority' => 150, ); -- cgit v1.2.3 From 3a644351f6bd26fc35b6905a30bbb52e89916078 Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Mon, 4 Feb 2013 21:07:19 -0500 Subject: Remove unnecessary get_data() callback --- engine/lib/metastrings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/lib/metastrings.php b/engine/lib/metastrings.php index 698c8c1a9..7e18597a6 100644 --- a/engine/lib/metastrings.php +++ b/engine/lib/metastrings.php @@ -67,7 +67,7 @@ function get_metastring_id($string, $case_sensitive = TRUE) { } $row = FALSE; - $metaStrings = get_data($query, "entity_row_to_elggstar"); + $metaStrings = get_data($query); if (is_array($metaStrings)) { if (sizeof($metaStrings) > 1) { $ids = array(); -- cgit v1.2.3 From 011485988a8fa734c2ed74fec21031b3beebbcfd Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Mon, 4 Feb 2013 21:09:02 -0500 Subject: Lots of new @todos :( --- engine/classes/ElggDiskFilestore.php | 1 + engine/classes/ElggFile.php | 3 +++ engine/classes/ElggGroup.php | 3 +++ engine/classes/ElggMenuBuilder.php | 2 ++ engine/classes/ElggPlugin.php | 2 ++ engine/lib/annotations.php | 1 + engine/lib/calendar.php | 2 ++ engine/lib/database.php | 2 ++ engine/lib/entities.php | 3 +++ engine/lib/input.php | 5 +++++ engine/lib/metastrings.php | 1 + engine/lib/notification.php | 3 ++- engine/lib/plugins.php | 2 ++ engine/lib/system_log.php | 2 ++ 14 files changed, 31 insertions(+), 1 deletion(-) diff --git a/engine/classes/ElggDiskFilestore.php b/engine/classes/ElggDiskFilestore.php index 2d86e7c2d..7374aad35 100644 --- a/engine/classes/ElggDiskFilestore.php +++ b/engine/classes/ElggDiskFilestore.php @@ -60,6 +60,7 @@ class ElggDiskFilestore extends ElggFilestore { $path = substr($fullname, 0, $ls); $name = substr($fullname, $ls); + // @todo $name is unused, remove it or do we need to fix something? // Try and create the directory try { diff --git a/engine/classes/ElggFile.php b/engine/classes/ElggFile.php index 532db3617..3e9c24c17 100644 --- a/engine/classes/ElggFile.php +++ b/engine/classes/ElggFile.php @@ -93,6 +93,7 @@ class ElggFile extends ElggObject { $container_guid = $this->container_guid; } $fs = $this->getFilestore(); + // @todo add getSize() to ElggFilestore return $fs->getSize($prefix, $container_guid); } @@ -289,6 +290,7 @@ class ElggFile extends ElggObject { public function seek($position) { $fs = $this->getFilestore(); + // @todo add seek() to ElggFilestore return $fs->seek($this->handle, $position); } @@ -393,6 +395,7 @@ class ElggFile extends ElggObject { $this->filestore = new $filestore(); $this->filestore->setParameters($parameters); + // @todo explain why $parameters will always be set here (PhpStorm complains) } // this means the entity hasn't been saved so fallback to default diff --git a/engine/classes/ElggGroup.php b/engine/classes/ElggGroup.php index 24bdee79e..61f699f1a 100644 --- a/engine/classes/ElggGroup.php +++ b/engine/classes/ElggGroup.php @@ -220,6 +220,7 @@ class ElggGroup extends ElggEntity * @return array|false */ public function getObjects($subtype = "", $limit = 10, $offset = 0) { + // @todo are we deprecating this method, too? return get_objects_in_group($this->getGUID(), $subtype, 0, 0, "", $limit, $offset, false); } @@ -233,6 +234,7 @@ class ElggGroup extends ElggEntity * @return array|false */ public function getFriendsObjects($subtype = "", $limit = 10, $offset = 0) { + // @todo are we deprecating this method, too? return get_objects_in_group($this->getGUID(), $subtype, 0, 0, "", $limit, $offset, false); } @@ -244,6 +246,7 @@ class ElggGroup extends ElggEntity * @return array|false */ public function countObjects($subtype = "") { + // @todo are we deprecating this method, too? return get_objects_in_group($this->getGUID(), $subtype, 0, 0, "", 10, 0, true); } diff --git a/engine/classes/ElggMenuBuilder.php b/engine/classes/ElggMenuBuilder.php index 6da951597..0dce02be4 100644 --- a/engine/classes/ElggMenuBuilder.php +++ b/engine/classes/ElggMenuBuilder.php @@ -274,6 +274,8 @@ class ElggMenuBuilder { * @param ElggMenuItem $a * @param ElggMenuItem $b * @return bool + * + * @todo deprecate this? weight seems to be deprecated */ public static function compareByWeight($a, $b) { $aw = $a->getWeight(); diff --git a/engine/classes/ElggPlugin.php b/engine/classes/ElggPlugin.php index 066fd9a79..ae447bddb 100644 --- a/engine/classes/ElggPlugin.php +++ b/engine/classes/ElggPlugin.php @@ -597,6 +597,8 @@ class ElggPlugin extends ElggObject { * Checks if this plugin can be activated on the current * Elgg installation. * + * @todo remove $site_guid param or implement it + * * @param mixed $site_guid Optional site guid * @return bool */ diff --git a/engine/lib/annotations.php b/engine/lib/annotations.php index 41a736aa1..f40a2cc6f 100644 --- a/engine/lib/annotations.php +++ b/engine/lib/annotations.php @@ -17,6 +17,7 @@ */ function row_to_elggannotation($row) { if (!($row instanceof stdClass)) { + // @todo should throw in this case? return $row; } diff --git a/engine/lib/calendar.php b/engine/lib/calendar.php index 9a06c5292..e6f95934c 100644 --- a/engine/lib/calendar.php +++ b/engine/lib/calendar.php @@ -39,6 +39,8 @@ function get_day_end($day = null, $month = null, $year = null) { /** * Return the notable entities for a given time period. * + * @todo this function also accepts an array(type => subtypes) for 3rd arg. Should we document this? + * * @param int $start_time The start time as a unix timestamp. * @param int $end_time The end time as a unix timestamp. * @param string $type The type of entity (eg "user", "object" etc) diff --git a/engine/lib/database.php b/engine/lib/database.php index dcf50545d..2b348366d 100644 --- a/engine/lib/database.php +++ b/engine/lib/database.php @@ -123,6 +123,8 @@ function establish_db_link($dblinkname = "readwrite") { // Set up cache if global not initialized and query cache not turned off if ((!$DB_QUERY_CACHE) && (!$db_cache_off)) { + // @todo everywhere else this is assigned to array(), making it dangerous to call + // object methods on this. We should consider making this an plain array $DB_QUERY_CACHE = new ElggStaticVariableCache('db_query_cache'); } } diff --git a/engine/lib/entities.php b/engine/lib/entities.php index e3535c741..25c927ac6 100644 --- a/engine/lib/entities.php +++ b/engine/lib/entities.php @@ -1220,6 +1220,7 @@ function elgg_get_entity_type_subtype_where_sql($table, $types, $subtypes, $pair if ($subtypes) { foreach ($subtypes as $subtype) { // check that the subtype is valid (with ELGG_ENTITIES_NO_VALUE being a valid subtype) + // @todo simplify this logic if (ELGG_ENTITIES_NO_VALUE === $subtype || $subtype_id = get_subtype_id($type, $subtype)) { $subtype_ids[] = (ELGG_ENTITIES_NO_VALUE === $subtype) ? ELGG_ENTITIES_NO_VALUE : $subtype_id; } else { @@ -1461,6 +1462,8 @@ function elgg_list_entities(array $options = array(), $getter = 'elgg_get_entiti * * @tip Use this to generate a list of archives by month for when entities were added or updated. * + * @todo document how to pass in array for $subtype + * * @warning Months are returned in the form YYYYMM. * * @param string $type The type of entity diff --git a/engine/lib/input.php b/engine/lib/input.php index bbb9ff94d..2d9bae4dd 100644 --- a/engine/lib/input.php +++ b/engine/lib/input.php @@ -226,6 +226,8 @@ function elgg_clear_sticky_value($form_name, $variable) { /** * Page handler for autocomplete endpoint. * + * @todo split this into functions/objects, this is way too big + * * /livesearch?q= * * Other options include: @@ -288,6 +290,7 @@ function input_livesearch_page_handler($page) { if ($entities = get_data($query)) { foreach ($entities as $entity) { + // @todo use elgg_get_entities (don't query in a loop!) $entity = get_entity($entity->guid); /* @var ElggUser $entity */ if (!$entity) { @@ -338,6 +341,7 @@ function input_livesearch_page_handler($page) { "; if ($entities = get_data($query)) { foreach ($entities as $entity) { + // @todo use elgg_get_entities (don't query in a loop!) $entity = get_entity($entity->guid); /* @var ElggGroup $entity */ if (!$entity) { @@ -386,6 +390,7 @@ function input_livesearch_page_handler($page) { if ($entities = get_data($query)) { foreach ($entities as $entity) { + // @todo use elgg_get_entities (don't query in a loop!) $entity = get_entity($entity->guid); /* @var ElggUser $entity */ if (!$entity) { diff --git a/engine/lib/metastrings.php b/engine/lib/metastrings.php index 7e18597a6..76c4bd8c4 100644 --- a/engine/lib/metastrings.php +++ b/engine/lib/metastrings.php @@ -804,6 +804,7 @@ function elgg_delete_metastring_based_object_by_id($id, $type) { } if ($metabyname_memcache) { + // @todo why name_id? is that even populated? $metabyname_memcache->delete("{$obj->entity_guid}:{$obj->name_id}"); } } diff --git a/engine/lib/notification.php b/engine/lib/notification.php index f0aff84e6..56e591192 100644 --- a/engine/lib/notification.php +++ b/engine/lib/notification.php @@ -174,7 +174,8 @@ function get_user_notification_settings($user_guid = 0) { $user_guid = elgg_get_logged_in_user_guid(); } - // @todo: holy crap, really? + // @todo: there should be a better way now that metadata is cached. E.g. just query for MD names, then + // query user object directly $all_metadata = elgg_get_metadata(array( 'guid' => $user_guid, 'limit' => 0 diff --git a/engine/lib/plugins.php b/engine/lib/plugins.php index 0ea4404f3..f281b1416 100644 --- a/engine/lib/plugins.php +++ b/engine/lib/plugins.php @@ -91,7 +91,9 @@ function elgg_get_plugin_ids_in_dir($dir = null) { * @access private */ function elgg_generate_plugin_entities() { + // @todo $site unused, can remove? $site = get_config('site'); + $dir = elgg_get_plugins_path(); $db_prefix = elgg_get_config('dbprefix'); diff --git a/engine/lib/system_log.php b/engine/lib/system_log.php index 38bc4ba96..5a153afb2 100644 --- a/engine/lib/system_log.php +++ b/engine/lib/system_log.php @@ -10,6 +10,8 @@ /** * Retrieve the system log based on a number of parameters. * + * @todo too many args, and the first arg is too confusing + * * @param int|array $by_user The guid(s) of the user(s) who initiated the event. * Use 0 for unowned entries. Anything else falsey means anyone. * @param string $event The event you are searching on. -- cgit v1.2.3 From 3a08f0d42e2b5167266eac2a8daf9a3c22b404b0 Mon Sep 17 00:00:00 2001 From: cash Date: Sat, 9 Feb 2013 10:32:05 -0500 Subject: updated todo comment on compareByWeight --- engine/classes/ElggMenuBuilder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/classes/ElggMenuBuilder.php b/engine/classes/ElggMenuBuilder.php index 0dce02be4..639e34755 100644 --- a/engine/classes/ElggMenuBuilder.php +++ b/engine/classes/ElggMenuBuilder.php @@ -275,7 +275,7 @@ class ElggMenuBuilder { * @param ElggMenuItem $b * @return bool * - * @todo deprecate this? weight seems to be deprecated + * @todo change name to compareByPriority */ public static function compareByWeight($a, $b) { $aw = $a->getWeight(); -- cgit v1.2.3 From a9a7c39540e196a4673775b6ec277a54b8436552 Mon Sep 17 00:00:00 2001 From: cash Date: Sat, 9 Feb 2013 12:10:19 -0500 Subject: cleaned up some coding standard issues --- engine/classes/ElggBatch.php | 3 ++- engine/classes/ElggPriorityList.php | 23 +++++++++++++---------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/engine/classes/ElggBatch.php b/engine/classes/ElggBatch.php index c1a77a0d9..5d59425d0 100644 --- a/engine/classes/ElggBatch.php +++ b/engine/classes/ElggBatch.php @@ -296,7 +296,8 @@ class ElggBatch * Increment the offset from the original options array? Setting to * false is required for callbacks that delete rows. * - * @param bool $increment + * @param bool $increment Set to false when deleting data + * @return void */ public function setIncrementOffset($increment = true) { $this->incrementOffset = (bool) $increment; diff --git a/engine/classes/ElggPriorityList.php b/engine/classes/ElggPriorityList.php index 8a3b836a8..b5f8fe163 100644 --- a/engine/classes/ElggPriorityList.php +++ b/engine/classes/ElggPriorityList.php @@ -89,7 +89,7 @@ * return true; * } * - * @package Elgg.Core + * @package Elgg.Core * @subpackage Helpers */ class ElggPriorityList @@ -126,7 +126,9 @@ class ElggPriorityList * maintains its priority and the new element is to the next available * slot, taking into consideration all previously registered elements. * Negative elements are accepted. + * @param bool $exact unused * @return int The priority of the added element. + * @todo remove $exact or implement it. Note we use variable name strict below. */ public function add($element, $priority = null, $exact = false) { if ($priority !== null && !is_numeric($priority)) { @@ -146,7 +148,8 @@ class ElggPriorityList * @warning The element must have the same attributes / values. If using $strict, it must have * the same types. array(10) will fail in strict against array('10') (str vs int). * - * @param type $element + * @param mixed $element The element to remove from the list + * @param bool $strict Whether to check the type of the element match * @return bool */ public function remove($element, $strict = false) { @@ -162,10 +165,10 @@ class ElggPriorityList /** * Move an existing element to a new priority. * - * @param mixed $current_priority - * @param int $new_priority - * - * @return int The new priority. + * @param mixed $element The element to move + * @param int $new_priority The new priority for the element + * @param bool $strict Whether to check the type of the element match + * @return bool */ public function move($element, $new_priority, $strict = false) { $new_priority = (int) $new_priority; @@ -200,12 +203,12 @@ class ElggPriorityList * * If no user function is provided the elements are sorted by priority registered. * - * The callback function should accept the array of elements as the first argument and should - * return a sorted array. + * The callback function should accept the array of elements as the first + * argument and should return a sorted array. * * This function can be called multiple times. * - * @param type $callback + * @param callback $callback The callback for sorting. Numeric sorting is the default. * @return bool */ public function sort($callback = null) { @@ -268,7 +271,7 @@ class ElggPriorityList /** * Returns the element at $priority. * - * @param int $priority + * @param int $priority The priority * @return mixed The element or false on fail. */ public function getElement($priority) { -- cgit v1.2.3 From e7eb5590470fe5e78c527f5d69d8621d7223943d Mon Sep 17 00:00:00 2001 From: Matt Beckett Date: Tue, 12 Feb 2013 23:56:50 -0700 Subject: fixes #5067 - elgg_get_page_owner_guid() can bypass all access for the rest of the pageload --- engine/lib/pageowner.php | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/lib/pageowner.php b/engine/lib/pageowner.php index bf5901aad..7e8e6e430 100644 --- a/engine/lib/pageowner.php +++ b/engine/lib/pageowner.php @@ -113,6 +113,7 @@ function default_page_owner_handler($hook, $entity_type, $returnvalue, $params) } if ($user = get_user_by_username($username)) { + elgg_set_ignore_access($ia); return $user->getGUID(); } } -- cgit v1.2.3 From d27d5859a67396f84ec2087136f266a0530bd7c4 Mon Sep 17 00:00:00 2001 From: cash Date: Mon, 18 Feb 2013 09:29:29 -0500 Subject: Fixes #5068 removed invalid menu item creation --- mod/groups/views/default/groups/sidebar/my_status.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/groups/views/default/groups/sidebar/my_status.php b/mod/groups/views/default/groups/sidebar/my_status.php index 4c36c0213..5951cbd28 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; -- cgit v1.2.3 From 46b07a30cecbbf5a3c6cee66d7573170272f6f03 Mon Sep 17 00:00:00 2001 From: cash Date: Mon, 18 Feb 2013 09:50:34 -0500 Subject: Closes #5053 decodes html entities for email subjects --- engine/lib/notification.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/engine/lib/notification.php b/engine/lib/notification.php index 56e591192..b6399b3c6 100644 --- a/engine/lib/notification.php +++ b/engine/lib/notification.php @@ -347,6 +347,8 @@ function elgg_send_email($from, $to, $subject, $body, array $params = NULL) { // Sanitise subject by stripping line endings $subject = preg_replace("/(\r\n|\r|\n)/", " ", $subject); + // this is because Elgg encodes everything and matches what is done with body + $subject = html_entity_decode($subject, ENT_COMPAT, 'UTF-8'); // Decode any html entities if (is_callable('mb_encode_mimeheader')) { $subject = mb_encode_mimeheader($subject, "UTF-8", "B"); } -- cgit v1.2.3 From 04cf742eaa4cedeadca9e46cc747e34899ed263c Mon Sep 17 00:00:00 2001 From: cash Date: Mon, 18 Feb 2013 10:27:43 -0500 Subject: Fixes #5057 sets width for IE7 --- views/default/css/ie7.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/views/default/css/ie7.php b/views/default/css/ie7.php index db0125b4b..5ef90a90b 100644 --- a/views/default/css/ie7.php +++ b/views/default/css/ie7.php @@ -62,4 +62,8 @@ .elgg-module-walledgarden > .elgg-head, .elgg-module-walledgarden > .elgg-foot { width: 530px; -} \ No newline at end of file +} + +input, textarea { + width: 98%; +} -- cgit v1.2.3 From 2cc3a9e7d7721b0051bf261ca922972075b72d3c Mon Sep 17 00:00:00 2001 From: cash Date: Mon, 18 Feb 2013 10:34:31 -0500 Subject: Fixes #5056 fixes tag wrapping on IE7 --- views/default/css/ie7.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/views/default/css/ie7.php b/views/default/css/ie7.php index 5ef90a90b..229df8431 100644 --- a/views/default/css/ie7.php +++ b/views/default/css/ie7.php @@ -67,3 +67,8 @@ input, textarea { width: 98%; } + +.elgg-tag a { + /* IE7 had a weird wrapping issue for tags */ + word-wrap: normal; +} -- cgit v1.2.3 From 1d11e783557ed5b1fe5ca73d2ad01d1bfc53dbfa Mon Sep 17 00:00:00 2001 From: cash Date: Tue, 19 Feb 2013 10:46:23 -0500 Subject: Refs #4970 prevent Firefox from adding files to TinyMCE editor --- mod/tinymce/views/default/js/tinymce.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mod/tinymce/views/default/js/tinymce.php b/mod/tinymce/views/default/js/tinymce.php index b4db43cee..344d71b14 100644 --- a/mod/tinymce/views/default/js/tinymce.php +++ b/mod/tinymce/views/default/js/tinymce.php @@ -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' }); -- cgit v1.2.3 From 4cd91acc35bd1923b9c2bfd5c377c0ac2e157d23 Mon Sep 17 00:00:00 2001 From: iionly Date: Tue, 19 Feb 2013 20:15:34 +0100 Subject: Tinymce plugin: update to version 3.5.8 of Tinymce editor --- mod/tinymce/vendor/tinymce/changelog.txt | 1414 +- mod/tinymce/vendor/tinymce/examples/full.html | 4 +- .../vendor/tinymce/examples/lists/media_list.js | 2 +- .../vendor/tinymce/jscripts/tiny_mce/langs/en.js | 2 +- .../jscripts/tiny_mce/plugins/advimage/js/image.js | 8 +- .../tiny_mce/plugins/advlink/js/advlink.js | 21 +- .../tiny_mce/plugins/autolink/editor_plugin.js | 2 +- .../tiny_mce/plugins/autolink/editor_plugin_src.js | 36 +- .../tiny_mce/plugins/autoresize/editor_plugin.js | 2 +- .../plugins/autoresize/editor_plugin_src.js | 58 +- .../tiny_mce/plugins/autosave/editor_plugin.js | 2 +- .../tiny_mce/plugins/autosave/editor_plugin_src.js | 6 +- .../jscripts/tiny_mce/plugins/autosave/langs/en.js | 4 - .../tiny_mce/plugins/contextmenu/editor_plugin.js | 2 +- .../plugins/contextmenu/editor_plugin_src.js | 17 +- .../plugins/directionality/editor_plugin.js | 2 +- .../plugins/directionality/editor_plugin_src.js | 41 +- .../tiny_mce/plugins/emotions/emotions.htm | 2 +- .../tiny_mce/plugins/fullscreen/editor_plugin.js | 2 +- .../plugins/fullscreen/editor_plugin_src.js | 10 +- .../tiny_mce/plugins/legacyoutput/editor_plugin.js | 2 +- .../plugins/legacyoutput/editor_plugin_src.js | 2 +- .../tiny_mce/plugins/lists/editor_plugin.js | 2 +- .../tiny_mce/plugins/lists/editor_plugin_src.js | 166 +- .../tiny_mce/plugins/media/editor_plugin.js | 2 +- .../tiny_mce/plugins/media/editor_plugin_src.js | 38 +- .../jscripts/tiny_mce/plugins/media/js/media.js | 101 +- .../tiny_mce/plugins/noneditable/editor_plugin.js | 2 +- .../plugins/noneditable/editor_plugin_src.js | 556 +- .../tiny_mce/plugins/paste/editor_plugin.js | 2 +- .../tiny_mce/plugins/paste/editor_plugin_src.js | 20 +- .../plugins/searchreplace/searchreplace.htm | 2 +- .../tiny_mce/plugins/spellchecker/editor_plugin.js | 2 +- .../plugins/spellchecker/editor_plugin_src.js | 8 +- .../jscripts/tiny_mce/plugins/style/css/props.css | 1 + .../tiny_mce/plugins/style/editor_plugin.js | 2 +- .../tiny_mce/plugins/style/editor_plugin_src.js | 22 +- .../jscripts/tiny_mce/plugins/style/js/props.js | 80 +- .../tiny_mce/plugins/style/langs/en_dlg.js | 2 +- .../jscripts/tiny_mce/plugins/style/props.htm | 7 +- .../jscripts/tiny_mce/plugins/style/readme.txt | 19 + .../tiny_mce/plugins/tabfocus/editor_plugin.js | 2 +- .../tiny_mce/plugins/tabfocus/editor_plugin_src.js | 2 +- .../tiny_mce/plugins/table/editor_plugin.js | 2 +- .../tiny_mce/plugins/table/editor_plugin_src.js | 128 +- .../jscripts/tiny_mce/plugins/table/js/cell.js | 4 +- .../jscripts/tiny_mce/plugins/table/js/row.js | 17 + .../jscripts/tiny_mce/plugins/table/js/table.js | 23 +- .../jscripts/tiny_mce/plugins/table/row.htm | 30 +- .../plugins/visualblocks/css/visualblocks.css | 21 + .../tiny_mce/plugins/visualblocks/editor_plugin.js | 1 + .../plugins/visualblocks/editor_plugin_src.js | 63 + .../tiny_mce/plugins/wordcount/editor_plugin.js | 2 +- .../plugins/wordcount/editor_plugin_src.js | 20 +- .../tiny_mce/themes/advanced/color_picker.htm | 8 +- .../tiny_mce/themes/advanced/editor_template.js | 2 +- .../themes/advanced/editor_template_src.js | 224 +- .../tiny_mce/themes/advanced/img/icons.gif | Bin 11790 -> 11982 bytes .../jscripts/tiny_mce/themes/advanced/js/anchor.js | 25 +- .../tiny_mce/themes/advanced/js/color_picker.js | 674 +- .../jscripts/tiny_mce/themes/advanced/js/image.js | 6 +- .../jscripts/tiny_mce/themes/advanced/js/link.js | 12 +- .../tiny_mce/themes/advanced/js/source_editor.js | 32 +- .../tiny_mce/themes/advanced/langs/en_dlg.js | 2 +- .../themes/advanced/skins/default/dialog.css | 5 +- .../tiny_mce/themes/advanced/skins/default/ui.css | 7 +- .../themes/advanced/skins/highcontrast/dialog.css | 7 +- .../themes/advanced/skins/highcontrast/ui.css | 6 +- .../tiny_mce/themes/advanced/skins/o2k7/dialog.css | 5 +- .../tiny_mce/themes/advanced/skins/o2k7/ui.css | 7 +- .../tiny_mce/themes/advanced/source_editor.htm | 2 +- .../vendor/tinymce/jscripts/tiny_mce/tiny_mce.js | 2 +- .../tinymce/jscripts/tiny_mce/tiny_mce_popup.js | 2 +- .../tinymce/jscripts/tiny_mce/tiny_mce_src.js | 16206 +++++++++++-------- 74 files changed, 11294 insertions(+), 8930 deletions(-) delete mode 100644 mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autosave/langs/en.js create mode 100644 mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/readme.txt create mode 100644 mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/visualblocks/css/visualblocks.css create mode 100644 mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/visualblocks/editor_plugin.js create mode 100644 mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/visualblocks/editor_plugin_src.js 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 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 Bildstrm (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 Bildstrm (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 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 Bildstrm (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 Bildstrm (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
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 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 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 - 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 - 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 6379b0d95..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_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:"}}}); \ 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' + name + ''; + + if ((name = nodes[i].id) != "" && !nodes[i].href) + html += ''; } if (html == "") @@ -481,7 +492,7 @@ function getLinkListHTML(elm_id, target_form_element, onchange_func) { var html = ""; html += ''; 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 += ''; 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&&F0?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:M124)&&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''+m.dom.encode(typeof(r[1])==="string"?r[1]:r[0])+""})}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 '' + ed.dom.encode(typeof(args[1]) === "string" ? args[1] : args[0]) + ''; + }); + } - 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,"
")});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="

"+o.encode(r).replace(/\r?\n\r?\n/g,"

").replace(/\r?\n/g,"
")+"

"}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([[/(?:
 [\s\r\n]+|
)*(<\/?(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)[^>]*>)(?:
 [\s\r\n]+|
)*/g,"$1"]]);d([[/

/g,"

"],[/
/g," "],[/

/g,"
"]])}if(/class="?Mso|style="[^"]*\bmso-|w:WordDocument/i.test(j)||e.wordContent){e.wordContent=true;d([/^\s*( )+/gi,/( |]*>)+\s*$/gi]);if(b(k,"paste_convert_headers_to_strong")){j=j.replace(/

]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi,"

$1

")}if(b(k,"paste_convert_middot_lists")){d([[//gi,"$&__MCE_ITEM__"],[/(]+(?:mso-list:|:\s*symbol)[^>]+>)/gi,"$1__MCE_ITEM__"],[/(]+(?:MsoListParagraph)[^>]+>)/gi,"$1__MCE_ITEM__"]])}d([//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([[/([\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([[/]*>/gi,"

"],[/<\/h[1-6][^>]*>/gi,"

"]])}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;l0){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]*>/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"],[/]*>|<\/tr>/gi,"\n"],[/<\/t[dh]>\s*]*>/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,"
"]])}else{if(e=="p"){h([[/\n+/g,"

"],[/^(.*<\/p>)(

)$/,"

$1"]])}else{h([[/\n\n/g,"

"],[/^(.*<\/p>)(

)$/,"

$1"],[/\n/g,"
"]])}}}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,"
")});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="

"+o.encode(r).replace(/\r?\n\r?\n/g,"

").replace(/\r?\n/g,"
")+"

"}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([[/(?:
 [\s\r\n]+|
)*(<\/?(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)[^>]*>)(?:
 [\s\r\n]+|
)*/g,"$1"]]);d([[/

/g,"

"],[/
/g," "],[/

/g,"
"]])}if(/class="?Mso|style="[^"]*\bmso-|w:WordDocument/i.test(j)||e.wordContent){e.wordContent=true;d([/^\s*( )+/gi,/( |]*>)+\s*$/gi]);if(b(k,"paste_convert_headers_to_strong")){j=j.replace(/

]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi,"

$1

")}if(b(k,"paste_convert_middot_lists")){d([[//gi,"$&__MCE_ITEM__"],[/(]+(?:mso-list:|:\s*symbol)[^>]+>)/gi,"$1__MCE_ITEM__"],[/(]+(?:MsoListParagraph)[^>]+>)/gi,"$1__MCE_ITEM__"]])}d([//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([[/([\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([[/]*>/gi,"

"],[/<\/h[1-6][^>]*>/gi,"

"]])}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;l0){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]*>/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"],[/]*>|<\/tr>/gi,"\n"],[/<\/t[dh]>\s*]*>/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"]])}else{if(f=="p"){e([[/\n+/g,"

"],[/^(.*<\/p>)(

)$/,"

$1"]])}else{e([[/\n\n/g,"

"],[/^(.*<\/p>)(

)$/,"

$1"],[/\n/g,"
"]])}}}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([[/(?:
 [\s\r\n]+|
)*(<\/?(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)[^>]*>)(?:
 [\s\r\n]+|
)*/g, '$1']]); @@ -790,10 +791,23 @@ [/<\/t[dh]>\s*]*>/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 @@ - + 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$2");while((s=p.indexOf(""))!=-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("");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$2')}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$2");while((s=p.indexOf(""))!=-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("");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$2')}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 @@ - + @@ -825,6 +825,11 @@ +

+ + +
+
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

text

, 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=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=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'}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='
'}}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;Q1){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;iN){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)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,'
')}}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'}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='
'}}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;Q1){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;iN){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)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?" ":'
')}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, '
'); - }; + if (last && last.nodeName == 'TABLE') { + if (ed.settings.forced_root_block) + ed.dom.add(ed.getBody(), ed.settings.forced_root_block, null, tinymce.isIE ? ' ' : '
'); + 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 - @@ -83,8 +83,8 @@ - - + + @@ -93,25 +93,25 @@ - + + - + + - + + - + +
- + + + -
-
@@ -119,11 +119,11 @@
 
-
@@ -133,7 +133,7 @@
-
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: ")+'0')}}else{tinymce.DOM.add(h,"span",{},'0')}});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: ")+'0')}}else{tinymce.DOM.add(k,"span",{},'0')}});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 @@
- -
- -
- -
+ +
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:"#"},""),"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"));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;s0){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:"#"},""),"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"));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;s0){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();'}, '')); 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 index 641a9e3d3..ca2224901 100644 Binary files a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/img/icons.gif and b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/img/icons.gif differ diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/anchor.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/anchor.js index 04f41e0ca..2909a3a4d 100644 --- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/anchor.js +++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/anchor.js @@ -6,7 +6,7 @@ var AnchorDialog = { this.editor = ed; elm = ed.dom.getParent(ed.selection.getNode(), 'A'); - v = ed.dom.getAttrib(elm, 'name'); + v = ed.dom.getAttrib(elm, 'name') || ed.dom.getAttrib(elm, 'id'); if (v) { this.action = 'update'; @@ -17,7 +17,7 @@ var AnchorDialog = { }, update : function() { - var ed = this.editor, elm, name = document.forms[0].anchorName.value; + var ed = this.editor, elm, name = document.forms[0].anchorName.value, attribName; if (!name || !/^[a-z][a-z0-9\-\_:\.]*$/i.test(name)) { tinyMCEPopup.alert('advanced_dlg.anchor_invalid'); @@ -29,12 +29,25 @@ var AnchorDialog = { if (this.action != 'update') ed.selection.collapse(1); + var aRule = ed.schema.getElementRule('a'); + if (!aRule || aRule.attributes.name) { + attribName = 'name'; + } else { + attribName = 'id'; + } + elm = ed.dom.getParent(ed.selection.getNode(), 'A'); if (elm) { - elm.setAttribute('name', name); - elm.name = name; - } else - ed.execCommand('mceInsertContent', 0, ed.dom.createHTML('a', {name : name, 'class' : 'mceItemAnchor'}, '')); + elm.setAttribute(attribName, name); + elm[attribName] = name; + ed.undoManager.add(); + } else { + // create with zero-sized nbsp so that in Webkit where anchor is on last line by itself caret cannot be placed after it + var attrs = {'class' : 'mceItemAnchor'}; + attrs[attribName] = name; + ed.execCommand('mceInsertContent', 0, ed.dom.createHTML('a', attrs, '\uFEFF')); + ed.nodeChanged(); + } tinyMCEPopup.close(); } diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/color_picker.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/color_picker.js index f51e703b0..cc891c171 100644 --- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/color_picker.js +++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/color_picker.js @@ -1,329 +1,345 @@ -tinyMCEPopup.requireLangPack(); - -var detail = 50, strhex = "0123456789abcdef", i, isMouseDown = false, isMouseOver = false; - -var colors = [ - "#000000","#000033","#000066","#000099","#0000cc","#0000ff","#330000","#330033", - "#330066","#330099","#3300cc","#3300ff","#660000","#660033","#660066","#660099", - "#6600cc","#6600ff","#990000","#990033","#990066","#990099","#9900cc","#9900ff", - "#cc0000","#cc0033","#cc0066","#cc0099","#cc00cc","#cc00ff","#ff0000","#ff0033", - "#ff0066","#ff0099","#ff00cc","#ff00ff","#003300","#003333","#003366","#003399", - "#0033cc","#0033ff","#333300","#333333","#333366","#333399","#3333cc","#3333ff", - "#663300","#663333","#663366","#663399","#6633cc","#6633ff","#993300","#993333", - "#993366","#993399","#9933cc","#9933ff","#cc3300","#cc3333","#cc3366","#cc3399", - "#cc33cc","#cc33ff","#ff3300","#ff3333","#ff3366","#ff3399","#ff33cc","#ff33ff", - "#006600","#006633","#006666","#006699","#0066cc","#0066ff","#336600","#336633", - "#336666","#336699","#3366cc","#3366ff","#666600","#666633","#666666","#666699", - "#6666cc","#6666ff","#996600","#996633","#996666","#996699","#9966cc","#9966ff", - "#cc6600","#cc6633","#cc6666","#cc6699","#cc66cc","#cc66ff","#ff6600","#ff6633", - "#ff6666","#ff6699","#ff66cc","#ff66ff","#009900","#009933","#009966","#009999", - "#0099cc","#0099ff","#339900","#339933","#339966","#339999","#3399cc","#3399ff", - "#669900","#669933","#669966","#669999","#6699cc","#6699ff","#999900","#999933", - "#999966","#999999","#9999cc","#9999ff","#cc9900","#cc9933","#cc9966","#cc9999", - "#cc99cc","#cc99ff","#ff9900","#ff9933","#ff9966","#ff9999","#ff99cc","#ff99ff", - "#00cc00","#00cc33","#00cc66","#00cc99","#00cccc","#00ccff","#33cc00","#33cc33", - "#33cc66","#33cc99","#33cccc","#33ccff","#66cc00","#66cc33","#66cc66","#66cc99", - "#66cccc","#66ccff","#99cc00","#99cc33","#99cc66","#99cc99","#99cccc","#99ccff", - "#cccc00","#cccc33","#cccc66","#cccc99","#cccccc","#ccccff","#ffcc00","#ffcc33", - "#ffcc66","#ffcc99","#ffcccc","#ffccff","#00ff00","#00ff33","#00ff66","#00ff99", - "#00ffcc","#00ffff","#33ff00","#33ff33","#33ff66","#33ff99","#33ffcc","#33ffff", - "#66ff00","#66ff33","#66ff66","#66ff99","#66ffcc","#66ffff","#99ff00","#99ff33", - "#99ff66","#99ff99","#99ffcc","#99ffff","#ccff00","#ccff33","#ccff66","#ccff99", - "#ccffcc","#ccffff","#ffff00","#ffff33","#ffff66","#ffff99","#ffffcc","#ffffff" -]; - -var named = { - '#F0F8FF':'Alice Blue','#FAEBD7':'Antique White','#00FFFF':'Aqua','#7FFFD4':'Aquamarine','#F0FFFF':'Azure','#F5F5DC':'Beige', - '#FFE4C4':'Bisque','#000000':'Black','#FFEBCD':'Blanched Almond','#0000FF':'Blue','#8A2BE2':'Blue Violet','#A52A2A':'Brown', - '#DEB887':'Burly Wood','#5F9EA0':'Cadet Blue','#7FFF00':'Chartreuse','#D2691E':'Chocolate','#FF7F50':'Coral','#6495ED':'Cornflower Blue', - '#FFF8DC':'Cornsilk','#DC143C':'Crimson','#00FFFF':'Cyan','#00008B':'Dark Blue','#008B8B':'Dark Cyan','#B8860B':'Dark Golden Rod', - '#A9A9A9':'Dark Gray','#A9A9A9':'Dark Grey','#006400':'Dark Green','#BDB76B':'Dark Khaki','#8B008B':'Dark Magenta','#556B2F':'Dark Olive Green', - '#FF8C00':'Darkorange','#9932CC':'Dark Orchid','#8B0000':'Dark Red','#E9967A':'Dark Salmon','#8FBC8F':'Dark Sea Green','#483D8B':'Dark Slate Blue', - '#2F4F4F':'Dark Slate Gray','#2F4F4F':'Dark Slate Grey','#00CED1':'Dark Turquoise','#9400D3':'Dark Violet','#FF1493':'Deep Pink','#00BFFF':'Deep Sky Blue', - '#696969':'Dim Gray','#696969':'Dim Grey','#1E90FF':'Dodger Blue','#B22222':'Fire Brick','#FFFAF0':'Floral White','#228B22':'Forest Green', - '#FF00FF':'Fuchsia','#DCDCDC':'Gainsboro','#F8F8FF':'Ghost White','#FFD700':'Gold','#DAA520':'Golden Rod','#808080':'Gray','#808080':'Grey', - '#008000':'Green','#ADFF2F':'Green Yellow','#F0FFF0':'Honey Dew','#FF69B4':'Hot Pink','#CD5C5C':'Indian Red','#4B0082':'Indigo','#FFFFF0':'Ivory', - '#F0E68C':'Khaki','#E6E6FA':'Lavender','#FFF0F5':'Lavender Blush','#7CFC00':'Lawn Green','#FFFACD':'Lemon Chiffon','#ADD8E6':'Light Blue', - '#F08080':'Light Coral','#E0FFFF':'Light Cyan','#FAFAD2':'Light Golden Rod Yellow','#D3D3D3':'Light Gray','#D3D3D3':'Light Grey','#90EE90':'Light Green', - '#FFB6C1':'Light Pink','#FFA07A':'Light Salmon','#20B2AA':'Light Sea Green','#87CEFA':'Light Sky Blue','#778899':'Light Slate Gray','#778899':'Light Slate Grey', - '#B0C4DE':'Light Steel Blue','#FFFFE0':'Light Yellow','#00FF00':'Lime','#32CD32':'Lime Green','#FAF0E6':'Linen','#FF00FF':'Magenta','#800000':'Maroon', - '#66CDAA':'Medium Aqua Marine','#0000CD':'Medium Blue','#BA55D3':'Medium Orchid','#9370D8':'Medium Purple','#3CB371':'Medium Sea Green','#7B68EE':'Medium Slate Blue', - '#00FA9A':'Medium Spring Green','#48D1CC':'Medium Turquoise','#C71585':'Medium Violet Red','#191970':'Midnight Blue','#F5FFFA':'Mint Cream','#FFE4E1':'Misty Rose','#FFE4B5':'Moccasin', - '#FFDEAD':'Navajo White','#000080':'Navy','#FDF5E6':'Old Lace','#808000':'Olive','#6B8E23':'Olive Drab','#FFA500':'Orange','#FF4500':'Orange Red','#DA70D6':'Orchid', - '#EEE8AA':'Pale Golden Rod','#98FB98':'Pale Green','#AFEEEE':'Pale Turquoise','#D87093':'Pale Violet Red','#FFEFD5':'Papaya Whip','#FFDAB9':'Peach Puff', - '#CD853F':'Peru','#FFC0CB':'Pink','#DDA0DD':'Plum','#B0E0E6':'Powder Blue','#800080':'Purple','#FF0000':'Red','#BC8F8F':'Rosy Brown','#4169E1':'Royal Blue', - '#8B4513':'Saddle Brown','#FA8072':'Salmon','#F4A460':'Sandy Brown','#2E8B57':'Sea Green','#FFF5EE':'Sea Shell','#A0522D':'Sienna','#C0C0C0':'Silver', - '#87CEEB':'Sky Blue','#6A5ACD':'Slate Blue','#708090':'Slate Gray','#708090':'Slate Grey','#FFFAFA':'Snow','#00FF7F':'Spring Green', - '#4682B4':'Steel Blue','#D2B48C':'Tan','#008080':'Teal','#D8BFD8':'Thistle','#FF6347':'Tomato','#40E0D0':'Turquoise','#EE82EE':'Violet', - '#F5DEB3':'Wheat','#FFFFFF':'White','#F5F5F5':'White Smoke','#FFFF00':'Yellow','#9ACD32':'Yellow Green' -}; - -var namedLookup = {}; - -function init() { - var inputColor = convertRGBToHex(tinyMCEPopup.getWindowArg('input_color')), key, value; - - tinyMCEPopup.resizeToInnerSize(); - - generatePicker(); - generateWebColors(); - generateNamedColors(); - - if (inputColor) { - changeFinalColor(inputColor); - - col = convertHexToRGB(inputColor); - - if (col) - updateLight(col.r, col.g, col.b); - } - - for (key in named) { - value = named[key]; - namedLookup[value.replace(/\s+/, '').toLowerCase()] = key.replace(/#/, '').toLowerCase(); - } -} - -function toHexColor(color) { - var matches, red, green, blue, toInt = parseInt; - - function hex(value) { - value = parseInt(value).toString(16); - - return value.length > 1 ? value : '0' + value; // Padd with leading zero - }; - - color = color.replace(/[\s#]+/g, '').toLowerCase(); - color = namedLookup[color] || color; - matches = /^rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)|([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})|([a-f0-9])([a-f0-9])([a-f0-9])$/.exec(color); - - if (matches) { - if (matches[1]) { - red = toInt(matches[1]); - green = toInt(matches[2]); - blue = toInt(matches[3]); - } else if (matches[4]) { - red = toInt(matches[4], 16); - green = toInt(matches[5], 16); - blue = toInt(matches[6], 16); - } else if (matches[7]) { - red = toInt(matches[7] + matches[7], 16); - green = toInt(matches[8] + matches[8], 16); - blue = toInt(matches[9] + matches[9], 16); - } - - return '#' + hex(red) + hex(green) + hex(blue); - } - - return ''; -} - -function insertAction() { - var color = document.getElementById("color").value, f = tinyMCEPopup.getWindowArg('func'); - - tinyMCEPopup.restoreSelection(); - - if (f) - f(toHexColor(color)); - - tinyMCEPopup.close(); -} - -function showColor(color, name) { - if (name) - document.getElementById("colorname").innerHTML = name; - - document.getElementById("preview").style.backgroundColor = color; - document.getElementById("color").value = color.toUpperCase(); -} - -function convertRGBToHex(col) { - var re = new RegExp("rgb\\s*\\(\\s*([0-9]+).*,\\s*([0-9]+).*,\\s*([0-9]+).*\\)", "gi"); - - if (!col) - return col; - - var rgb = col.replace(re, "$1,$2,$3").split(','); - if (rgb.length == 3) { - r = parseInt(rgb[0]).toString(16); - g = parseInt(rgb[1]).toString(16); - b = parseInt(rgb[2]).toString(16); - - r = r.length == 1 ? '0' + r : r; - g = g.length == 1 ? '0' + g : g; - b = b.length == 1 ? '0' + b : b; - - return "#" + r + g + b; - } - - return col; -} - -function convertHexToRGB(col) { - if (col.indexOf('#') != -1) { - col = col.replace(new RegExp('[^0-9A-F]', 'gi'), ''); - - r = parseInt(col.substring(0, 2), 16); - g = parseInt(col.substring(2, 4), 16); - b = parseInt(col.substring(4, 6), 16); - - return {r : r, g : g, b : b}; - } - - return null; -} - -function generatePicker() { - var el = document.getElementById('light'), h = '', i; - - for (i = 0; i < detail; i++){ - h += '
'; - } - - 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 += '
' - + ''; - - for (i=0; i' - + ''; - if (tinyMCEPopup.editor.forcedHighContrastMode) { - h += ''; - } - h += ''; - h += ''; - if ((i+1) % 18 == 0) - h += ''; - } - - h += '
'; - - 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 += ''; - if (tinyMCEPopup.editor.forcedHighContrastMode) { - h += ''; - } - h += ''; - h += ''; - 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=0) && (i 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 += '
'; + } + + 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 += '
' + + ''; + + for (i=0; i' + + ''; + if (tinyMCEPopup.editor.forcedHighContrastMode) { + h += ''; + } + h += ''; + h += ''; + if ((i+1) % 18 == 0) + h += ''; + } + + h += '
'; + + 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 += ''; + if (tinyMCEPopup.editor.forcedHighContrastMode) { + h += ''; + } + h += ''; + h += ''; + 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=0) && (i - +
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=c.length){for(e=0,b=g.length;e=c.length||g[e]!=c[e]){f=e+1;break}}}if(g.length=g.length||g[e]!=c[e]){f=e+1;break}}}if(f==1){return h}for(e=0,b=g.length-(f-1);e=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="[";i0?",":"")+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;n1){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;g1?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;u0){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=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(F0&&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=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;h1){v.reverse();z=n=f.filterNode(v[0].clone());for(t=0;t0){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;C0){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;n0){o=c[c.length-1];if(o.length>0&&o!=="\n"){c.push("\n")}}},end:function(h){var i;c.push("");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("")},comment:function(h){c.push("")},pi:function(h,i){if(i){c.push("")}else{c.push("")}if(a){c.push("\n")}},doctype:function(h){c.push("",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=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+""}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="
"+k;n.removeChild(n.firstChild)}catch(m){n=j.create("div");n.innerHTML="
"+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(Z0){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":function(x,r){var u=typeof r==="string",v,s=0,e=x.length;if(u&&!/\W/.test(r)){r=r.toLowerCase();for(;s=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 re[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=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";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="";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="

";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="
";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;t0){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=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"+(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+='_';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('_'+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(''+l+"");if(!n){j.collapse(false);g.moveToElementText(j.parentElement());if(g.compareEndPoints("StartToEnd",j)==0){j.move("character",-1)}j.pasteHTML(''+l+"")}}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?'
':" "}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(/()/g,"\n").replace(/^[\r\n]*|[\r\n]*$/g,"").replace(/^\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="// "}}else{if(o.length>0){n.firstChild.value=""}}}});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&&dd){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&&d0&&g=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.clientHeightv){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='';if(e.image&&!(this.editor&&this.editor.forcedHighContrastMode)){d+=''+a.encode(e.title)+''+c}else{d+=''+(c?''+c+"":"")}d+='";d+="";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='';i+="";i+="";i+="";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="";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+=""+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)+"";e=b.createHTML("span",{"class":"mceOpen "+g["class"]},'');i+=""+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)+"";i+="";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('
');i.push("");i.push('");j(e,function(h){i.push(h.renderHTML())});i.push("");i.push("
");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"))}if(d&&k.ListBox){if(d.Button||d.SplitButton){f+=c.createHTML("td",{"class":"mceToolbarEnd"},c.createHTML("span",null,""))}}if(c.stdMode){f+=''+k.renderHTML()+""}else{f+=""+k.renderHTML()+""}if(g&&k.ListBox){if(g.Button||g.SplitButton){f+=c.createHTML("td",{"class":"mceToolbarStart"},c.createHTML("span",null,""))}}}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,""));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"},""+f+"")}})})(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':"",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+'';if(I.document_base_url!=m.documentBaseURL){H.iframeHTML+=''}if(I.ie7_compat){H.iframeHTML+=''}else{H.iframeHTML+=''}H.iframeHTML+='';for(z=0;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+='
';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""})})}})}if(v.convert_newlines_to_brs){q.onBeforeSetContent.add(function(s,t){if(t.initial){t.content=t.content.replace(/\r?\n/g,"
")}})}if(v.preformatted){q.onPostProcess.add(function(s,t){t.content=t.content.replace(/^\s*/,"");t.content=t.content.replace(/<\/pre>\s*$/,"");if(t.set){t.content='
'+t.content+"
"}})}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(/^(]*>( | |\s|\u00a0|)<\/p>[\r\n]*|
[\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+'>
"}else{u='
'}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='\uFEFF';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(//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.yL.x+L.w||C.x")},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;j0){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(e0||this.typing},hasRedo:function(){return e');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('
',{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,"
");T=v.dom.add(O,V.element,null,"
")}else{I=O.innerHTML;O.innerHTML="";v.dom.add(O,V.element,null,I);T=v.dom.add(O,V.element,null,"
")}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":"
";return r[0]}else{y.innerHTML=b?"\u00a0":"
"}}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(BL.y+L.h){v.getWin().scrollTo(0,B1||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||(avam)){O(a.grep(an.childNodes),al);return 0}else{au=aq.cloneNode(R);O(a.grep(an.childNodes),function(ax,aw){if((avam&&aw>am)){ak.push(ax);ax.parentNode.removeChild(ax)}});if(avam){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;av1||!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=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:aeag?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||ay0&&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;aoX?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=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;m0?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=c.length||g[e]!=c[e]){f=e+1;break}}}if(g.length=g.length||g[e]!=c[e]){f=e+1;break}}}if(f===1){return h}for(e=0,b=g.length-(f-1);e=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="[";i0?",":"")+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;n1){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;g1?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;u0){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=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(G0&&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=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;h1){x.reverse();A=o=f.filterNode(x[0].clone());for(u=0;u0){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;D0){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;n0){o=c[c.length-1];if(o.length>0&&o!=="\n"){c.push("\n")}}},end:function(h){var i;c.push("");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("")},comment:function(h){c.push("")},pi:function(h,i){if(i){c.push("")}else{c.push("")}if(a){c.push("\n")}},doctype:function(h){c.push("",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=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+""}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="
"+j;m.removeChild(m.firstChild)}catch(l){var n=i.create("div");n.innerHTML="
"+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(ab0){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="\uFEFF";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="\uFEFF\uFEFF";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;e0};d.find=function(E,e,F){var D,z,B,A,C,y;if(!E){return[]}for(z=0,B=k.order.length;z":function(D,y){var C,B=typeof y==="string",z=0,e=D.length;if(B&&!x.test(y)){y=y.toLowerCase();for(;z=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 ye[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=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.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="";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="

";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="
";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;A0){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"+(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+='_';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('_'+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(''+m+"");if(!o){k.collapse(false);h.moveToElementText(k.parentElement());if(h.compareEndPoints("StartToEnd",k)===0){k.move("character",-1)}k.pasteHTML(''+m+"")}}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='
'}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(jh.y+h.h){g.editor.getWin().scrollTo(0,j0){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(/()/g,"\n").replace(/^[\r\n]*|[\r\n]*$/g,"").replace(/^\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="// "}}else{if(o.length>0){n.firstChild.value=""}}}});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&&dd){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&&d0&&g=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.clientHeightv){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='';if(e.image&&!(this.editor&&this.editor.forcedHighContrastMode)){d+=''+a.encode(e.title)+''+(c?''+c+"":"")}else{d+=''+(c?''+c+"":"")}d+='";d+="";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='';j+="";j+="";j+="";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="";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+=""+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)+"";e=b.createHTML("span",{"class":"mceOpen "+g["class"]},'');i+=""+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)+"";i+="";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('
');i.push("");i.push('");j(e,function(h){i.push(h.renderHTML())});i.push("");i.push("
");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"))}if(d&&k.ListBox){if(d.Button||d.SplitButton){f+=c.createHTML("td",{"class":"mceToolbarEnd"},c.createHTML("span",null,""))}}if(c.stdMode){f+=''+k.renderHTML()+""}else{f+=""+k.renderHTML()+""}if(g&&k.ListBox){if(g.Button||g.SplitButton){f+=c.createHTML("td",{"class":"mceToolbarStart"},c.createHTML("span",null,""))}}}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,""));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"},""+f+"")}})})(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':"",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';if(H.document_base_url!=k.documentBaseURL){G.iframeHTML+=''}if(k.isIE8){if(H.ie7_compat){G.iframeHTML+=''}else{G.iframeHTML+=''}}G.iframeHTML+='';for(x=0;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+='
";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""})})})}n.onSetContent.add(function(){n.addVisual(n.getBody())});if(p.padd_empty_editor){n.onPostProcess.add(function(t,u){u.content=u.content.replace(/^(]*>( | |\s|\u00a0|)<\/p>[\r\n]*|
[\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+'>
"}else{r='
'}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='\uFEFF';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(//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.yL.x+L.w||C.x")},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(/]+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;m0){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(i0||this.typing},hasRedo:function(){return i0){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;k1||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||(aAar)){T(a.grep(at.childNodes),aq);return 0}else{az=c.clone(aw,X);T(a.grep(at.childNodes),function(aC,aB){if((aAar&&aB>ar)){ap.push(aC);aC.parentNode.removeChild(aC)}});if(aAar){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;aB1||!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=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:akan?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||aF0&&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;adad?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=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(US.y+S.h){f.getWin().scrollTo(0,U'}return R}function q(Q){var P,O,N;if(z.nodeType==3&&(Q?u>0: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(' - -
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 @@ - -
- - 'params[twitter_username]', - 'value' => $vars['entity']->twitter_username, - )) ?> -
-
- - 'params[twitter_num]', - 'value' => $vars['entity']->twitter_num, - )) ?> - -
\ No newline at end of file -- cgit v1.2.3 From 999f23c13e8c34f4cbae4af409446d037043d798 Mon Sep 17 00:00:00 2001 From: cash Date: Sun, 14 Apr 2013 19:32:03 -0400 Subject: Refs #5335 added unit test for underscore in relative path for JS url normalization --- js/tests/ElggLibTest.js | 1 + 1 file changed, 1 insertion(+) diff --git a/js/tests/ElggLibTest.js b/js/tests/ElggLibTest.js index 2a676e22a..31b561923 100644 --- a/js/tests/ElggLibTest.js +++ b/js/tests/ElggLibTest.js @@ -78,6 +78,7 @@ ElggLibTest.prototype.testNormalizeUrl = function() { ['https://example.com', 'https://example.com'], ['http://example-time.com', 'http://example-time.com'], ['//example.com', '//example.com'], + ['mod/my_plugin/graphics/image.jpg', elgg.config.wwwroot + 'mod/my_plugin/graphics/image.jpg'], ['ftp://example.com/file', 'ftp://example.com/file'], ['mailto:brett@elgg.org', 'mailto:brett@elgg.org'], -- cgit v1.2.3 From 037cce80328318f7151c498d48f983cabd886505 Mon Sep 17 00:00:00 2001 From: cash Date: Sun, 14 Apr 2013 19:59:30 -0400 Subject: entity and query cache are size limited so don't need cache clearing in ElggBatch --- engine/classes/ElggBatch.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/engine/classes/ElggBatch.php b/engine/classes/ElggBatch.php index 1912f89a2..eb93b0f5d 100644 --- a/engine/classes/ElggBatch.php +++ b/engine/classes/ElggBatch.php @@ -227,12 +227,6 @@ class ElggBatch * @return bool */ private function getNextResultsChunk() { - // reset memory caches after first chunk load - if ($this->chunkIndex > 0) { - global $ENTITY_CACHE; - $ENTITY_CACHE = array(); - _elgg_invalidate_query_cache(); - } // always reset results. $this->results = array(); -- cgit v1.2.3 From 6470154396269707e95c6dbbf40157f533928059 Mon Sep 17 00:00:00 2001 From: cash Date: Sun, 14 Apr 2013 20:16:47 -0400 Subject: Fixes #5358 updates text on twitter login --- mod/twitter_api/languages/en.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/twitter_api/languages/en.php b/mod/twitter_api/languages/en.php index f4b3c7f94..c19a058aa 100644 --- a/mod/twitter_api/languages/en.php +++ b/mod/twitter_api/languages/en.php @@ -25,7 +25,7 @@ $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: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.', -- cgit v1.2.3 From 97e797427da01fffe3c85b17f41427469b33ca32 Mon Sep 17 00:00:00 2001 From: cash Date: Sun, 14 Apr 2013 20:35:48 -0400 Subject: updated twitteroauth library to latest from github: https://github.com/abraham/twitteroauth/commit/61f5a550cd3643c619e84cb914ef1f22e491c270 (note: now conflicts with oauth_api library) --- mod/twitter_api/manifest.xml | 10 +- mod/twitter_api/vendors/twitteroauth/OAuth.php | 390 ++++++++++++++++++++- mod/twitter_api/vendors/twitteroauth/README | 117 ++++++- .../vendors/twitteroauth/twitterOAuth.php | 16 +- 4 files changed, 495 insertions(+), 38 deletions(-) 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 @@ Twitter API Core developers - 1.8 + 1.8.15 Allows users to authenticate their Elgg account with Twitter. api bundled @@ -13,15 +13,15 @@ elgg_release 1.8 - - plugin - oauth_api - php_extension curl + + plugin + oauth_api + plugin twitterservice 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 @@ 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': -- cgit v1.2.3 From e96a2e9b8ec370a53900a82847502cee763277d3 Mon Sep 17 00:00:00 2001 From: cash Date: Sun, 14 Apr 2013 20:58:45 -0400 Subject: centralize the creation of the api object --- mod/twitter_api/lib/twitter_api.php | 32 +++++++++++++++++++++----------- mod/twitter_api/start.php | 27 ++++++++++----------------- 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/mod/twitter_api/lib/twitter_api.php b/mod/twitter_api/lib/twitter_api.php index e163d2b3e..1299232c0 100644 --- a/mod/twitter_api/lib/twitter_api.php +++ b/mod/twitter_api/lib/twitter_api.php @@ -5,6 +5,24 @@ * @package twitter_api */ +/** + * 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); + return $api; +} + /** * Tests if the system admin has enabled Sign-On-With-Twitter * @@ -121,9 +139,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 @@ -314,11 +330,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 +353,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); } diff --git a/mod/twitter_api/start.php b/mod/twitter_api/start.php index e6221de6b..e3e866c1f 100644 --- a/mod/twitter_api/start.php +++ b/mod/twitter_api/start.php @@ -115,13 +115,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 +123,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 +139,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 +147,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); } -- cgit v1.2.3 From 53c0783cdf59d7320233b79b7bb2b019bdfbd33a Mon Sep 17 00:00:00 2001 From: cash Date: Sun, 14 Apr 2013 21:03:55 -0400 Subject: Fixes #4917 Using twitter API v1.1 --- mod/twitter_api/lib/twitter_api.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mod/twitter_api/lib/twitter_api.php b/mod/twitter_api/lib/twitter_api.php index 1299232c0..8cb1b885e 100644 --- a/mod/twitter_api/lib/twitter_api.php +++ b/mod/twitter_api/lib/twitter_api.php @@ -20,6 +20,9 @@ function twitter_api_get_api_object($oauth_token = null, $oauth_token_secret = n } $api = new TwitterOAuth($consumer_key, $consumer_secret, $oauth_token, $oauth_token_secret); + if ($api) { + $api->host = "https://api.twitter.com/1.1/"; + } return $api; } -- cgit v1.2.3 From 9c3d423d87fe5f287a48158df105364b6eb36de0 Mon Sep 17 00:00:00 2001 From: cash Date: Sun, 14 Apr 2013 21:06:36 -0400 Subject: removes oauth plugin as twitter_api does not depend on it anymore --- mod/oauth_api/manifest.xml | 25 - mod/oauth_api/start.php | 24 - mod/oauth_api/vendors/oauth/LICENSE | 21 - mod/oauth_api/vendors/oauth/example/server/INSTALL | 53 - .../vendors/oauth/example/server/core/init.php | 127 -- .../example/server/core/templates/inc/footer.tpl | 2 - .../example/server/core/templates/inc/header.tpl | 2 - .../oauth/example/server/core/templates/index.tpl | 13 - .../oauth/example/server/core/templates/logon.tpl | 21 - .../example/server/core/templates/register.tpl | 41 - .../vendors/oauth/example/server/www/hello.php | 65 - .../vendors/oauth/example/server/www/index.php | 37 - .../vendors/oauth/example/server/www/logon.php | 55 - .../vendors/oauth/example/server/www/oauth.php | 77 - .../vendors/oauth/example/server/www/register.php | 28 - .../oauth/example/server/www/services.xrds.php | 71 - .../vendors/oauth/library/OAuthDiscovery.php | 226 --- .../vendors/oauth/library/OAuthException.php | 50 - .../vendors/oauth/library/OAuthRequest.php | 801 --------- .../vendors/oauth/library/OAuthRequestLogger.php | 274 --- .../vendors/oauth/library/OAuthRequestSigner.php | 209 --- .../vendors/oauth/library/OAuthRequestVerifier.php | 262 --- .../vendors/oauth/library/OAuthRequester.php | 508 ------ .../vendors/oauth/library/OAuthServer.php | 232 --- mod/oauth_api/vendors/oauth/library/OAuthStore.php | 86 - .../library/body/OAuthBodyContentDisposition.php | 129 -- .../library/body/OAuthBodyMultipartFormdata.php | 143 -- .../vendors/oauth/library/discovery/xrds_parse.php | 304 ---- .../vendors/oauth/library/discovery/xrds_parse.txt | 101 -- .../OAuthSignatureMethod.class.php | 69 - .../OAuthSignatureMethod_HMAC_SHA1.php | 115 -- .../signature_method/OAuthSignatureMethod_MD5.php | 95 - .../OAuthSignatureMethod_PLAINTEXT.php | 80 - .../OAuthSignatureMethod_RSA_SHA1.php | 136 -- .../library/store/OAuthStoreAbstract.class.php | 149 -- .../oauth/library/store/OAuthStoreAnyMeta.php | 265 --- .../oauth/library/store/OAuthStoreMySQL.php | 1879 -------------------- .../vendors/oauth/library/store/mysql/install.php | 32 - .../vendors/oauth/library/store/mysql/mysql.sql | 219 --- .../oauth/test/discovery/xrds-fireeagle.xrds | 78 - .../oauth/test/discovery/xrds-getsatisfaction.xrds | 73 - .../oauth/test/discovery/xrds-magnolia.xrds | 81 - mod/oauth_api/vendors/oauth/test/oauth_test.php | 188 -- 43 files changed, 7446 deletions(-) delete mode 100644 mod/oauth_api/manifest.xml delete mode 100644 mod/oauth_api/start.php delete mode 100644 mod/oauth_api/vendors/oauth/LICENSE delete mode 100644 mod/oauth_api/vendors/oauth/example/server/INSTALL delete mode 100644 mod/oauth_api/vendors/oauth/example/server/core/init.php delete mode 100644 mod/oauth_api/vendors/oauth/example/server/core/templates/inc/footer.tpl delete mode 100644 mod/oauth_api/vendors/oauth/example/server/core/templates/inc/header.tpl delete mode 100644 mod/oauth_api/vendors/oauth/example/server/core/templates/index.tpl delete mode 100644 mod/oauth_api/vendors/oauth/example/server/core/templates/logon.tpl delete mode 100644 mod/oauth_api/vendors/oauth/example/server/core/templates/register.tpl delete mode 100644 mod/oauth_api/vendors/oauth/example/server/www/hello.php delete mode 100644 mod/oauth_api/vendors/oauth/example/server/www/index.php delete mode 100644 mod/oauth_api/vendors/oauth/example/server/www/logon.php delete mode 100644 mod/oauth_api/vendors/oauth/example/server/www/oauth.php delete mode 100644 mod/oauth_api/vendors/oauth/example/server/www/register.php delete mode 100644 mod/oauth_api/vendors/oauth/example/server/www/services.xrds.php delete mode 100644 mod/oauth_api/vendors/oauth/library/OAuthDiscovery.php delete mode 100644 mod/oauth_api/vendors/oauth/library/OAuthException.php delete mode 100644 mod/oauth_api/vendors/oauth/library/OAuthRequest.php delete mode 100644 mod/oauth_api/vendors/oauth/library/OAuthRequestLogger.php delete mode 100644 mod/oauth_api/vendors/oauth/library/OAuthRequestSigner.php delete mode 100644 mod/oauth_api/vendors/oauth/library/OAuthRequestVerifier.php delete mode 100644 mod/oauth_api/vendors/oauth/library/OAuthRequester.php delete mode 100644 mod/oauth_api/vendors/oauth/library/OAuthServer.php delete mode 100644 mod/oauth_api/vendors/oauth/library/OAuthStore.php delete mode 100644 mod/oauth_api/vendors/oauth/library/body/OAuthBodyContentDisposition.php delete mode 100644 mod/oauth_api/vendors/oauth/library/body/OAuthBodyMultipartFormdata.php delete mode 100644 mod/oauth_api/vendors/oauth/library/discovery/xrds_parse.php delete mode 100644 mod/oauth_api/vendors/oauth/library/discovery/xrds_parse.txt delete mode 100644 mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod.class.php delete mode 100644 mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_HMAC_SHA1.php delete mode 100644 mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_MD5.php delete mode 100644 mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_PLAINTEXT.php delete mode 100644 mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_RSA_SHA1.php delete mode 100644 mod/oauth_api/vendors/oauth/library/store/OAuthStoreAbstract.class.php delete mode 100644 mod/oauth_api/vendors/oauth/library/store/OAuthStoreAnyMeta.php delete mode 100644 mod/oauth_api/vendors/oauth/library/store/OAuthStoreMySQL.php delete mode 100644 mod/oauth_api/vendors/oauth/library/store/mysql/install.php delete mode 100644 mod/oauth_api/vendors/oauth/library/store/mysql/mysql.sql delete mode 100644 mod/oauth_api/vendors/oauth/test/discovery/xrds-fireeagle.xrds delete mode 100644 mod/oauth_api/vendors/oauth/test/discovery/xrds-getsatisfaction.xrds delete mode 100644 mod/oauth_api/vendors/oauth/test/discovery/xrds-magnolia.xrds delete mode 100644 mod/oauth_api/vendors/oauth/test/oauth_test.php 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 @@ - - - OAuth API - Core developers - 1.8 - Provides OAuth libraries and API support. - bundled - api - http://www.elgg.org/ - See COPYRIGHT.txt - GNU General Public License version 2 - - elgg_release - 1.8 - - - - plugin - oauth_lib - - - php_extension - oauth - - 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 @@ - - 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 - - - Options Indexes FollowSymLinks MultiViews - AllowOverride None - Order allow,deny - Allow from all - - - php_value magic_quotes_gpc 0 - php_value register_globals 0 - php_value session.auto_start 0 - - - - - - -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 , 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 @@ - - * - * - * 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 @@ - - 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 @@ - - 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'} - -

OAuth server

-Go to: - - - -Afterwards, make an OAuth test request to http://{$smarty.server.name}/hello to test your connection.

- -{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'} - -

Login

- - - - -
- - -

- -
- - -

- - - - -{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'} - -

Register server

- -

Register a server which is gonna act as an identity client.

- -
- -
- About You - -

-
- -

- -

-
- -

-
- -
- Location Of Your Application Or Site - -

-
- -

- -

-
- -

-
- -
- -
- -{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 @@ - - * - * - * 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 @@ - - * - * - * 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 @@ - - * - * - * 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 @@ - - * - * - * 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 @@ -updateConsumer($_POST, 1, true); - - $c = $store->getConsumer($key); - echo 'Your consumer key is: ' . $c['consumer_key'] . '
'; - echo 'Your consumer secret is: ' . $c['consumer_secret'] . '
'; - } - catch (OAuthException $e) - { - echo 'Error: ' . $e->getMessage() . '
'; - } -} - - -$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 @@ - - * - * - * 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 '' . "\n"; - -?> - - - xri://$xrds*simple - - http://oauth.net/discovery/1.0 - #main - - - http://oauth.net/core/1.0/endpoint/request - http://oauth.net/core/1.0/parameters/auth-header - http://oauth.net/core/1.0/parameters/uri-query - http://oauth.net/core/1.0/signature/HMAC-SHA1 - http://oauth.net/core/1.0/signature/PLAINTEXT - http:///oauth/request_token - - - http://oauth.net/core/1.0/endpoint/authorize - http://oauth.net/core/1.0/parameters/uri-query - http:///oauth/authorize - - - http://oauth.net/core/1.0/endpoint/access - http://oauth.net/core/1.0/parameters/auth-header - http://oauth.net/core/1.0/parameters/uri-query - http://oauth.net/core/1.0/signature/HMAC-SHA1 - http://oauth.net/core/1.0/signature/PLAINTEXT - http:///oauth/access_token - - - 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 @@ - - * @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* \ 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 @@ - - * @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 @@ - - * @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 @@ - - * @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 @@ - - * @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 @@ - - * @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 @@ - - * @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 @@ - - * @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 @@ - - * @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 @@ - - * - * 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 @@ - - * @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 @@ - - * - * - * 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 @@ - - * @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 @@ - - * @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 @@ - - * @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 @@ - - * @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 @@ - - * @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 @@ - - * - * 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 @@ - - * @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 @@ - - * @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 @@ - \ 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 @@ - - - - - - - xri://$xrds*simple - 2008-04-15T00:25:30-07:00 - - - - http://oauth.net/core/1.0/endpoint/request - - http://oauth.net/core/1.0/parameters/auth-header - http://oauth.net/core/1.0/parameters/post-body - http://oauth.net/core/1.0/parameters/uri-query - http://oauth.net/core/1.0/signature/HMAC-SHA1 - http://oauth.net/core/1.0/signature/PLAINTEXT - - https://fireeagle.yahooapis.com/oauth/request_token - - - - - http://oauth.net/core/1.0/endpoint/authorize - - http://oauth.net/core/1.0/parameters/auth-header - http://oauth.net/core/1.0/parameters/uri-query - - https://fireeagle.yahooapis.com/oauth/access_token - - - - - http://oauth.net/core/1.0/endpoint/access - - http://oauth.net/core/1.0/parameters/auth-header - http://oauth.net/core/1.0/parameters/post-body - http://oauth.net/core/1.0/parameters/uri-query - http://oauth.net/core/1.0/signature/HMAC-SHA1 - http://oauth.net/core/1.0/signature/PLAINTEXT - - http://fireeagle.yahoo.net/oauth/authorize - - - - - http://oauth.net/core/1.0/endpoint/resource - - http://oauth.net/core/1.0/parameters/auth-header - http://oauth.net/core/1.0/parameters/post-body - http://oauth.net/core/1.0/parameters/uri-query - http://oauth.net/core/1.0/signature/HMAC-SHA1 - http://oauth.net/core/1.0/signature/PLAINTEXT - - - - - - - http://oauth.net/discovery/1.0/consumer-identity/oob - https://fireeagle.yahoo.net/developer/create - - - - - - - xri://$xrds*simple - - - - http://oauth.net/discovery/1.0 - #oauth - - - - \ 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 @@ - - - - - xri://$xrds*simple - 2008-04-30T23:59:59Z - - - - http://oauth.net/core/1.0/endpoint/request - - http://oauth.net/core/1.0/parameters/auth-header - http://oauth.net/core/1.0/signature/HMAC-SHA1 - - http://getsatisfaction.com/api/request_token - - - - http://oauth.net/core/1.0/endpoint/authorize - - http://oauth.net/core/1.0/parameters/uri-query - - http://getsatisfaction.com/api/authorize - - - - - http://oauth.net/core/1.0/endpoint/access - - http://oauth.net/core/1.0/parameters/auth-header - http://oauth.net/core/1.0/signature/HMAC-SHA1 - - http://getsatisfaction.com/api/access_token - - - - - - http://oauth.net/core/1.0/endpoint/resource - - http://oauth.net/core/1.0/parameters/auth-header - http://oauth.net/core/1.0/signature/HMAC-SHA1 - - - - - - - http://oauth.net/discovery/1.0/consumer-identity/oob - http://getsatisfaction.com/me/extensions/new - - - - - - - xri://$xrds*simple - - - - http://oauth.net/discovery/1.0 - #oauth - - - - \ 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 @@ - - - - - - - xri://$xrds*simple - 2008-04-13T07:34:58Z - - - - http://oauth.net/core/1.0/endpoint/request - - http://oauth.net/core/1.0/parameters/auth-header - http://oauth.net/core/1.0/parameters/post-body - http://oauth.net/core/1.0/parameters/uri-query - http://oauth.net/core/1.0/signature/HMAC-SHA1 - http://oauth.net/core/1.0/signature/RSA-SHA1 - http://oauth.net/core/1.0/signature/PLAINTEXT - - https://ma.gnolia.com/oauth/get_request_token - - - - - http://oauth.net/core/1.0/endpoint/authorize - - http://oauth.net/core/1.0/parameters/auth-header - http://oauth.net/core/1.0/parameters/uri-query - - https://ma.gnolia.com/oauth/authorize - http://ma.gnolia.com/oauth/authorize - - - - - http://oauth.net/core/1.0/endpoint/access - - http://oauth.net/core/1.0/parameters/auth-header - http://oauth.net/core/1.0/parameters/post-body - http://oauth.net/core/1.0/parameters/uri-query - http://oauth.net/core/1.0/signature/HMAC-SHA1 - http://oauth.net/core/1.0/signature/RSA-SHA1 - http://oauth.net/core/1.0/signature/PLAINTEXT - - https://ma.gnolia.com/oauth/get_access_token - - - - - http://oauth.net/core/1.0/endpoint/resource - - http://oauth.net/core/1.0/parameters/auth-header - http://oauth.net/core/1.0/parameters/post-body - http://oauth.net/core/1.0/parameters/uri-query - http://oauth.net/core/1.0/signature/HMAC-SHA1 - http://oauth.net/core/1.0/signature/RSA-SHA1 - - - - - - - http://oauth.net/discovery/1.0/consumer-identity/oob - http://ma.gnolia.com/applications/new - - - - - - - xri://$xrds*simple - - - - http://oauth.net/discovery/1.0 - #oauth - - - - \ 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 @@ - - * @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 -- cgit v1.2.3 From 6c7ec418fc0a5b35489614325c807d6f523844df Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Mon, 15 Apr 2013 21:03:00 -0400 Subject: Fixes #5363: Append trailing slash to site URL if missing --- actions/admin/site/update_advanced.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/actions/admin/site/update_advanced.php b/actions/admin/site/update_advanced.php index 0fd8d1f35..4888b0a8d 100644 --- a/actions/admin/site/update_advanced.php +++ b/actions/admin/site/update_advanced.php @@ -14,10 +14,10 @@ if ($site = elgg_get_site_entity()) { throw new InstallationException(elgg_echo('InvalidParameterException:NonElggSite')); } - $site->url = get_input('wwwroot'); + $site->url = rtrim(get_input('wwwroot', '', false), '/') . '/'; - datalist_set('path', sanitise_filepath(get_input('path'))); - $dataroot = sanitise_filepath(get_input('dataroot')); + datalist_set('path', sanitise_filepath(get_input('path', '', false))); + $dataroot = sanitise_filepath(get_input('dataroot', '', false)); // check for relative paths if (stripos(PHP_OS, 'win') === 0) { -- cgit v1.2.3 From 16297373c2e91b6af32102a809b75326a44eb7f3 Mon Sep 17 00:00:00 2001 From: cash Date: Tue, 16 Apr 2013 19:19:08 -0400 Subject: fixed display issues on interstitial twitter page --- mod/twitter_api/pages/twitter_api/interstitial.php | 4 +--- .../views/default/forms/twitter_api/interstitial_settings.php | 7 ++++++- 2 files changed, 7 insertions(+), 4 deletions(-) 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/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 '
'; +$site = get_config('site'); +echo elgg_echo('twitter_api:interstitial:description', array($site->name)); +echo '
'; + $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( -- cgit v1.2.3 From 21d8d10c3a3e974f1dfc110ad55e09a1d4db98c9 Mon Sep 17 00:00:00 2001 From: cash Date: Tue, 16 Apr 2013 19:27:18 -0400 Subject: Fixes #5362 correctly getting user on user plugin settings page --- engine/lib/user_settings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/lib/user_settings.php b/engine/lib/user_settings.php index 3466c25f9..0e36dc46d 100644 --- a/engine/lib/user_settings.php +++ b/engine/lib/user_settings.php @@ -308,7 +308,7 @@ function usersettings_page_handler($page) { $user = get_user_by_username($page[1]); elgg_set_page_owner_guid($user->guid); } else { - $user = elgg_get_logged_in_user_guid(); + $user = elgg_get_logged_in_user_entity(); elgg_set_page_owner_guid($user->guid); } -- cgit v1.2.3 From f1d0a8e62f25002644c7b0394d5a8940d4ac7e5b Mon Sep 17 00:00:00 2001 From: Cash Costello Date: Fri, 19 Apr 2013 08:05:25 -0400 Subject: Fixes #5367 returning all groups that a user belongs to - need to deprecate this function --- engine/lib/group.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/engine/lib/group.php b/engine/lib/group.php index 624029d98..359bc59c2 100644 --- a/engine/lib/group.php +++ b/engine/lib/group.php @@ -242,7 +242,8 @@ function get_users_membership($user_guid) { $options = array( 'relationship' => 'member', 'relationship_guid' => $user_guid, - 'inverse_relationship' => FALSE + 'inverse_relationship' => false, + 'limit' => false, ); return elgg_get_entities_from_relationship($options); } -- cgit v1.2.3 From c4e17030d2041963a11f86abee584c04ee7ccd82 Mon Sep 17 00:00:00 2001 From: cash Date: Fri, 19 Apr 2013 09:34:51 -0400 Subject: Fixes #5373 easier to access URL on reported content --- .../views/default/object/reported_content.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) 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') {

: title; ?> -
- "#report-$report->guid", - 'text' => elgg_echo('reportedcontent:moreinfo'), - 'rel' => "toggle", - )); - ?> -

- - + 0) { foreach ($profile_fields as $shortname => $valtype) { @@ -40,6 +42,14 @@ if (is_array($profile_fields) && count($profile_fields) > 0) { $access_id = ACCESS_DEFAULT; } + //sticky form values take precedence over saved ones + if (isset($stickyValues[$shortname])) { + $value = $stickyValues[$shortname]; + } + if (isset($stickyValues['accesslevel'][$shortname])) { + $access_id = $stickyValues['accesslevel'][$shortname]; + } + ?>
-- cgit v1.2.3 From 666b214e94b80b713797710d04d06d3f11271ff0 Mon Sep 17 00:00:00 2001 From: Cash Costello Date: Fri, 7 Jun 2013 18:26:18 -0400 Subject: clearing sticky form after it is used on edit form --- actions/profile/edit.php | 2 +- views/default/forms/profile/edit.php | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/actions/profile/edit.php b/actions/profile/edit.php index c2a124309..e1f066e82 100644 --- a/actions/profile/edit.php +++ b/actions/profile/edit.php @@ -82,7 +82,7 @@ if (sizeof($input) > 0) { ); elgg_delete_metadata($options); - if(!is_null($value) && ($value !== '')){ + if (!is_null($value) && ($value !== '')) { // only create metadata for non empty values (0 is allowed) to prevent metadata records with empty string values #4858 if (isset($accesslevel[$shortname])) { diff --git a/views/default/forms/profile/edit.php b/views/default/forms/profile/edit.php index aef180f36..cb0a37ca4 100644 --- a/views/default/forms/profile/edit.php +++ b/views/default/forms/profile/edit.php @@ -13,7 +13,7 @@
0) { @@ -42,12 +42,12 @@ if (is_array($profile_fields) && count($profile_fields) > 0) { $access_id = ACCESS_DEFAULT; } - //sticky form values take precedence over saved ones - if (isset($stickyValues[$shortname])) { - $value = $stickyValues[$shortname]; + // sticky form values take precedence over saved ones + if (isset($sticky_values[$shortname])) { + $value = $sticky_values[$shortname]; } - if (isset($stickyValues['accesslevel'][$shortname])) { - $access_id = $stickyValues['accesslevel'][$shortname]; + if (isset($sticky_values['accesslevel'][$shortname])) { + $access_id = $sticky_values['accesslevel'][$shortname]; } ?> @@ -69,6 +69,9 @@ if (is_array($profile_fields) && count($profile_fields) > 0) {
Date: Sun, 9 Jun 2013 01:48:07 +0200 Subject: Refs #5594 - Clearer JS implementation --- views/default/core/walled_garden/login.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/default/core/walled_garden/login.php b/views/default/core/walled_garden/login.php index db9ce3310..54af90f67 100644 --- a/views/default/core/walled_garden/login.php +++ b/views/default/core/walled_garden/login.php @@ -34,7 +34,7 @@ if (elgg_is_sticky_form('register')) { ?> Date: Sat, 8 Jun 2013 21:12:03 -0400 Subject: ElggBatch with incrementOffset off now handles incomplete entities --- engine/classes/ElggBatch.php | 36 +++++++++++++++++++-------- engine/tests/api/helpers.php | 58 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 80 insertions(+), 14 deletions(-) diff --git a/engine/classes/ElggBatch.php b/engine/classes/ElggBatch.php index 83963ccee..ac79cf084 100644 --- a/engine/classes/ElggBatch.php +++ b/engine/classes/ElggBatch.php @@ -242,9 +242,12 @@ class ElggBatch /** * Fetches the next chunk of results * + * @param int $num_incompletes_last_fetch When called recursively, this is the number of + * incomplete entities returned in the last fetch. + * * @return bool */ - private function getNextResultsChunk() { + private function getNextResultsChunk($num_incompletes_last_fetch = 0) { // always reset results. $this->results = array(); @@ -278,7 +281,7 @@ class ElggBatch if ($this->incrementOffset) { $offset = $this->offset + $this->retrievedResults; } else { - $offset = $this->offset; + $offset = $this->offset + $num_incompletes_last_fetch; } $current_options = array( @@ -292,17 +295,30 @@ class ElggBatch $this->incompleteEntities = array(); $this->results = call_user_func_array($this->getter, array($options)); - // If there were incomplete entities, we pretend they were at the beginning of the results, - // fool the local counter to think it's skipped by them already, and update the running - // total as if the results contained the incompletes. - if ($this->results || $this->incompleteEntities) { + $num_results = count($this->results); + $num_incomplete = count($this->incompleteEntities); + + if ($this->incompleteEntities) { + // pad the front of the results with nulls representing the incompletes + array_splice($this->results, 0, 0, array_pad(array(), $num_incomplete, null)); + // ...and skip past them + reset($this->results); + for ($i = 0; $i < $num_incomplete; $i++) { + next($this->results); + } + } + + if ($this->results) { $this->chunkIndex++; - $this->resultIndex = count($this->incompleteEntities); - $this->retrievedResults += (count($this->results) + count($this->incompleteEntities)); - if (!$this->results) { + + // let the system know we've jumped past the nulls + $this->resultIndex = $num_incomplete; + + $this->retrievedResults += ($num_results + $num_incomplete); + if ($num_results == 0) { // This fetch was *all* incompletes! We need to fetch until we can either // offer at least one row to iterate over, or give up. - return $this->getNextResultsChunk(); + return $this->getNextResultsChunk($num_incomplete); } return true; } else { diff --git a/engine/tests/api/helpers.php b/engine/tests/api/helpers.php index 43244636b..06ef55138 100644 --- a/engine/tests/api/helpers.php +++ b/engine/tests/api/helpers.php @@ -578,16 +578,14 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest { $this->assertEqual(11, $j); } - public function testElggBatchHandlesBrokenEntities() { + public function testElggBatchReadHandlesBrokenEntities() { $num_test_entities = 6; $guids = array(); - $now = time(); for ($i = $num_test_entities; $i > 0; $i--) { $entity = new ElggObject(); $entity->type = 'object'; $entity->subtype = 'test_5357_subtype'; $entity->access_id = ACCESS_PUBLIC; - $entity->time_created = ($now - $i); $entity->save(); $guids[] = $entity->guid; _elgg_invalidate_cache_for_entity($entity->guid); @@ -604,11 +602,12 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest { $options = array( 'type' => 'object', 'subtype' => 'test_5357_subtype', - 'order_by' => 'e.time_created ASC', + 'order_by' => 'e.guid', ); $entities_visited = array(); $batch = new ElggBatch('elgg_get_entities', $options, null, 2); + /* @var ElggEntity[] $batch */ foreach ($batch as $entity) { $entities_visited[] = $entity->guid; } @@ -629,6 +628,57 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest { delete_data("DELETE FROM {$db_prefix}objects_entity WHERE guid IN (" . implode(',', $guids) . ")"); } + public function testElggBatchDeleteHandlesBrokenEntities() { + $num_test_entities = 6; + $guids = array(); + for ($i = $num_test_entities; $i > 0; $i--) { + $entity = new ElggObject(); + $entity->type = 'object'; + $entity->subtype = 'test_5357_subtype'; + $entity->access_id = ACCESS_PUBLIC; + $entity->save(); + $guids[] = $entity->guid; + _elgg_invalidate_cache_for_entity($entity->guid); + } + + // break entities such that the first fetch has one incomplete + // and the second fetch has only incompletes! + $db_prefix = elgg_get_config('dbprefix'); + delete_data(" + DELETE FROM {$db_prefix}objects_entity + WHERE guid IN ({$guids[1]}, {$guids[2]}, {$guids[3]}) + "); + + $options = array( + 'type' => 'object', + 'subtype' => 'test_5357_subtype', + 'order_by' => 'e.guid', + ); + + $entities_visited = array(); + $batch = new ElggBatch('elgg_get_entities', $options, null, 2, false); + /* @var ElggEntity[] $batch */ + foreach ($batch as $entity) { + $entities_visited[] = $entity->guid; + $entity->delete(); + } + + // The broken entities should not have been visited + $this->assertEqual($entities_visited, array($guids[0], $guids[4], $guids[5])); + + // cleanup (including leftovers from previous tests) + $entity_rows = elgg_get_entities(array_merge($options, array( + 'callback' => '', + 'limit' => false, + ))); + $guids = array(); + foreach ($entity_rows as $row) { + $guids[] = $row->guid; + } + delete_data("DELETE FROM {$db_prefix}entities WHERE guid IN (" . implode(',', $guids) . ")"); + delete_data("DELETE FROM {$db_prefix}objects_entity WHERE guid IN (" . implode(',', $guids) . ")"); + } + static function elgg_batch_callback_test($options, $reset = false) { static $count = 1; -- cgit v1.2.3 From 8a47b2342e53c9cdf3093982486b19d6cc2f3e9b Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Sat, 8 Jun 2013 21:34:11 -0400 Subject: Improved algorithm by tracking total incomplete entities --- engine/classes/ElggBatch.php | 18 ++++++++++++------ engine/tests/api/helpers.php | 16 ++++++++-------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/engine/classes/ElggBatch.php b/engine/classes/ElggBatch.php index ac79cf084..d810ea066 100644 --- a/engine/classes/ElggBatch.php +++ b/engine/classes/ElggBatch.php @@ -156,6 +156,13 @@ class ElggBatch */ private $incompleteEntities = array(); + /** + * Total number of incomplete entities fetched + * + * @var int + */ + private $totalIncompletes = 0; + /** * Batches operations on any elgg_get_*() or compatible function that supports * an options array. @@ -242,12 +249,9 @@ class ElggBatch /** * Fetches the next chunk of results * - * @param int $num_incompletes_last_fetch When called recursively, this is the number of - * incomplete entities returned in the last fetch. - * * @return bool */ - private function getNextResultsChunk($num_incompletes_last_fetch = 0) { + private function getNextResultsChunk() { // always reset results. $this->results = array(); @@ -281,7 +285,7 @@ class ElggBatch if ($this->incrementOffset) { $offset = $this->offset + $this->retrievedResults; } else { - $offset = $this->offset + $num_incompletes_last_fetch; + $offset = $this->offset + $this->totalIncompletes; } $current_options = array( @@ -298,6 +302,8 @@ class ElggBatch $num_results = count($this->results); $num_incomplete = count($this->incompleteEntities); + $this->totalIncompletes += $num_incomplete; + if ($this->incompleteEntities) { // pad the front of the results with nulls representing the incompletes array_splice($this->results, 0, 0, array_pad(array(), $num_incomplete, null)); @@ -318,7 +324,7 @@ class ElggBatch if ($num_results == 0) { // This fetch was *all* incompletes! We need to fetch until we can either // offer at least one row to iterate over, or give up. - return $this->getNextResultsChunk($num_incomplete); + return $this->getNextResultsChunk(); } return true; } else { diff --git a/engine/tests/api/helpers.php b/engine/tests/api/helpers.php index 06ef55138..10216140f 100644 --- a/engine/tests/api/helpers.php +++ b/engine/tests/api/helpers.php @@ -579,7 +579,7 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest { } public function testElggBatchReadHandlesBrokenEntities() { - $num_test_entities = 6; + $num_test_entities = 8; $guids = array(); for ($i = $num_test_entities; $i > 0; $i--) { $entity = new ElggObject(); @@ -592,11 +592,11 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest { } // break entities such that the first fetch has one incomplete - // and the second fetch has only incompletes! + // and the second and third fetches have only incompletes! $db_prefix = elgg_get_config('dbprefix'); delete_data(" DELETE FROM {$db_prefix}objects_entity - WHERE guid IN ({$guids[1]}, {$guids[2]}, {$guids[3]}) + WHERE guid IN ({$guids[1]}, {$guids[2]}, {$guids[3]}, {$guids[4]}, {$guids[5]}) "); $options = array( @@ -613,7 +613,7 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest { } // The broken entities should not have been visited - $this->assertEqual($entities_visited, array($guids[0], $guids[4], $guids[5])); + $this->assertEqual($entities_visited, array($guids[0], $guids[6], $guids[7])); // cleanup (including leftovers from previous tests) $entity_rows = elgg_get_entities(array_merge($options, array( @@ -629,7 +629,7 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest { } public function testElggBatchDeleteHandlesBrokenEntities() { - $num_test_entities = 6; + $num_test_entities = 8; $guids = array(); for ($i = $num_test_entities; $i > 0; $i--) { $entity = new ElggObject(); @@ -642,11 +642,11 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest { } // break entities such that the first fetch has one incomplete - // and the second fetch has only incompletes! + // and the second and third fetches have only incompletes! $db_prefix = elgg_get_config('dbprefix'); delete_data(" DELETE FROM {$db_prefix}objects_entity - WHERE guid IN ({$guids[1]}, {$guids[2]}, {$guids[3]}) + WHERE guid IN ({$guids[1]}, {$guids[2]}, {$guids[3]}, {$guids[4]}, {$guids[5]}) "); $options = array( @@ -664,7 +664,7 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest { } // The broken entities should not have been visited - $this->assertEqual($entities_visited, array($guids[0], $guids[4], $guids[5])); + $this->assertEqual($entities_visited, array($guids[0], $guids[6], $guids[7])); // cleanup (including leftovers from previous tests) $entity_rows = elgg_get_entities(array_merge($options, array( -- cgit v1.2.3 From 5a1a793f8634e0f7133644e09e9ace60782864d4 Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Sat, 8 Jun 2013 22:12:12 -0400 Subject: Fixes #5598: Properly update metadata cache in update_metadata() --- engine/lib/metadata.php | 10 +++++----- engine/tests/api/metadata_cache.php | 7 +++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/engine/lib/metadata.php b/engine/lib/metadata.php index 43f7d5d6e..046b85124 100644 --- a/engine/lib/metadata.php +++ b/engine/lib/metadata.php @@ -191,19 +191,19 @@ function update_metadata($id, $name, $value, $value_type, $owner_guid, $access_i } // Add the metastring - $value = add_metastring($value); - if (!$value) { + $value_id = add_metastring($value); + if (!$value_id) { return false; } - $name = add_metastring($name); - if (!$name) { + $name_id = add_metastring($name); + if (!$name_id) { return false; } // If ok then add it $query = "UPDATE {$CONFIG->dbprefix}metadata" - . " set name_id='$name', value_id='$value', value_type='$value_type', access_id=$access_id," + . " set name_id='$name_id', value_id='$value_id', value_type='$value_type', access_id=$access_id," . " owner_guid=$owner_guid where id=$id"; $result = update_data($query); diff --git a/engine/tests/api/metadata_cache.php b/engine/tests/api/metadata_cache.php index 846116a7b..7fb328169 100644 --- a/engine/tests/api/metadata_cache.php +++ b/engine/tests/api/metadata_cache.php @@ -166,4 +166,11 @@ class ElggCoreMetadataCacheTest extends ElggCoreUnitTest { $actual = $this->cache->filterMetadataHeavyEntities($guids, 6000); $this->assertIdentical($actual, $expected); } + + public function testCreateMetadataInvalidates() { + $this->obj1->foo = 1; + create_metadata($this->guid1, 'foo', 2, '', elgg_get_logged_in_user_guid(), ACCESS_FRIENDS); + + $this->assertEqual($this->obj1->foo, 2); + } } -- cgit v1.2.3 From 42726251455e87b5ee4d368c0e743057506ad60d Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Sat, 8 Jun 2013 23:29:52 -0400 Subject: Fixes elgg.session.cookie() support of Date() for expires --- js/lib/session.js | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/js/lib/session.js b/js/lib/session.js index fa3d60aa9..0fc7f5c87 100644 --- a/js/lib/session.js +++ b/js/lib/session.js @@ -47,21 +47,18 @@ elgg.session.cookie = function (name, value, options) { } cookies.push(name + '=' + value); - - if (elgg.isNumber(options.expires)) { - if (elgg.isNumber(options.expires)) { - date = new Date(); - date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000)); - } else if (options.expires.toUTCString) { - date = options.expires; - } else { - valid = false; - } - - if (valid) { - cookies.push('expires=' + date.toUTCString()); - } - } + + if (elgg.isNumber(options.expires)) { + date = new Date(); + date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000)); + } else if (options.expires.toUTCString) { + date = options.expires; + } + + if (date) { + cookies.push('expires=' + date.toUTCString()); + } + // CAUTION: Needed to parenthesize options.path and options.domain // in the following expressions, otherwise they evaluate to undefined -- cgit v1.2.3 From 39fa5af1a79f444ec31eb63d0baf299fc8bc7b7a Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Sat, 8 Jun 2013 23:31:37 -0400 Subject: If JS enabled, walled garden registration reload will not flicker --- views/default/core/walled_garden/login.php | 10 --------- views/default/js/walled_garden.php | 35 +++++++++++++++++++++++------- views/default/page/walled_garden.php | 15 ++++++++++++- 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/views/default/core/walled_garden/login.php b/views/default/core/walled_garden/login.php index 54af90f67..42b79607d 100644 --- a/views/default/core/walled_garden/login.php +++ b/views/default/core/walled_garden/login.php @@ -29,13 +29,3 @@ echo <<
HTML; - -if (elgg_is_sticky_form('register')) { -?> - - elgg_echo('cancel'), 'class' => 'elgg-button-cancel mlm', )); -$cancel_button = trim($cancel_button); +$cancel_button = json_encode($cancel_button); if (0) { ?> + \ No newline at end of file -- cgit v1.2.3 From 731b7bdde4790d2bfd565eb2d9c847f4adedf4b4 Mon Sep 17 00:00:00 2001 From: Cash Costello Date: Wed, 12 Jun 2013 21:09:03 -0400 Subject: Refs #5487 account related pages use walled garden now --- pages/account/forgotten_password.php | 11 ++++++++--- pages/account/login.php | 14 +++++++++++--- pages/account/register.php | 11 ++++++++--- pages/account/reset_password.php | 11 ++++++++--- 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/pages/account/forgotten_password.php b/pages/account/forgotten_password.php index bf6ef87e0..f464f98c9 100644 --- a/pages/account/forgotten_password.php +++ b/pages/account/forgotten_password.php @@ -17,6 +17,11 @@ $content .= elgg_view_form('user/requestnewpassword', array( 'class' => 'elgg-form-account', )); -$body = elgg_view_layout("one_column", array('content' => $content)); - -echo elgg_view_page($title, $body); +if (elgg_get_config('walled_garden')) { + elgg_load_css('elgg.walled_garden'); + $body = elgg_view_layout('walled_garden', array('content' => $content)); + echo elgg_view_page($title, $body, 'walled_garden'); +} else { + $body = elgg_view_layout('one_column', array('content' => $content)); + echo elgg_view_page($title, $body); +} diff --git a/pages/account/login.php b/pages/account/login.php index 14f65cc3f..6aa3752d0 100644 --- a/pages/account/login.php +++ b/pages/account/login.php @@ -15,6 +15,14 @@ if (elgg_is_logged_in()) { forward(''); } -$login_box = elgg_view('core/account/login_box'); -$content = elgg_view_layout('one_column', array('content' => $login_box)); -echo elgg_view_page(elgg_echo('login'), $content); +$title = elgg_echo('login'); +$content = elgg_view('core/account/login_box'); + +if (elgg_get_config('walled_garden')) { + elgg_load_css('elgg.walled_garden'); + $body = elgg_view_layout('walled_garden', array('content' => $content)); + echo elgg_view_page($title, $body, 'walled_garden'); +} else { + $body = elgg_view_layout('one_column', array('content' => $content)); + echo elgg_view_page($title, $body); +} diff --git a/pages/account/register.php b/pages/account/register.php index cf18a635b..2fe8b74c0 100644 --- a/pages/account/register.php +++ b/pages/account/register.php @@ -48,6 +48,11 @@ $content .= elgg_view_form('register', $form_params, $body_params); $content .= elgg_view('help/register'); -$body = elgg_view_layout("one_column", array('content' => $content)); - -echo elgg_view_page($title, $body); +if (elgg_get_config('walled_garden')) { + elgg_load_css('elgg.walled_garden'); + $body = elgg_view_layout('walled_garden', array('content' => $content)); + echo elgg_view_page($title, $body, 'walled_garden'); +} else { + $body = elgg_view_layout('one_column', array('content' => $content)); + echo elgg_view_page($title, $body); +} diff --git a/pages/account/reset_password.php b/pages/account/reset_password.php index 6515bfc5d..3ab8ccf3e 100644 --- a/pages/account/reset_password.php +++ b/pages/account/reset_password.php @@ -30,6 +30,11 @@ $form = elgg_view_form('user/passwordreset', array('class' => 'elgg-form-account $title = elgg_echo('resetpassword'); $content = elgg_view_title(elgg_echo('resetpassword')) . $form; -$body = elgg_view_layout('one_column', array('content' => $content)); - -echo elgg_view_page($title, $body); +if (elgg_get_config('walled_garden')) { + elgg_load_css('elgg.walled_garden'); + $body = elgg_view_layout('walled_garden', array('content' => $content)); + echo elgg_view_page($title, $body, 'walled_garden'); +} else { + $body = elgg_view_layout('one_column', array('content' => $content)); + echo elgg_view_page($title, $body); +} -- cgit v1.2.3 From 7b7e6c190f146525848d1849d2d903473f8d7682 Mon Sep 17 00:00:00 2001 From: Cash Costello Date: Wed, 12 Jun 2013 21:09:03 -0400 Subject: Refs #5487 account related pages use walled garden now --- pages/account/forgotten_password.php | 11 ++++++++--- pages/account/login.php | 14 +++++++++++--- pages/account/register.php | 11 ++++++++--- pages/account/reset_password.php | 11 ++++++++--- 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/pages/account/forgotten_password.php b/pages/account/forgotten_password.php index bf6ef87e0..f464f98c9 100644 --- a/pages/account/forgotten_password.php +++ b/pages/account/forgotten_password.php @@ -17,6 +17,11 @@ $content .= elgg_view_form('user/requestnewpassword', array( 'class' => 'elgg-form-account', )); -$body = elgg_view_layout("one_column", array('content' => $content)); - -echo elgg_view_page($title, $body); +if (elgg_get_config('walled_garden')) { + elgg_load_css('elgg.walled_garden'); + $body = elgg_view_layout('walled_garden', array('content' => $content)); + echo elgg_view_page($title, $body, 'walled_garden'); +} else { + $body = elgg_view_layout('one_column', array('content' => $content)); + echo elgg_view_page($title, $body); +} diff --git a/pages/account/login.php b/pages/account/login.php index 14f65cc3f..6aa3752d0 100644 --- a/pages/account/login.php +++ b/pages/account/login.php @@ -15,6 +15,14 @@ if (elgg_is_logged_in()) { forward(''); } -$login_box = elgg_view('core/account/login_box'); -$content = elgg_view_layout('one_column', array('content' => $login_box)); -echo elgg_view_page(elgg_echo('login'), $content); +$title = elgg_echo('login'); +$content = elgg_view('core/account/login_box'); + +if (elgg_get_config('walled_garden')) { + elgg_load_css('elgg.walled_garden'); + $body = elgg_view_layout('walled_garden', array('content' => $content)); + echo elgg_view_page($title, $body, 'walled_garden'); +} else { + $body = elgg_view_layout('one_column', array('content' => $content)); + echo elgg_view_page($title, $body); +} diff --git a/pages/account/register.php b/pages/account/register.php index cf18a635b..2fe8b74c0 100644 --- a/pages/account/register.php +++ b/pages/account/register.php @@ -48,6 +48,11 @@ $content .= elgg_view_form('register', $form_params, $body_params); $content .= elgg_view('help/register'); -$body = elgg_view_layout("one_column", array('content' => $content)); - -echo elgg_view_page($title, $body); +if (elgg_get_config('walled_garden')) { + elgg_load_css('elgg.walled_garden'); + $body = elgg_view_layout('walled_garden', array('content' => $content)); + echo elgg_view_page($title, $body, 'walled_garden'); +} else { + $body = elgg_view_layout('one_column', array('content' => $content)); + echo elgg_view_page($title, $body); +} diff --git a/pages/account/reset_password.php b/pages/account/reset_password.php index 6515bfc5d..3ab8ccf3e 100644 --- a/pages/account/reset_password.php +++ b/pages/account/reset_password.php @@ -30,6 +30,11 @@ $form = elgg_view_form('user/passwordreset', array('class' => 'elgg-form-account $title = elgg_echo('resetpassword'); $content = elgg_view_title(elgg_echo('resetpassword')) . $form; -$body = elgg_view_layout('one_column', array('content' => $content)); - -echo elgg_view_page($title, $body); +if (elgg_get_config('walled_garden')) { + elgg_load_css('elgg.walled_garden'); + $body = elgg_view_layout('walled_garden', array('content' => $content)); + echo elgg_view_page($title, $body, 'walled_garden'); +} else { + $body = elgg_view_layout('one_column', array('content' => $content)); + echo elgg_view_page($title, $body); +} -- cgit v1.2.3 From 13a7ca453d09672c7bbc7bef84eaf4e47b646da5 Mon Sep 17 00:00:00 2001 From: Cash Costello Date: Wed, 12 Jun 2013 21:45:06 -0400 Subject: removed unnecessary cookie and fixed empty screen if cancel after failing registration --- views/default/js/walled_garden.php | 12 +----------- views/default/page/walled_garden.php | 2 +- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/views/default/js/walled_garden.php b/views/default/js/walled_garden.php index 09a478e93..e228df507 100644 --- a/views/default/js/walled_garden.php +++ b/views/default/js/walled_garden.php @@ -5,7 +5,6 @@ * @since 1.8 */ -// note that this assumes the button view is not using single quotes $cancel_button = elgg_view('input/button', array( 'value' => elgg_echo('cancel'), 'class' => 'elgg-button-cancel mlm', @@ -52,22 +51,13 @@ elgg.walled_garden.load = function(view) { if (view == 'register' && $wg.hasClass('hidden')) { // this was a failed register, display the register form ASAP - $('#elgg-walledgarden-login').toggle(); + $('#elgg-walledgarden-login').toggle(false); $(id).toggle(); $wg.removeClass('hidden'); } else { $('#elgg-walledgarden-login').fadeToggle(); $(id).fadeToggle(); } - - if (view == 'register') { - $('.elgg-form-register').submit(function () { - // set short cookie indicating JS support - var date = new Date(); - date.setTime(date.getTime() + (60 * 1000)); - elgg.session.cookie('elgg_js_support', '1', { expires: date }); - }); - } } }); event.preventDefault(); diff --git a/views/default/page/walled_garden.php b/views/default/page/walled_garden.php index 87a79a690..b280cf6b2 100644 --- a/views/default/page/walled_garden.php +++ b/views/default/page/walled_garden.php @@ -7,7 +7,7 @@ $is_sticky_register = elgg_is_sticky_form('register'); $wg_body_class = 'elgg-body-walledgarden'; -if ($is_sticky_register && !empty($_COOKIE['elgg_js_support'])) { +if ($is_sticky_register) { $wg_body_class .= ' hidden'; } -- cgit v1.2.3 From 6d17bde6d059a5d0d5782719623317ec494b773b Mon Sep 17 00:00:00 2001 From: Cash Costello Date: Thu, 13 Jun 2013 07:50:50 -0400 Subject: added some of the notes for the 1.8.16 release --- CHANGES.txt | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 9b79735b3..39a88a677 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,26 @@ +Version 1.8.16 +(June xx, 2013 from https://github.com/Elgg/Elgg/tree/1.8) + Contributing Developers: + * Brett Profitt + * Cash Costello + * Jeff Tilson + * Jerome Bakker + * Paweł Sroka + * Steve Clay + + Security Fixes: + * + + Bugfixes: + * Fixed infinite loop when deleting/disabling an entity with > 50 annotations + * Fixed deleting log tables in log rotate plugin + * Added full text index for groups if missing + * Added workaround for IE8 and jumping user avatar + * Fixed pagination for members pages + * Fixed several internal cache issues + * Plus many more bug fixes + + Version 1.8.15 (April 23, 2013 from https://github.com/Elgg/Elgg/tree/1.8) Contributing Developers: -- cgit v1.2.3 From d2b525a8c9d4926944ad4a9126dbd266dc823a9b Mon Sep 17 00:00:00 2001 From: Cash Costello Date: Fri, 14 Jun 2013 07:29:43 -0400 Subject: Fixes #5626 adds limit for display of titles for river, breadcrumbs, and list pages --- engine/lib/navigation.php | 2 +- views/default/object/elements/summary.php | 2 +- views/default/river/elements/summary.php | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/engine/lib/navigation.php b/engine/lib/navigation.php index 2831d418b..ab9cc05e8 100644 --- a/engine/lib/navigation.php +++ b/engine/lib/navigation.php @@ -218,7 +218,7 @@ function elgg_push_breadcrumb($title, $link = NULL) { } // avoid key collisions. - $CONFIG->breadcrumbs[] = array('title' => $title, 'link' => $link); + $CONFIG->breadcrumbs[] = array('title' => elgg_get_excerpt($title, 100), 'link' => $link); } /** diff --git a/views/default/object/elements/summary.php b/views/default/object/elements/summary.php index c0f3ad340..63ab8f816 100644 --- a/views/default/object/elements/summary.php +++ b/views/default/object/elements/summary.php @@ -27,7 +27,7 @@ if ($title_link === '') { $text = $entity->name; } $params = array( - 'text' => $text, + 'text' => elgg_get_excerpt($text, 100), 'href' => $entity->getURL(), 'is_trusted' => true, ); diff --git a/views/default/river/elements/summary.php b/views/default/river/elements/summary.php index 416bc708b..d7bde51dd 100644 --- a/views/default/river/elements/summary.php +++ b/views/default/river/elements/summary.php @@ -18,9 +18,10 @@ $subject_link = elgg_view('output/url', array( 'is_trusted' => true, )); +$object_text = $object->title ? $object->title : $object->name; $object_link = elgg_view('output/url', array( 'href' => $object->getURL(), - 'text' => $object->title ? $object->title : $object->name, + 'text' => elgg_get_excerpt($object_text, 100), 'class' => 'elgg-river-object', 'is_trusted' => true, )); -- cgit v1.2.3 From afb0cef118a59c86e649e0717748c299a05f2427 Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Fri, 14 Jun 2013 11:28:54 -0400 Subject: elgg.session.cookie() again handles missing options.expires --- js/lib/session.js | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/js/lib/session.js b/js/lib/session.js index 0fc7f5c87..a8d52733c 100644 --- a/js/lib/session.js +++ b/js/lib/session.js @@ -14,9 +14,9 @@ elgg.provide('elgg.session'); * {string} options[domain] * {boolean} options[secure] * - * @return {string} The value of the cookie, if only name is specified + * @return {string|undefined} The value of the cookie, if only name is specified. Undefined if no value set */ -elgg.session.cookie = function (name, value, options) { +elgg.session.cookie = function(name, value, options) { var cookies = [], cookie = [], i = 0, date, valid = true; //elgg.session.cookie() @@ -48,17 +48,18 @@ elgg.session.cookie = function (name, value, options) { cookies.push(name + '=' + value); - if (elgg.isNumber(options.expires)) { - date = new Date(); - date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000)); - } else if (options.expires.toUTCString) { - date = options.expires; - } + if (options.expires) { + if (elgg.isNumber(options.expires)) { + date = new Date(); + date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000)); + } else if (options.expires.toUTCString) { + date = options.expires; + } - if (date) { - cookies.push('expires=' + date.toUTCString()); + if (date) { + cookies.push('expires=' + date.toUTCString()); + } } - // CAUTION: Needed to parenthesize options.path and options.domain // in the following expressions, otherwise they evaluate to undefined -- cgit v1.2.3 From e29554909dc098f163a9dc6de31c42cd749ab4ae Mon Sep 17 00:00:00 2001 From: Jerome Bakker Date: Mon, 17 Jun 2013 14:47:49 +0200 Subject: fixed: wrong default behaviour in can_edit_entity_metadata --- engine/lib/entities.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/lib/entities.php b/engine/lib/entities.php index 072b26805..226cf5c6c 100644 --- a/engine/lib/entities.php +++ b/engine/lib/entities.php @@ -761,7 +761,7 @@ function get_entity($guid) { // @todo We need a single Memcache instance with a shared pool of namespace wrappers. This function would pull an instance from the pool. static $shared_cache; - // We could also use: if (!(int) $guid) { return FALSE }, + // We could also use: if (!(int) $guid) { return FALSE }, // but that evaluates to a false positive for $guid = TRUE. // This is a bit slower, but more thorough. if (!is_numeric($guid) || $guid === 0 || $guid === '0') { @@ -2126,7 +2126,7 @@ function can_edit_entity_metadata($entity_guid, $user_guid = 0, $metadata = null $return = null; - if ($metadata->owner_guid == 0) { + if ($metadata && ($metadata->owner_guid == 0)) { $return = true; } if (is_null($return)) { -- cgit v1.2.3 From ccdf769513fa0268c5ef6880f54dfa1a25f71da6 Mon Sep 17 00:00:00 2001 From: Paweł Sroka Date: Wed, 19 Jun 2013 19:37:10 +0200 Subject: Fixes #5647 - Corrects handling of plus sign in elgg.parse_str --- js/lib/elgglib.js | 4 ++-- js/tests/ElggLibTest.js | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/js/lib/elgglib.js b/js/lib/elgglib.js index af2c94000..cea2d0cdb 100644 --- a/js/lib/elgglib.js +++ b/js/lib/elgglib.js @@ -474,8 +474,8 @@ elgg.parse_str = function(string) { re = /([^&=]+)=?([^&]*)/g; while (result = re.exec(string)) { - key = decodeURIComponent(result[1]) - value = decodeURIComponent(result[2]) + key = decodeURIComponent(result[1].replace(/\+/g,' ')) + value = decodeURIComponent(result[2].replace(/\+/g,' ')) params[key] = value; } diff --git a/js/tests/ElggLibTest.js b/js/tests/ElggLibTest.js index 31b561923..21c6cb214 100644 --- a/js/tests/ElggLibTest.js +++ b/js/tests/ElggLibTest.js @@ -128,3 +128,13 @@ ElggLibTest.prototype.testParseUrl = function() { }); }; +ElggLibTest.prototype.testParseStr = function() { + + [ + ["A+%2B+B", "A + B"] + + ].forEach(function(args) { + assertEquals(args[1], elgg.parse_str(args[0])); + }); +}; + -- cgit v1.2.3 From 77773f0cf081dbb7321075f625cc4ef3d07bbe3b Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Wed, 19 Jun 2013 15:29:18 -0400 Subject: style fixes --- js/lib/elgglib.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/lib/elgglib.js b/js/lib/elgglib.js index cea2d0cdb..a8e187f1d 100644 --- a/js/lib/elgglib.js +++ b/js/lib/elgglib.js @@ -474,8 +474,8 @@ elgg.parse_str = function(string) { re = /([^&=]+)=?([^&]*)/g; while (result = re.exec(string)) { - key = decodeURIComponent(result[1].replace(/\+/g,' ')) - value = decodeURIComponent(result[2].replace(/\+/g,' ')) + key = decodeURIComponent(result[1].replace(/\+/g, ' ')); + value = decodeURIComponent(result[2].replace(/\+/g, ' ')); params[key] = value; } -- cgit v1.2.3 From 8464276af251c8938ecdeb558de4ccaece4f628a Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Wed, 19 Jun 2013 15:40:30 -0400 Subject: Fix ElggLibTest.prototype.testParseStr --- js/tests/ElggLibTest.js | 2 +- js/tests/README | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/js/tests/ElggLibTest.js b/js/tests/ElggLibTest.js index 21c6cb214..bd39e7fb3 100644 --- a/js/tests/ElggLibTest.js +++ b/js/tests/ElggLibTest.js @@ -131,7 +131,7 @@ ElggLibTest.prototype.testParseUrl = function() { ElggLibTest.prototype.testParseStr = function() { [ - ["A+%2B+B", "A + B"] + ["A+%2B+B=A+%2B+B", {"A + B": "A + B"}] ].forEach(function(args) { assertEquals(args[1], elgg.parse_str(args[0])); diff --git a/js/tests/README b/js/tests/README index 4f86b27c6..f43c0c89d 100644 --- a/js/tests/README +++ b/js/tests/README @@ -12,9 +12,10 @@ based debuggers. Visit its wiki at the Google Code site for more information. Sample Usage ============ 1. Put jar file in the base directory of Elgg - 2. Run the server: java -jar JsTestDriver-1.3.3d.jar --port 4224 + 2. Run the server: java -jar JsTestDriver-1.3.5.jar --port 4224 3. Point a web browser at http://localhost:4224 - 4. Run the tests: java -jar JsTestDriver-1.3.3d.jar --config js/tests/jsTestDriver.conf --basePath . --tests all + 4. Click "Capture this browser" + 5. Run the tests: java -jar JsTestDriver-1.3.5.jar --config js/tests/jsTestDriver.conf --basePath . --tests all Configuration Hints -- cgit v1.2.3 From 0433c6e2e1cfda644e18640867828de787cccb8e Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Wed, 19 Jun 2013 15:43:01 -0400 Subject: Add JsTestDriver jar to gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c0bba2c6c..a1c78c400 100644 --- a/.gitignore +++ b/.gitignore @@ -38,7 +38,7 @@ !/mod/uservalidationbyemail/ !/mod/zaudio/ -# ignore IDE/hidden/OS cache files +# ignore IDE/hidden/testing/OS cache files .* *~ /nbproject @@ -49,6 +49,7 @@ Session.vim tmtags Thumbs.db Desktop.ini +/JsTestDriver-*.jar # don't ignore travis config !/.travis.yml -- cgit v1.2.3 From c9d1dd21d3dcf7353570d555c21a2b6ca7ecdd00 Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Sat, 22 Jun 2013 20:19:53 -0400 Subject: Fixes #5673: Properly check simplecache views list if cache file missing --- engine/handlers/cache_handler.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/engine/handlers/cache_handler.php b/engine/handlers/cache_handler.php index 9848d3531..36fc665bb 100644 --- a/engine/handlers/cache_handler.php +++ b/engine/handlers/cache_handler.php @@ -88,20 +88,18 @@ header("ETag: \"$etag\""); $filename = $dataroot . 'views_simplecache/' . md5($viewtype . $view); if (file_exists($filename)) { - $contents = file_get_contents($filename); + readfile($filename); } else { // someone trying to access a non-cached file or a race condition with cache flushing mysql_close($mysql_dblink); require_once(dirname(dirname(__FILE__)) . "/start.php"); global $CONFIG; - if (!isset($CONFIG->views->simplecache[$view])) { + if (!in_array($view, $CONFIG->views->simplecache)) { header("HTTP/1.1 404 Not Found"); exit; } elgg_set_viewtype($viewtype); - $contents = elgg_view($view); + echo elgg_view($view); } - -echo $contents; -- cgit v1.2.3 From 175c65bec4a46ee7ffa424555870b383e77bd3bf Mon Sep 17 00:00:00 2001 From: Cash Costello Date: Tue, 25 Jun 2013 06:54:48 -0400 Subject: preparing 1.8.16 release --- CHANGES.txt | 4 ++-- version.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 39a88a677..187dc7e25 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,5 @@ Version 1.8.16 -(June xx, 2013 from https://github.com/Elgg/Elgg/tree/1.8) +(June 25, 2013 from https://github.com/Elgg/Elgg/tree/1.8) Contributing Developers: * Brett Profitt * Cash Costello @@ -9,7 +9,7 @@ Version 1.8.16 * Steve Clay Security Fixes: - * + * Fixed avatar removal bug (thanks to Jerome Bakker for the first report of this) Bugfixes: * Fixed infinite loop when deleting/disabling an entity with > 50 annotations diff --git a/version.php b/version.php index ac554a945..f21ea074a 100644 --- a/version.php +++ b/version.php @@ -14,4 +14,4 @@ $version = 2013051700; // Human-friendly version name -$release = '1.8.15'; +$release = '1.8.16'; -- cgit v1.2.3 From a873fa6429460ccebbfdb5b7d17f124c80a6ee5c Mon Sep 17 00:00:00 2001 From: cash Date: Fri, 5 Jul 2013 20:10:12 -0400 Subject: Fixes #5745 serve 404 pages when someone requests content of a user that does not exist --- mod/blog/start.php | 13 +++++++++++++ mod/bookmarks/pages/bookmarks/friends.php | 2 +- mod/bookmarks/pages/bookmarks/owner.php | 2 +- mod/file/pages/file/friends.php | 2 +- mod/file/pages/file/owner.php | 2 +- mod/groups/lib/discussion.php | 5 ++--- mod/groups/lib/groups.php | 4 ++-- mod/groups/start.php | 4 ++++ mod/pages/pages/pages/friends.php | 2 +- mod/pages/pages/pages/owner.php | 2 +- mod/thewire/pages/thewire/friends.php | 2 +- mod/thewire/pages/thewire/owner.php | 2 +- 12 files changed, 29 insertions(+), 13 deletions(-) diff --git a/mod/blog/start.php b/mod/blog/start.php index 25cd81935..91525acee 100644 --- a/mod/blog/start.php +++ b/mod/blog/start.php @@ -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,6 +148,10 @@ function blog_page_handler($page) { $params = blog_get_page_content_edit($page_type, $page[1], $page[2]); break; case 'group': + $group = get_entity($page[1]); + if (!elgg_instanceof($group, 'group')) { + forward('', '404'); + } if ($page[2] == 'all') { $params = blog_get_page_content_list($page[1]); } else { 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 b99730fb9..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); 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 5ad6866d6..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"); 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 0557d41eb..e5b047eba 100644 --- a/mod/groups/lib/groups.php +++ b/mod/groups/lib/groups.php @@ -255,8 +255,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); diff --git a/mod/groups/start.php b/mod/groups/start.php index 46ab0e636..6002a535c 100644 --- a/mod/groups/start.php +++ b/mod/groups/start.php @@ -142,6 +142,10 @@ 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()}"; 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/owner.php b/mod/pages/pages/pages/owner.php index 48199368c..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 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 d8dff401e..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)); -- cgit v1.2.3 From 597323ce5dece2597accbf98d55c2d2fa6891f4f Mon Sep 17 00:00:00 2001 From: cash Date: Fri, 5 Jul 2013 20:44:02 -0400 Subject: Fixes #5708 search supports multiple comments on the same entity --- mod/search/search_hooks.php | 28 +++++++++++++++------- .../views/default/search/comments/entity.php | 11 +++++---- mod/search/views/rss/search/comments/entity.php | 11 +++++---- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/mod/search/search_hooks.php b/mod/search/search_hooks.php index c92003c7e..923cf0aa8 100644 --- a/mod/search/search_hooks.php +++ b/mod/search/search_hooks.php @@ -405,14 +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); } - - $order_by = search_get_order_by_sql('e', null, $params['sort'], $params['order']); + + // 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 @@ -450,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; } 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 = "$title"; } -$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 = "

$title

$description"; 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']; ?> -- cgit v1.2.3 From d64cc6180d59768b33fd097bbcc87c91839aab0d Mon Sep 17 00:00:00 2001 From: cash Date: Fri, 5 Jul 2013 20:50:31 -0400 Subject: Fixes #5699 reversing order of months on blog sidebar --- mod/blog/views/default/blog/sidebar/archives.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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'); -- cgit v1.2.3 From 217c51eb6bbf754570cc9347a031b72927bcc985 Mon Sep 17 00:00:00 2001 From: cash Date: Fri, 5 Jul 2013 21:03:34 -0400 Subject: Fixes #5671 fixed query for clearing admin settings on plugin --- engine/classes/ElggPlugin.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/engine/classes/ElggPlugin.php b/engine/classes/ElggPlugin.php index c1c46f272..3cc90f623 100644 --- a/engine/classes/ElggPlugin.php +++ b/engine/classes/ElggPlugin.php @@ -350,11 +350,14 @@ class ElggPlugin extends ElggObject { */ public function unsetAllSettings() { $db_prefix = get_config('dbprefix'); - $ps_prefix = elgg_namespace_plugin_private_setting('setting', ''); + + $us_prefix = elgg_namespace_plugin_private_setting('user_setting', '', $this->getID()); + $is_prefix = elgg_namespace_plugin_private_setting('internal', '', $this->getID()); $q = "DELETE FROM {$db_prefix}private_settings WHERE entity_guid = $this->guid - AND name NOT LIKE '$ps_prefix%'"; + AND name NOT LIKE '$us_prefix%' + AND name NOT LIKE '$is_prefix%'"; return delete_data($q); } -- cgit v1.2.3 From c301e22224db8b064c1de68799b8e49cc1259e2e Mon Sep 17 00:00:00 2001 From: cash Date: Fri, 5 Jul 2013 21:21:00 -0400 Subject: Refs #5662 added error message for what should be impossible condition --- engine/classes/ElggPlugin.php | 2 +- engine/classes/ElggPluginPackage.php | 1 + languages/en.php | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/engine/classes/ElggPlugin.php b/engine/classes/ElggPlugin.php index 3cc90f623..7bf6eb1df 100644 --- a/engine/classes/ElggPlugin.php +++ b/engine/classes/ElggPlugin.php @@ -549,7 +549,7 @@ class ElggPlugin extends ElggObject { * Returns if the plugin is complete, meaning has all required files * and Elgg can read them and they make sense. * - * @todo bad name? This could be confused with isValid() from ElggPackage. + * @todo bad name? This could be confused with isValid() from ElggPluginPackage. * * @return bool */ diff --git a/engine/classes/ElggPluginPackage.php b/engine/classes/ElggPluginPackage.php index 209242288..37eb4bf4d 100644 --- a/engine/classes/ElggPluginPackage.php +++ b/engine/classes/ElggPluginPackage.php @@ -294,6 +294,7 @@ class ElggPluginPackage { return true; } + $this->errorMsg = elgg_echo('unknown_error'); return false; } diff --git a/languages/en.php b/languages/en.php index 49e366484..1721865f7 100644 --- a/languages/en.php +++ b/languages/en.php @@ -903,6 +903,7 @@ $english = array( 'total' => 'Total', 'learnmore' => "Click here to learn more.", + 'unknown_error' => 'Unknown error', 'content' => "content", 'content:latest' => 'Latest activity', -- cgit v1.2.3 From 25929426871cc09d81b0612b239e9ea025bc808c Mon Sep 17 00:00:00 2001 From: cash Date: Fri, 5 Jul 2013 21:36:20 -0400 Subject: Fixes #5613 group member list alphabetical --- mod/groups/lib/groups.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mod/groups/lib/groups.php b/mod/groups/lib/groups.php index e5b047eba..77d7c09cc 100644 --- a/mod/groups/lib/groups.php +++ b/mod/groups/lib/groups.php @@ -366,12 +366,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, 'type' => 'user', 'limit' => 20, + 'joins' => array("JOIN {$db_prefix}users_entity u ON e.guid=u.guid"), + 'order_by' => 'u.name ASC', )); $params = array( -- cgit v1.2.3 From 802b73ade2efe405fb0ba695dc37679002fd1c1c Mon Sep 17 00:00:00 2001 From: cash Date: Fri, 5 Jul 2013 21:51:33 -0400 Subject: Fixes #4872 not passing an array to view that expects HTML --- views/default/object/default.php | 1 - 1 file changed, 1 deletion(-) diff --git a/views/default/object/default.php b/views/default/object/default.php index 110648304..70e098742 100644 --- a/views/default/object/default.php +++ b/views/default/object/default.php @@ -41,7 +41,6 @@ $params = array( 'title' => $title, 'metadata' => $metadata, 'subtitle' => $subtitle, - 'tags' => $vars['entity']->tags, ); $params = $params + $vars; $body = elgg_view('object/elements/summary', $params); -- cgit v1.2.3 From 094b8f3afc75dac6520ea58d4e014b0e7f77d62a Mon Sep 17 00:00:00 2001 From: cash Date: Fri, 5 Jul 2013 22:05:11 -0400 Subject: Fixes #4819 clearing floated images in the content area --- mod/groups/views/default/object/groupforumtopic.php | 5 ++++- views/default/object/elements/full.php | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) 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 << Date: Fri, 5 Jul 2013 22:31:56 -0400 Subject: Fixes #4293 don't require both offset key and offset for listing stuff --- engine/lib/entities.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/engine/lib/entities.php b/engine/lib/entities.php index 226cf5c6c..997db79d2 100644 --- a/engine/lib/entities.php +++ b/engine/lib/entities.php @@ -1473,8 +1473,10 @@ function elgg_list_entities(array $options = array(), $getter = 'elgg_get_entiti global $autofeed; $autofeed = true; + $offset_key = isset($options['offset_key']) ? $options['offset_key'] : 'offset'; + $defaults = array( - 'offset' => (int) max(get_input('offset', 0), 0), + 'offset' => (int) max(get_input($offset_key, 0), 0), 'limit' => (int) max(get_input('limit', 10), 0), 'full_view' => TRUE, 'list_type_toggle' => FALSE, -- cgit v1.2.3 From 9892692deefdb06d9e7176c72fc5780ab79e3a7d Mon Sep 17 00:00:00 2001 From: Brett Profitt Date: Tue, 9 Jul 2013 12:13:17 -0400 Subject: Fixes #5706. Allowing parens in URIs if not last character. --- engine/lib/output.php | 8 ++++---- engine/tests/regression/trac_bugs.php | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/engine/lib/output.php b/engine/lib/output.php index 5adc01053..6172a5c8d 100644 --- a/engine/lib/output.php +++ b/engine/lib/output.php @@ -27,16 +27,16 @@ function parse_urls($text) { // By default htmlawed rewrites tags to this format. // if PHP supported conditional negative lookbehinds we could use this: // $r = preg_replace_callback('/(?"\'\!\(\),]+)/i', - $r = preg_replace_callback('/(?"\'\(\)]+)/i', + $r = preg_replace_callback('/(?"\']+)/i', create_function( '$matches', ' $url = $matches[1]; - $punc = \'\'; + $punc = ""; $last = substr($url, -1, 1); - if (in_array($last, array(".", "!", ","))) { + if (in_array($last, array(".", "!", ",", "(", ")"))) { $punc = $last; - $url = rtrim($url, ".!,"); + $url = rtrim($url, ".!,()"); } $urltext = str_replace("/", "/", $url); return "$urltext$punc"; diff --git a/engine/tests/regression/trac_bugs.php b/engine/tests/regression/trac_bugs.php index 5730830bb..f173b5b9f 100644 --- a/engine/tests/regression/trac_bugs.php +++ b/engine/tests/regression/trac_bugs.php @@ -291,6 +291,9 @@ class ElggCoreRegressionBugsTest extends ElggCoreUnitTest { 'unquoted already anchor yahoo' => 'unquoted already anchor yahoo', + + 'parens in uri http://thedailywtf.com/Articles/A-(Long-Overdue)-BuildMaster-Introduction.aspx' => + 'parens in uri http://thedailywtf.com/Articles/A-(Long-Overdue)-BuildMaster-Introduction.aspx' ); foreach ($cases as $input => $output) { $this->assertEqual($output, parse_urls($input)); -- cgit v1.2.3 From cf15971dc02b59e2ea36041aaec69bc1b9b21a64 Mon Sep 17 00:00:00 2001 From: Cash Costello Date: Sat, 13 Jul 2013 08:06:54 -0400 Subject: Fixes #5800 cast user guid to an int --- engine/lib/sessions.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/engine/lib/sessions.php b/engine/lib/sessions.php index a34c2045b..fb28e1e9a 100644 --- a/engine/lib/sessions.php +++ b/engine/lib/sessions.php @@ -87,6 +87,9 @@ function elgg_is_admin_logged_in() { */ function elgg_is_admin_user($user_guid) { global $CONFIG; + + $user_guid = (int)$user_guid; + // cannot use magic metadata here because of recursion // must support the old way of getting admin from metadata -- cgit v1.2.3 From 2cab755b9df35f5e338151cf1e94cbce5f544e98 Mon Sep 17 00:00:00 2001 From: Cash Costello Date: Sat, 13 Jul 2013 08:39:58 -0400 Subject: adds warning for those using a string value with the operand "IN" for metadata pairs --- engine/lib/metadata.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/engine/lib/metadata.php b/engine/lib/metadata.php index 046b85124..d2f8d4cd4 100644 --- a/engine/lib/metadata.php +++ b/engine/lib/metadata.php @@ -402,9 +402,11 @@ function elgg_enable_metadata(array $options) { * 'operand' => '=', * 'case_sensitive' => TRUE * ) - * Currently if multiple values are sent via + * Currently if multiple values are sent via * an array (value => array('value1', 'value2') * the pair's operand will be forced to "IN". + * If passing "IN" as the operand and a string as the value, + * the value must be a properly quoted and escaped string. * * metadata_name_value_pairs_operator => NULL|STR The operator to use for combining * (name = value) OPERATOR (name = value); default AND -- cgit v1.2.3 From aa3b8ac53fb0f761061ca8ac713427daa5186750 Mon Sep 17 00:00:00 2001 From: Jerome Bakker Date: Tue, 16 Jul 2013 11:38:58 +0200 Subject: changed: page title now matches the document title --- pages/river.php | 1 + 1 file changed, 1 insertion(+) diff --git a/pages/river.php b/pages/river.php index 0e1511334..801d9f664 100644 --- a/pages/river.php +++ b/pages/river.php @@ -49,6 +49,7 @@ $content = elgg_view('core/river/filter', array('selector' => $selector)); $sidebar = elgg_view('core/river/sidebar'); $params = array( + 'title' => $title, 'content' => $content . $activity, 'sidebar' => $sidebar, 'filter_context' => $page_filter, -- cgit v1.2.3 From c09b6c3b91fb46c9816af3bc54a02670bcc6567a Mon Sep 17 00:00:00 2001 From: Brett Profitt Date: Thu, 18 Jul 2013 17:33:16 -0400 Subject: Fixes #5834. Wrapping input with label to prevent line break. --- views/default/input/userpicker.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/views/default/input/userpicker.php b/views/default/input/userpicker.php index 91a397e37..8b64d7df5 100644 --- a/views/default/input/userpicker.php +++ b/views/default/input/userpicker.php @@ -63,11 +63,13 @@ foreach ($vars['value'] as $user_id) { ?>
- - +
\ No newline at end of file + -- cgit v1.2.3 From 392e8e691066c7a7ca9ef82c24f30ef235e1df93 Mon Sep 17 00:00:00 2001 From: Brett Profitt Date: Fri, 9 Aug 2013 18:10:11 -0400 Subject: Fixes #5898. Fixed selector for finding the only friends checkbox. --- js/lib/ui.userpicker.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/lib/ui.userpicker.js b/js/lib/ui.userpicker.js index 7298da114..669b84cdb 100644 --- a/js/lib/ui.userpicker.js +++ b/js/lib/ui.userpicker.js @@ -107,11 +107,11 @@ elgg.userpicker.viewUser = function(info) { * @return Object */ elgg.userpicker.getSearchParams = function(obj) { - if (obj.element.siblings('[name=match_on]').attr('checked')) { + if (obj.element.parent('.elgg-user-picker').find('input[name=match_on]').attr('checked')) { return {'match_on[]': 'friends', 'term' : obj.term}; } else { return {'match_on[]': 'users', 'term' : obj.term}; } }; -elgg.register_hook_handler('init', 'system', elgg.userpicker.init); \ No newline at end of file +elgg.register_hook_handler('init', 'system', elgg.userpicker.init); -- cgit v1.2.3 From fb283378bdb2c987fc85c14aa8d644fe02996168 Mon Sep 17 00:00:00 2001 From: Sem Date: Sun, 1 Sep 2013 20:06:32 +0200 Subject: Fixed links in logbrowser table. --- mod/logbrowser/views/default/logbrowser/table.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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, )); -- cgit v1.2.3 From ec68afb872fe006f74178a679cb6b69c9ff33bc1 Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Sun, 1 Sep 2013 16:19:07 -0400 Subject: Pages/Bookmarks: Match language convention for adding an object --- mod/bookmarks/languages/en.php | 2 +- mod/pages/languages/en.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/pages/languages/en.php b/mod/pages/languages/en.php index 13b6ece2a..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', -- cgit v1.2.3 From 283106afa1fb6ff9984341b8911f90c5d4e4c4a2 Mon Sep 17 00:00:00 2001 From: Sem Date: Thu, 12 Sep 2013 00:15:52 +0200 Subject: Fixes #6052. Urldecoding usernames to allow non-alphanumeric characters. --- engine/lib/users.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/lib/users.php b/engine/lib/users.php index 9a5194896..0b4608034 100644 --- a/engine/lib/users.php +++ b/engine/lib/users.php @@ -553,7 +553,7 @@ function get_user($guid) { function get_user_by_username($username) { global $CONFIG, $USERNAME_TO_GUID_MAP_CACHE; - $username = sanitise_string($username); + $username = sanitise_string(rawurldecode($username)); $access = get_access_sql_suffix('e'); // Caching -- cgit v1.2.3 From c1ea910e3b3b0bcc27a214383c9f6355a05dd495 Mon Sep 17 00:00:00 2001 From: Paweł Sroka Date: Thu, 12 Sep 2013 05:59:18 +0200 Subject: Added function for escaping query strings and fixed several XSRF vulnerabilities. --- engine/lib/output.php | 19 +++++++++++++++++++ mod/groups/lib/groups.php | 3 ++- mod/members/pages/members/search.php | 8 ++++++-- mod/search/pages/search/index.php | 10 +--------- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/engine/lib/output.php b/engine/lib/output.php index 6172a5c8d..de4f911fb 100644 --- a/engine/lib/output.php +++ b/engine/lib/output.php @@ -420,6 +420,25 @@ function _elgg_html_decode($string) { return $string; } +/** + * Prepares query string for output to prevent CSRF attacks. + * + * @param string $string + * @return string + * + * @access private + */ +function _elgg_get_display_query($string) { + //encode <,>,&, quotes and characters above 127 + if (function_exists('mb_convert_encoding')) { + $display_query = mb_convert_encoding($string, 'HTML-ENTITIES', 'UTF-8'); + } else { + // if no mbstring extension, we just strip characters + $display_query = preg_replace("/[^\x01-\x7F]/", "", $string); + } + return htmlspecialchars($display_query, ENT_QUOTES, 'UTF-8', false); +} + /** * Unit tests for Output * diff --git a/mod/groups/lib/groups.php b/mod/groups/lib/groups.php index 77d7c09cc..aa8766e06 100644 --- a/mod/groups/lib/groups.php +++ b/mod/groups/lib/groups.php @@ -73,7 +73,8 @@ 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( 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/search/pages/search/index.php b/mod/search/pages/search/index.php index ede09329b..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) { -- cgit v1.2.3 From 0f448571e3618a2a4ef56e377ff26c6278585b48 Mon Sep 17 00:00:00 2001 From: Matt Beckett Date: Thu, 12 Sep 2013 22:10:51 -0700 Subject: collections pages are only for logged in users --- engine/lib/users.php | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/lib/users.php b/engine/lib/users.php index 9a5194896..e26b6cd4b 100644 --- a/engine/lib/users.php +++ b/engine/lib/users.php @@ -1091,6 +1091,7 @@ function friends_page_handler($segments, $handler) { * @access private */ function collections_page_handler($page_elements) { + gatekeeper(); elgg_set_context('friends'); $base = elgg_get_config('path'); if (isset($page_elements[0])) { -- cgit v1.2.3 From 25cdb9287c734dbe7fb29704f39537dd1a1a0cc0 Mon Sep 17 00:00:00 2001 From: Matt Beckett Date: Thu, 12 Sep 2013 22:13:45 -0700 Subject: spacing --- engine/lib/users.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/lib/users.php b/engine/lib/users.php index e26b6cd4b..a3813e6a8 100644 --- a/engine/lib/users.php +++ b/engine/lib/users.php @@ -1091,7 +1091,7 @@ function friends_page_handler($segments, $handler) { * @access private */ function collections_page_handler($page_elements) { - gatekeeper(); + gatekeeper(); elgg_set_context('friends'); $base = elgg_get_config('path'); if (isset($page_elements[0])) { -- cgit v1.2.3 From e02239465b538d0364043e4e8613279eb04408f8 Mon Sep 17 00:00:00 2001 From: Matt Beckett Date: Thu, 12 Sep 2013 22:20:34 -0700 Subject: stop page propagation if there is no page or container --- mod/pages/pages/pages/history.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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()); -- cgit v1.2.3 From 32f2a17bec4dc7e19cfdc5f2e5dd55b37732e910 Mon Sep 17 00:00:00 2001 From: Jeroen Dalsem Date: Fri, 20 Sep 2013 10:36:27 +0200 Subject: detect pagination needs --- engine/lib/river.php | 10 ++++++++-- engine/lib/views.php | 20 +++++++++++++++----- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/engine/lib/river.php b/engine/lib/river.php index 4926a85c4..e92040eb7 100644 --- a/engine/lib/river.php +++ b/engine/lib/river.php @@ -120,7 +120,7 @@ $posted = 0, $annotation_id = 0) { * subtypes => STR|ARR Entity subtype string(s) * type_subtype_pairs => ARR Array of type => subtype pairs where subtype * can be an array of subtype strings - * + * * posted_time_lower => INT The lower bound on the time posted * posted_time_upper => INT The upper bound on the time posted * @@ -434,8 +434,13 @@ function elgg_list_river(array $options = array()) { 'pagination' => TRUE, 'list_class' => 'elgg-list-river elgg-river', // @todo remove elgg-river in Elgg 1.9 ); - + $options = array_merge($defaults, $options); + + if (!$options["limit"] && !$options["offset"]) { + // no need for pagination if listing is unlimited + $options["pagination"] = false; + } $options['count'] = TRUE; $count = elgg_get_river($options); @@ -445,6 +450,7 @@ function elgg_list_river(array $options = array()) { $options['count'] = $count; $options['items'] = $items; + return elgg_view('page/components/list', $options); } diff --git a/engine/lib/views.php b/engine/lib/views.php index 65ba20204..7f179f572 100644 --- a/engine/lib/views.php +++ b/engine/lib/views.php @@ -218,7 +218,7 @@ function elgg_register_ajax_view($view) { /** * Unregister a view for ajax calls - * + * * @param string $view The view name * @return void * @since 1.8.3 @@ -992,6 +992,11 @@ function elgg_view_annotation(ElggAnnotation $annotation, array $vars = array(), function elgg_view_entity_list($entities, $vars = array(), $offset = 0, $limit = 10, $full_view = true, $list_type_toggle = true, $pagination = true) { + if (!$vars["limit"] && !$vars["offset"]) { + // no need for pagination if listing is unlimited + $vars["pagination"] = false; + } + if (!is_int($offset)) { $offset = (int)get_input('offset', 0); } @@ -1064,8 +1069,13 @@ function elgg_view_annotation_list($annotations, array $vars = array()) { 'full_view' => true, 'offset_key' => 'annoff', ); - + $vars = array_merge($defaults, $vars); + + if (!$vars["limit"] && !$vars["offset"]) { + // no need for pagination if listing is unlimited + $vars["pagination"] = false; + } return elgg_view('page/components/list', $vars); } @@ -1334,12 +1344,12 @@ function elgg_view_list_item($item, array $vars = array()) { /** * View one of the elgg sprite icons - * + * * Shorthand for - * + * * @param string $name The specific icon to display * @param string $class Additional class: float, float-alt, or custom class - * + * * @return string The html for displaying an icon */ function elgg_view_icon($name, $class = '') { -- cgit v1.2.3 From ee2b6351f5a759b6e713d3992c3b0c348850fecf Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Fri, 20 Sep 2013 21:02:30 -0400 Subject: Adds comment to explain URL decoding in get_user_by_username --- engine/lib/users.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/engine/lib/users.php b/engine/lib/users.php index 0b4608034..bccfb8b03 100644 --- a/engine/lib/users.php +++ b/engine/lib/users.php @@ -553,7 +553,12 @@ function get_user($guid) { function get_user_by_username($username) { global $CONFIG, $USERNAME_TO_GUID_MAP_CACHE; - $username = sanitise_string(rawurldecode($username)); + // Fixes #6052. Username is frequently sniffed from the path info, which, + // unlike $_GET, is not URL decoded. If the username was not URL encoded, + // this is harmless. + $username = rawurldecode($username); + + $username = sanitise_string($username); $access = get_access_sql_suffix('e'); // Caching -- cgit v1.2.3 From 49ab3a17173aedb8b5e3a2a228cc6cfd0a510e49 Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Fri, 20 Sep 2013 21:19:06 -0400 Subject: Test that get_user_by_username accepts URL encoded input --- engine/tests/objects/users.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/engine/tests/objects/users.php b/engine/tests/objects/users.php index dc9129326..7d2ef6961 100644 --- a/engine/tests/objects/users.php +++ b/engine/tests/objects/users.php @@ -159,6 +159,22 @@ class ElggCoreUserTest extends ElggCoreUnitTest { $this->assertFalse($user); } + public function testGetUserByUsernameAcceptsUrlEncoded() { + $username = (string)time(); + $this->user->username = $username; + $guid = $this->user->save(); + + // percent encode first letter + $first_letter = $username[0]; + $first_letter = str_pad('%' . dechex(ord($first_letter)), 2, '0', STR_PAD_LEFT); + $username = $first_letter . substr($username, 1); + + $user = get_user_by_username($username); + $this->assertTrue((bool) $user); + $this->assertEqual($guid, $user->guid); + + $this->user->delete(); + } public function testElggUserMakeAdmin() { global $CONFIG; -- cgit v1.2.3 From 1150f831637c87322fcc85cb59758805d48c90e0 Mon Sep 17 00:00:00 2001 From: Matt Beckett Date: Thu, 26 Sep 2013 13:36:17 -0700 Subject: Fix redeclaring $size variable - breaks user icon if filesize == 0 or the file somehow no longer exists --- mod/profile/icondirect.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mod/profile/icondirect.php b/mod/profile/icondirect.php index dbab5d31f..85221945a 100644 --- a/mod/profile/icondirect.php +++ b/mod/profile/icondirect.php @@ -55,8 +55,8 @@ 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"); -- cgit v1.2.3 From 12dbe2faa03281cc4c1c30d0f9635620676ac49a Mon Sep 17 00:00:00 2001 From: Matt Beckett Date: Thu, 26 Sep 2013 14:13:23 -0700 Subject: change variable name for content length --- mod/profile/icondirect.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/profile/icondirect.php b/mod/profile/icondirect.php index 85221945a..5f1599e0d 100644 --- a/mod/profile/icondirect.php +++ b/mod/profile/icondirect.php @@ -61,7 +61,7 @@ if ($mysql_dblink) { 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; -- cgit v1.2.3 From ceb9829595b62330ec0d6903287c498e6d6ee37c Mon Sep 17 00:00:00 2001 From: Jerome Bakker Date: Mon, 30 Sep 2013 10:34:22 +0200 Subject: fixes #6100 auth_gettoken now works with email address --- engine/lib/web_services.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/engine/lib/web_services.php b/engine/lib/web_services.php index b440e3afb..51cad6f39 100644 --- a/engine/lib/web_services.php +++ b/engine/lib/web_services.php @@ -1166,6 +1166,17 @@ function list_all_apis() { * @access private */ function auth_gettoken($username, $password) { + // check if username is an email address + if (is_email_address($username)) { + $users = get_user_by_email($username); + + // check if we have a unique user + if (is_array($users) && (count($users) == 1)) { + $username = $users[0]->username; + } + } + + // validate username and password if (true === elgg_authenticate($username, $password)) { $token = create_user_token($username); if ($token) { @@ -1195,7 +1206,7 @@ $ERRORS = array(); * * @return void * @access private - * + * * @throws Exception */ function _php_api_error_handler($errno, $errmsg, $filename, $linenum, $vars) { -- cgit v1.2.3 From 088eb2ce72bfe3852a19f4387e28fee8bdba69c7 Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Tue, 8 Oct 2013 08:46:01 -0400 Subject: Fix inline docs for set_input --- engine/lib/input.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/lib/input.php b/engine/lib/input.php index 2d9bae4dd..80b0b8766 100644 --- a/engine/lib/input.php +++ b/engine/lib/input.php @@ -60,8 +60,8 @@ function get_input($variable, $default = NULL, $filter_result = TRUE) { * * Note: this function does not handle nested arrays (ex: form input of param[m][n]) * - * @param string $variable The name of the variable - * @param string $value The value of the variable + * @param string $variable The name of the variable + * @param string|string[] $value The value of the variable * * @return void */ -- cgit v1.2.3 From b3cf5a302d25b06421a055f280ca4f654bd8e6a7 Mon Sep 17 00:00:00 2001 From: beck24 Date: Sun, 13 Oct 2013 00:03:11 -0700 Subject: Fixes #6177 - refuse to deactive plugins used as dependencies --- engine/lib/plugins.php | 35 +++++++++++++++++++++++++++++++++++ languages/en.php | 2 ++ 2 files changed, 37 insertions(+) diff --git a/engine/lib/plugins.php b/engine/lib/plugins.php index 74bce45fd..f0d89e92d 100644 --- a/engine/lib/plugins.php +++ b/engine/lib/plugins.php @@ -1104,6 +1104,39 @@ function plugins_test($hook, $type, $value, $params) { return $value; } +function plugins_deactivate_dependency_check($event, $type, $params) { + $plugin_id = $params['plugin_entity']->getManifest()->getPluginID(); + $plugin_name = $params['plugin_entity']->getManifest()->getName(); + + $active_plugins = elgg_get_plugins(); + + $dependents = array(); + foreach ($active_plugins as $plugin) { + $manifest = $plugin->getManifest(); + $requires = $manifest->getRequires(); + + foreach ($requires as $required) { + if ($required['type'] == 'plugin' && $required['name'] == $plugin_id) { + // there are active dependents + $dependents[$manifest->getPluginID()] = $plugin; + } + } + } + + if ($dependents) { + $list = '
    '; + // construct error message and prevent disabling + foreach ($dependents as $dependent) { + $list .= '
  • ' . $dependent->getManifest()->getName() . '
  • '; + } + $list .= '
'; + + register_error(elgg_echo('ElggPlugin:Dependencies:ActiveDependent', array($plugin_name, $list))); + + return false; + } +} + /** * Initialize the plugin system * Listens to system init and registers actions @@ -1115,6 +1148,8 @@ function plugin_init() { run_function_once("plugin_run_once"); elgg_register_plugin_hook_handler('unit_test', 'system', 'plugins_test'); + + elgg_register_event_handler('deactivate', 'plugin', 'plugins_deactivate_dependency_check'); elgg_register_action("plugins/settings/save", '', 'admin'); elgg_register_action("plugins/usersettings/save"); diff --git a/languages/en.php b/languages/en.php index 1721865f7..ad4831db7 100644 --- a/languages/en.php +++ b/languages/en.php @@ -105,6 +105,8 @@ $english = array( 'ElggPlugin:Dependencies:Priority:Before' => 'Before %s', 'ElggPlugin:Dependencies:Priority:Uninstalled' => '%s is not installed', 'ElggPlugin:Dependencies:Suggests:Unsatisfied' => 'Missing', + + 'ElggPlugin:Dependencies:ActiveDependent' => 'There are other plugins that list %s as a dependency. You must disable the following plugins before disabling this one: %s', 'ElggPlugin:InvalidAndDeactivated' => '%s is an invalid plugin and has been deactivated.', -- cgit v1.2.3 From 634216f0978d037fb84ef8e68e4e4272752c22fb Mon Sep 17 00:00:00 2001 From: beck24 Date: Sun, 13 Oct 2013 00:19:46 -0700 Subject: whitespace fix --- engine/lib/plugins.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/engine/lib/plugins.php b/engine/lib/plugins.php index f0d89e92d..e0aa705bb 100644 --- a/engine/lib/plugins.php +++ b/engine/lib/plugins.php @@ -1105,13 +1105,13 @@ function plugins_test($hook, $type, $value, $params) { } function plugins_deactivate_dependency_check($event, $type, $params) { - $plugin_id = $params['plugin_entity']->getManifest()->getPluginID(); - $plugin_name = $params['plugin_entity']->getManifest()->getName(); - - $active_plugins = elgg_get_plugins(); + $plugin_id = $params['plugin_entity']->getManifest()->getPluginID(); + $plugin_name = $params['plugin_entity']->getManifest()->getName(); + + $active_plugins = elgg_get_plugins(); $dependents = array(); - foreach ($active_plugins as $plugin) { + foreach ($active_plugins as $plugin) { $manifest = $plugin->getManifest(); $requires = $manifest->getRequires(); @@ -1121,9 +1121,9 @@ function plugins_deactivate_dependency_check($event, $type, $params) { $dependents[$manifest->getPluginID()] = $plugin; } } - } - - if ($dependents) { + } + + if ($dependents) { $list = '
    '; // construct error message and prevent disabling foreach ($dependents as $dependent) { -- cgit v1.2.3 From 6da43b70ca0de807c0532adb0bba65405d3ffbc1 Mon Sep 17 00:00:00 2001 From: beck24 Date: Sun, 13 Oct 2013 21:51:02 -0700 Subject: move deactivate event registration to the user-triggered action --- actions/admin/plugins/deactivate.php | 3 +++ engine/lib/plugins.php | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/actions/admin/plugins/deactivate.php b/actions/admin/plugins/deactivate.php index 354f4717d..adb86dd7a 100644 --- a/actions/admin/plugins/deactivate.php +++ b/actions/admin/plugins/deactivate.php @@ -10,6 +10,9 @@ * @package Elgg.Core * @subpackage Administration.Plugins */ + + // prevent disabling plugins with active dependents + elgg_register_event_handler('deactivate', 'plugin', 'plugins_deactivate_dependency_check'); $plugin_guids = get_input('plugin_guids'); diff --git a/engine/lib/plugins.php b/engine/lib/plugins.php index e0aa705bb..1b7ad5db9 100644 --- a/engine/lib/plugins.php +++ b/engine/lib/plugins.php @@ -1148,8 +1148,6 @@ function plugin_init() { run_function_once("plugin_run_once"); elgg_register_plugin_hook_handler('unit_test', 'system', 'plugins_test'); - - elgg_register_event_handler('deactivate', 'plugin', 'plugins_deactivate_dependency_check'); elgg_register_action("plugins/settings/save", '', 'admin'); elgg_register_action("plugins/usersettings/save"); -- cgit v1.2.3 From 0f18b3b09e2d033f0523736a15ba200b985ee9ac Mon Sep 17 00:00:00 2001 From: Jeroen Dalsem Date: Mon, 21 Oct 2013 08:36:22 +0200 Subject: forward to all if no group archive requested --- mod/blog/start.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/blog/start.php b/mod/blog/start.php index 91525acee..e724b91c2 100644 --- a/mod/blog/start.php +++ b/mod/blog/start.php @@ -152,7 +152,7 @@ function blog_page_handler($page) { if (!elgg_instanceof($group, 'group')) { forward('', '404'); } - if ($page[2] == 'all') { + 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]); -- cgit v1.2.3 From 5a3095d0664dc8c1fa81c7c2e6fac6a8dc8f0eaf Mon Sep 17 00:00:00 2001 From: Jerome Bakker Date: Tue, 22 Oct 2013 14:35:38 +0200 Subject: fixed a unittest I broke in #398 --- engine/tests/api/entity_getter_functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/tests/api/entity_getter_functions.php b/engine/tests/api/entity_getter_functions.php index 0492b1fb0..fef9dc0c5 100644 --- a/engine/tests/api/entity_getter_functions.php +++ b/engine/tests/api/entity_getter_functions.php @@ -426,7 +426,7 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest { $options = array( 'types' => $types, - 'subtype' => $subtype + 'subtypes' => $subtype ); $es = elgg_get_entities($options); -- cgit v1.2.3 From 0d3c2bcf71a493cb5b0923227a2b043a09762fdb Mon Sep 17 00:00:00 2001 From: Jeroen Dalsem Date: Tue, 22 Oct 2013 16:46:06 +0200 Subject: remove thumbnails if no longer needed --- mod/file/actions/file/upload.php | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/mod/file/actions/file/upload.php b/mod/file/actions/file/upload.php index 7ee402121..e20c4079f 100644 --- a/mod/file/actions/file/upload.php +++ b/mod/file/actions/file/upload.php @@ -165,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 @@ -202,4 +219,4 @@ if ($new_file) { } forward($file->getURL()); -} +} -- cgit v1.2.3 From ad4473263c578918dfe93e6fbf7debc70727e486 Mon Sep 17 00:00:00 2001 From: Jerome Bakker Date: Wed, 23 Oct 2013 14:56:20 +0200 Subject: changed the bugtracker url from the developers plugin to GitHub --- mod/developers/manifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 @@ Developer tools for Elgg A set of tools for writing plugins and themes. It is recommended that you have this plugin at the top of the plugin list. http://www.elgg.org/ - http://trac.elgg.org + https://github.com/Elgg/Elgg/issues See COPYRIGHT.txt GNU General Public License version 2 -- cgit v1.2.3 From 577201be6cff3dbe2a0ee6e35c9ab33a74a55f77 Mon Sep 17 00:00:00 2001 From: Jeroen Dalsem Date: Wed, 23 Oct 2013 15:11:11 +0200 Subject: fixed issue when reordering plugins to second last position of other column that has more than 2 widgets --- engine/classes/ElggWidget.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/engine/classes/ElggWidget.php b/engine/classes/ElggWidget.php index c123e5032..66191bf47 100644 --- a/engine/classes/ElggWidget.php +++ b/engine/classes/ElggWidget.php @@ -146,10 +146,15 @@ class ElggWidget extends ElggObject { } } + $bottom_rank = count($widgets); + if ($column == $this->column) { + $bottom_rank--; + } + if ($rank == 0) { // top of the column $this->order = reset($widgets)->order - 10; - } elseif ($rank == (count($widgets) - 1)) { + } elseif ($rank == $bottom_rank) { // bottom of the column of active widgets $this->order = end($widgets)->order + 10; } else { -- cgit v1.2.3 From 424eff09557bf5e0cee7f0c1a717b3992d2e82ac Mon Sep 17 00:00:00 2001 From: Jerome Bakker Date: Wed, 23 Oct 2013 15:35:05 +0200 Subject: replaced all references to trac.elgg.org to the correct GitHub issues --- documentation/info/manifest.xml | 2 +- engine/classes/ElggAttributeLoader.php | 12 ++++++------ engine/classes/ElggEntity.php | 12 ++++++------ engine/lib/database.php | 12 ++++++------ engine/lib/entities.php | 2 +- engine/lib/upgrade.php | 2 +- engine/lib/upgrades/2010033101.php | 2 +- ...012041801-1.8.3-multiple_user_tokens-852225f7fd89f6c5.php | 2 +- engine/lib/views.php | 2 +- engine/tests/api/helpers.php | 2 +- engine/tests/api/metadata.php | 2 +- engine/tests/api/plugins.php | 4 ++-- engine/tests/objects/entities.php | 2 +- engine/tests/objects/objects.php | 2 +- engine/tests/regression/trac_bugs.php | 10 +++++----- engine/tests/test_files/plugin_18/manifest.xml | 2 +- mod/search/README.txt | 2 +- upgrade.php | 2 +- views/default/js/elgg.php | 2 +- 19 files changed, 39 insertions(+), 39 deletions(-) diff --git a/documentation/info/manifest.xml b/documentation/info/manifest.xml index 494158481..4fd4be8ce 100644 --- a/documentation/info/manifest.xml +++ b/documentation/info/manifest.xml @@ -7,7 +7,7 @@ This is a longer, more interesting description of my plugin, its features, and other important information. http://www.elgg.org/ https://github.com/Elgg/Elgg - http://trac.elgg.org + https://github.com/Elgg/Elgg/issues http://elgg.org/supporter.php (C) Elgg 2011 GNU General Public License version 2 diff --git a/engine/classes/ElggAttributeLoader.php b/engine/classes/ElggAttributeLoader.php index 0b770da75..ffc80b02d 100644 --- a/engine/classes/ElggAttributeLoader.php +++ b/engine/classes/ElggAttributeLoader.php @@ -4,7 +4,7 @@ * Loads ElggEntity attributes from DB or validates those passed in via constructor * * @access private - * + * * @package Elgg.Core * @subpackage DataModel */ @@ -69,7 +69,7 @@ class ElggAttributeLoader { /** * Constructor - * + * * @param string $class class of object being loaded * @param string $required_type entity type this is being used to populate * @param array $initialized_attrs attributes after initializeAttributes() has been run @@ -94,7 +94,7 @@ class ElggAttributeLoader { /** * Get primary attributes missing that are missing - * + * * @param stdClass $row Database row * @return array */ @@ -104,7 +104,7 @@ class ElggAttributeLoader { /** * Get secondary attributes that are missing - * + * * @param stdClass $row Database row * @return array */ @@ -114,7 +114,7 @@ class ElggAttributeLoader { /** * Check that the type is correct - * + * * @param stdClass $row Database row * @return void * @throws InvalidClassException @@ -216,7 +216,7 @@ class ElggAttributeLoader { // Note: If there are still missing attributes, we're running on a 1.7 or earlier schema. We let // this pass so the upgrades can run. - // guid needs to be an int http://trac.elgg.org/ticket/4111 + // guid needs to be an int https://github.com/elgg/elgg/issues/4111 $row['guid'] = (int) $row['guid']; return $row; diff --git a/engine/classes/ElggEntity.php b/engine/classes/ElggEntity.php index dd1c7c114..a563f6fad 100644 --- a/engine/classes/ElggEntity.php +++ b/engine/classes/ElggEntity.php @@ -24,7 +24,7 @@ * * @package Elgg.Core * @subpackage DataModel.Entities - * + * * @property string $type object, user, group, or site (read-only after save) * @property string $subtype Further clarifies the nature of the entity (read-only after save) * @property int $guid The unique identifier for this entity (read only) @@ -352,8 +352,8 @@ abstract class ElggEntity extends ElggData implements 'limit' => 0 ); // @todo in 1.9 make this return false if can't add metadata - // http://trac.elgg.org/ticket/4520 - // + // https://github.com/elgg/elgg/issues/4520 + // // need to remove access restrictions right now to delete // because this is the expected behavior $ia = elgg_set_ignore_access(true); @@ -379,7 +379,7 @@ abstract class ElggEntity extends ElggData implements // unsaved entity. store in temp array // returning single entries instead of an array of 1 element is decided in // getMetaData(), just like pulling from the db. - // + // // if overwrite, delete first if (!$multiple || !isset($this->temp_metadata[$name])) { $this->temp_metadata[$name] = array(); @@ -964,7 +964,7 @@ abstract class ElggEntity extends ElggData implements * * @tip Can be overridden by registering for the permissions_check:comment, * plugin hook. - * + * * @param int $user_guid User guid (default is logged in user) * * @return bool @@ -1365,7 +1365,7 @@ abstract class ElggEntity extends ElggData implements $this->attributes['tables_loaded']++; } - // guid needs to be an int http://trac.elgg.org/ticket/4111 + // guid needs to be an int https://github.com/elgg/elgg/issues/4111 $this->attributes['guid'] = (int)$this->attributes['guid']; // Cache object handle diff --git a/engine/lib/database.php b/engine/lib/database.php index 37dfb8f8d..a7949788d 100644 --- a/engine/lib/database.php +++ b/engine/lib/database.php @@ -129,7 +129,7 @@ function establish_db_link($dblinkname = "readwrite") { // Set up cache if global not initialized and query cache not turned off if ((!$DB_QUERY_CACHE) && (!$db_cache_off)) { // @todo if we keep this cache in 1.9, expose the size as a config parameter - $DB_QUERY_CACHE = new ElggLRUCache(200); + $DB_QUERY_CACHE = new ElggLRUCache(200); } } @@ -399,14 +399,14 @@ function elgg_query_runner($query, $callback = null, $single = false) { // Since we want to cache results of running the callback, we need to // need to namespace the query with the callback and single result request. - // http://trac.elgg.org/ticket/4049 + // https://github.com/elgg/elgg/issues/4049 $hash = (string)$callback . (int)$single . $query; // Is cached? if ($DB_QUERY_CACHE) { if (isset($DB_QUERY_CACHE[$hash])) { elgg_log("DB query $query results returned from cache (hash: $hash)", 'NOTICE'); - return $DB_QUERY_CACHE[$hash]; + return $DB_QUERY_CACHE[$hash]; } } @@ -524,7 +524,7 @@ function delete_data($query) { /** * Invalidate the query cache - * + * * @access private */ function _elgg_invalidate_query_cache() { @@ -533,7 +533,7 @@ function _elgg_invalidate_query_cache() { $DB_QUERY_CACHE->clear(); elgg_log("Query cache invalidated", 'NOTICE'); } elseif ($DB_QUERY_CACHE) { - // In case someone sets the cache to an array and primes it with data + // In case someone sets the cache to an array and primes it with data $DB_QUERY_CACHE = array(); elgg_log("Query cache invalidated", 'NOTICE'); } @@ -668,7 +668,7 @@ function run_sql_script($scriptlocation) { /** * Format a query string for logging - * + * * @param string $query Query string * @return string * @access private diff --git a/engine/lib/entities.php b/engine/lib/entities.php index 997db79d2..4fcf1c657 100644 --- a/engine/lib/entities.php +++ b/engine/lib/entities.php @@ -791,7 +791,7 @@ function get_entity($guid) { if ($shared_cache) { $cached_entity = $shared_cache->load($guid); - // @todo store ACLs in memcache http://trac.elgg.org/ticket/3018#comment:3 + // @todo store ACLs in memcache https://github.com/elgg/elgg/issues/3018#issuecomment-13662617 if ($cached_entity) { // @todo use ACL and cached entity access_id to determine if user can see it return $cached_entity; diff --git a/engine/lib/upgrade.php b/engine/lib/upgrade.php index 0cc1e64dc..158ec9ec1 100644 --- a/engine/lib/upgrade.php +++ b/engine/lib/upgrade.php @@ -245,7 +245,7 @@ function version_upgrade() { // No version number? Oh snap...this is an upgrade from a clean installation < 1.7. // Run all upgrades without error reporting and hope for the best. - // See http://trac.elgg.org/elgg/ticket/1432 for more. + // See https://github.com/elgg/elgg/issues/1432 for more. $quiet = !$dbversion; // Note: Database upgrades are deprecated as of 1.8. Use code upgrades. See #1433 diff --git a/engine/lib/upgrades/2010033101.php b/engine/lib/upgrades/2010033101.php index 0bffee001..4779295fd 100644 --- a/engine/lib/upgrades/2010033101.php +++ b/engine/lib/upgrades/2010033101.php @@ -1,7 +1,7 @@ container_guid); diff --git a/engine/tests/api/helpers.php b/engine/tests/api/helpers.php index 10216140f..414fb4145 100644 --- a/engine/tests/api/helpers.php +++ b/engine/tests/api/helpers.php @@ -519,7 +519,7 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest { $this->assertIdentical($elements_sorted_string, $test_elements); } - // see http://trac.elgg.org/ticket/4288 + // see https://github.com/elgg/elgg/issues/4288 public function testElggBatchIncOffset() { // normal increment $options = array( diff --git a/engine/tests/api/metadata.php b/engine/tests/api/metadata.php index 0862341c1..d23510c6a 100644 --- a/engine/tests/api/metadata.php +++ b/engine/tests/api/metadata.php @@ -139,7 +139,7 @@ class ElggCoreMetadataAPITest extends ElggCoreUnitTest { // Make sure metadata with multiple values is correctly deleted when re-written // by another user - // http://trac.elgg.org/ticket/2776 + // https://github.com/elgg/elgg/issues/2776 public function test_elgg_metadata_multiple_values() { $u1 = new ElggUser(); $u1->username = rand(); diff --git a/engine/tests/api/plugins.php b/engine/tests/api/plugins.php index 114f3991b..d0f111c48 100644 --- a/engine/tests/api/plugins.php +++ b/engine/tests/api/plugins.php @@ -69,7 +69,7 @@ class ElggCorePluginsAPITest extends ElggCoreUnitTest { 'description' => 'A longer, more interesting description.', 'website' => 'http://www.elgg.org/', 'repository' => 'https://github.com/Elgg/Elgg', - 'bugtracker' => 'http://trac.elgg.org', + 'bugtracker' => 'https://github.com/elgg/elgg/issues', 'donations' => 'http://elgg.org/supporter.php', 'copyright' => '(C) Elgg Foundation 2011', 'license' => 'GNU General Public License version 2', @@ -174,7 +174,7 @@ class ElggCorePluginsAPITest extends ElggCoreUnitTest { } public function testElggPluginManifestGetBugtracker() { - $this->assertEqual($this->manifest18->getBugTrackerURL(), 'http://trac.elgg.org'); + $this->assertEqual($this->manifest18->getBugTrackerURL(), 'https://github.com/elgg/elgg/issues'); $this->assertEqual($this->manifest17->getBugTrackerURL(), ''); } diff --git a/engine/tests/objects/entities.php b/engine/tests/objects/entities.php index 248b85c9e..bac72079e 100644 --- a/engine/tests/objects/entities.php +++ b/engine/tests/objects/entities.php @@ -271,7 +271,7 @@ class ElggCoreEntityTest extends ElggCoreUnitTest { $this->save_entity(); // test deleting incorrectly - // @link http://trac.elgg.org/ticket/2273 + // @link https://github.com/elgg/elgg/issues/2273 $this->assertNull($this->entity->deleteMetadata('impotent')); $this->assertEqual($this->entity->important, 'indeed!'); diff --git a/engine/tests/objects/objects.php b/engine/tests/objects/objects.php index 915594e0a..263ab2414 100644 --- a/engine/tests/objects/objects.php +++ b/engine/tests/objects/objects.php @@ -194,7 +194,7 @@ class ElggCoreObjectTest extends ElggCoreUnitTest { $old = elgg_set_ignore_access(true); } - // see http://trac.elgg.org/ticket/1196 + // see https://github.com/elgg/elgg/issues/1196 public function testElggEntityRecursiveDisableWhenLoggedOut() { $e1 = new ElggObject(); $e1->access_id = ACCESS_PUBLIC; diff --git a/engine/tests/regression/trac_bugs.php b/engine/tests/regression/trac_bugs.php index f173b5b9f..9372b0855 100644 --- a/engine/tests/regression/trac_bugs.php +++ b/engine/tests/regression/trac_bugs.php @@ -201,8 +201,8 @@ class ElggCoreRegressionBugsTest extends ElggCoreUnitTest { } /** - * http://trac.elgg.org/ticket/3210 - Don't remove -s in friendly titles - * http://trac.elgg.org/ticket/2276 - improve char encoding + * https://github.com/elgg/elgg/issues/3210 - Don't remove -s in friendly titles + * https://github.com/elgg/elgg/issues/2276 - improve char encoding */ public function test_friendly_title() { $cases = array( @@ -216,7 +216,7 @@ class ElggCoreRegressionBugsTest extends ElggCoreUnitTest { => "a-a-a-a-a-a-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // separators trimmed - "-_ hello _-" + "-_ hello _-" => "hello", // accents removed, lower case, other multibyte chars are URL encoded @@ -286,7 +286,7 @@ class ElggCoreRegressionBugsTest extends ElggCoreUnitTest { 'web archive anchor google' => 'web archive anchor google', - 'single quotes already anchor yahoo' => + 'single quotes already anchor yahoo' => 'single quotes already anchor yahoo', 'unquoted already anchor yahoo' => @@ -302,7 +302,7 @@ class ElggCoreRegressionBugsTest extends ElggCoreUnitTest { /** * Ensure additional select columns do not end up in entity attributes. - * + * * https://github.com/Elgg/Elgg/issues/5538 */ public function test_extra_columns_dont_appear_in_attributes() { diff --git a/engine/tests/test_files/plugin_18/manifest.xml b/engine/tests/test_files/plugin_18/manifest.xml index 5d788616a..c8b407511 100644 --- a/engine/tests/test_files/plugin_18/manifest.xml +++ b/engine/tests/test_files/plugin_18/manifest.xml @@ -7,7 +7,7 @@ A longer, more interesting description. http://www.elgg.org/ https://github.com/Elgg/Elgg - http://trac.elgg.org + https://github.com/elgg/elgg/issues http://elgg.org/supporter.php (C) Elgg Foundation 2011 GNU General Public License version 2 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/upgrade.php b/upgrade.php index c5f158c61..d07b2a1da 100644 --- a/upgrade.php +++ b/upgrade.php @@ -46,7 +46,7 @@ if (get_input('upgrade') == 'upgrade') { } else { // if upgrading from < 1.8.0, check for the core view 'welcome' and bail if it's found. - // see http://trac.elgg.org/ticket/3064 + // see https://github.com/elgg/elgg/issues/3064 // we're not checking the view itself because it's likely themes will override this view. // we're only concerned with core files. $welcome = dirname(__FILE__) . '/views/default/welcome.php'; diff --git a/views/default/js/elgg.php b/views/default/js/elgg.php index 6fe03484d..c3b56e398 100644 --- a/views/default/js/elgg.php +++ b/views/default/js/elgg.php @@ -43,7 +43,7 @@ $libs = array( foreach ($libs as $file) { include("{$CONFIG->path}js/lib/$file.js"); - // putting a new line between the files to address http://trac.elgg.org/ticket/3081 + // putting a new line between the files to address https://github.com/elgg/elgg/issues/3081 echo "\n"; } -- cgit v1.2.3 From 0deb80da1e82af55bf8d7500d09b36225ddd7927 Mon Sep 17 00:00:00 2001 From: Jerome Bakker Date: Wed, 23 Oct 2013 16:03:08 +0200 Subject: found some more references to trac --- engine/lib/elgglib.php | 4 ++-- engine/lib/views.php | 4 ++-- engine/tests/objects/users.php | 2 +- engine/tests/regression/trac_bugs.php | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/engine/lib/elgglib.php b/engine/lib/elgglib.php index b5ef7e572..c95e0c28c 100644 --- a/engine/lib/elgglib.php +++ b/engine/lib/elgglib.php @@ -1350,7 +1350,7 @@ function full_url() { "" : (":" . $_SERVER["SERVER_PORT"]); // This is here to prevent XSS in poorly written browsers used by 80% of the population. - // {@trac [5813]} + // https://github.com/Elgg/Elgg/commit/0c947e80f512cb0a482b1864fd0a6965c8a0cd4a $quotes = array('\'', '"'); $encoded = array('%27', '%22'); @@ -2249,7 +2249,7 @@ function elgg_api_test($hook, $type, $value, $params) { * * @warning ACCESS_DEFAULT is a place holder for the input/access view. Do not * use it when saving an entity. - * + * * @var int */ define('ACCESS_DEFAULT', -1); diff --git a/engine/lib/views.php b/engine/lib/views.php index dc69395c6..fff3581cf 100644 --- a/engine/lib/views.php +++ b/engine/lib/views.php @@ -369,7 +369,7 @@ function elgg_view_exists($view, $viewtype = '', $recurse = true) { * view, $view_name plugin hook. * * @warning Any variables in $_SESSION will override passed vars - * upon name collision. See {@trac #2124}. + * upon name collision. See https://github.com/Elgg/Elgg/issues/2124 * * @param string $view The name and location of the view to use * @param array $vars Variables to pass to the view. @@ -795,7 +795,7 @@ function elgg_view_menu($menu_name, array $vars = array()) { * - bool 'full_view' Whether to show a full or condensed view. * * @tip This function can automatically appends annotations to entities if in full - * view and a handler is registered for the entity:annotate. See {@trac 964} and + * view and a handler is registered for the entity:annotate. See https://github.com/Elgg/Elgg/issues/964 and * {@link elgg_view_entity_annotations()}. * * @param ElggEntity $entity The entity to display diff --git a/engine/tests/objects/users.php b/engine/tests/objects/users.php index 7d2ef6961..8a1033ac4 100644 --- a/engine/tests/objects/users.php +++ b/engine/tests/objects/users.php @@ -145,7 +145,7 @@ class ElggCoreUserTest extends ElggCoreUnitTest { } public function testElggUserNameCache() { - // Trac #1305 + // issue https://github.com/elgg/elgg/issues/1305 // very unlikely a user would have this username $name = (string)time(); diff --git a/engine/tests/regression/trac_bugs.php b/engine/tests/regression/trac_bugs.php index 9372b0855..f823825ab 100644 --- a/engine/tests/regression/trac_bugs.php +++ b/engine/tests/regression/trac_bugs.php @@ -1,7 +1,7 @@ Date: Wed, 23 Oct 2013 23:42:30 -0700 Subject: Revert "move deactivate event registration to the user-triggered action" This reverts commit 6da43b70ca0de807c0532adb0bba65405d3ffbc1. --- actions/admin/plugins/deactivate.php | 3 --- engine/lib/plugins.php | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/actions/admin/plugins/deactivate.php b/actions/admin/plugins/deactivate.php index adb86dd7a..354f4717d 100644 --- a/actions/admin/plugins/deactivate.php +++ b/actions/admin/plugins/deactivate.php @@ -10,9 +10,6 @@ * @package Elgg.Core * @subpackage Administration.Plugins */ - - // prevent disabling plugins with active dependents - elgg_register_event_handler('deactivate', 'plugin', 'plugins_deactivate_dependency_check'); $plugin_guids = get_input('plugin_guids'); diff --git a/engine/lib/plugins.php b/engine/lib/plugins.php index 1b7ad5db9..e0aa705bb 100644 --- a/engine/lib/plugins.php +++ b/engine/lib/plugins.php @@ -1148,6 +1148,8 @@ function plugin_init() { run_function_once("plugin_run_once"); elgg_register_plugin_hook_handler('unit_test', 'system', 'plugins_test'); + + elgg_register_event_handler('deactivate', 'plugin', 'plugins_deactivate_dependency_check'); elgg_register_action("plugins/settings/save", '', 'admin'); elgg_register_action("plugins/usersettings/save"); -- cgit v1.2.3 From 9762edd4305ab8e6523d2f8171a32688295f1c88 Mon Sep 17 00:00:00 2001 From: Matt Beckett Date: Wed, 23 Oct 2013 23:52:53 -0700 Subject: Added comments regarding deactivation due to error --- engine/lib/plugins.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/engine/lib/plugins.php b/engine/lib/plugins.php index e0aa705bb..c296346fc 100644 --- a/engine/lib/plugins.php +++ b/engine/lib/plugins.php @@ -1149,6 +1149,8 @@ function plugin_init() { elgg_register_plugin_hook_handler('unit_test', 'system', 'plugins_test'); + // note - plugins are booted by the time this handler is registered + // deactivation due to error may have already occurred elgg_register_event_handler('deactivate', 'plugin', 'plugins_deactivate_dependency_check'); elgg_register_action("plugins/settings/save", '', 'admin'); -- cgit v1.2.3 From 1e884c6abe933468ecfa035780bd3e9ff5e1ad61 Mon Sep 17 00:00:00 2001 From: Matt Beckett Date: Thu, 10 Oct 2013 13:29:50 -0700 Subject: #5952 - fix infinite loop when disabling > 50 annotations or metadata --- engine/lib/annotations.php | 6 +++++- engine/lib/metadata.php | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/engine/lib/annotations.php b/engine/lib/annotations.php index 124e67e0f..5e9b530de 100644 --- a/engine/lib/annotations.php +++ b/engine/lib/annotations.php @@ -249,9 +249,13 @@ function elgg_disable_annotations(array $options) { if (!elgg_is_valid_options_for_batch_operation($options, 'annotations')) { return false; } + + // if we can see hidden (disabled) we need to use the offset + // otherwise we risk an infinite loop if there are more than 50 + $inc_offset = access_get_show_hidden_status(); $options['metastring_type'] = 'annotations'; - return elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', false); + return elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', $inc_offset); } /** diff --git a/engine/lib/metadata.php b/engine/lib/metadata.php index d2f8d4cd4..fdb1b85f6 100644 --- a/engine/lib/metadata.php +++ b/engine/lib/metadata.php @@ -333,9 +333,13 @@ function elgg_disable_metadata(array $options) { } elgg_get_metadata_cache()->invalidateByOptions('disable', $options); + + // if we can see hidden (disabled) we need to use the offset + // otherwise we risk an infinite loop if there are more than 50 + $inc_offset = access_get_show_hidden_status(); $options['metastring_type'] = 'metadata'; - return elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', false); + return elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', $inc_offset); } /** -- cgit v1.2.3 From 8a79dc3904a2fda8081216fdf52be6a462db2709 Mon Sep 17 00:00:00 2001 From: Paweł Sroka Date: Sun, 27 Oct 2013 12:17:05 +0100 Subject: Refs #5952 - Added test --- engine/tests/regression/trac_bugs.php | 41 +++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/engine/tests/regression/trac_bugs.php b/engine/tests/regression/trac_bugs.php index f823825ab..ef1348cf6 100644 --- a/engine/tests/regression/trac_bugs.php +++ b/engine/tests/regression/trac_bugs.php @@ -332,4 +332,45 @@ class ElggCoreRegressionBugsTest extends ElggCoreUnitTest { $group->delete(); } + + /** + * Ensure that ElggBatch doesn't go into infinite loop when disabling annotations recursively when show hidden is enabled. + * + * https://github.com/Elgg/Elgg/issues/5952 + */ + public function test_disabling_annotations_infinite_loop() { + + //let's have some entity + $group = new ElggGroup(); + $group->name = 'test_group'; + $group->access_id = ACCESS_PUBLIC; + $this->assertTrue($group->save() !== false); + + $total = 51; + //add some annotations + for ($cnt = 0; $cnt < $total; $cnt++) { + $group->annotate('test_annotation', 'value_' . $total); + } + + //disable them + $show_hidden = access_get_show_hidden_status(); + access_show_hidden_entities(true); + $options = array( + 'guid' => $group->guid, + 'limit' => $total, //using strict limit to avoid real infinite loop and just see ElggBatch limiting on it before finishing the work + ); + elgg_disable_annotations($options); + access_show_hidden_entities($show_hidden); + + //confirm all being disabled + $annotations = $group->getAnnotations(array( + 'limit' => $total, + )); + foreach ($annotations as $annotation) { + $this->assertTrue($annotation->enabled == 'no'); + } + + //delete group and annotations + $group->delete(); + } } -- cgit v1.2.3 From 61d049487a4f1a72f79f8c49bb65ffa82825e378 Mon Sep 17 00:00:00 2001 From: Paweł Sroka Date: Sun, 27 Oct 2013 13:28:16 +0100 Subject: Refs #6172 - Fixed one more trac mention --- engine/lib/elgglib.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/lib/elgglib.php b/engine/lib/elgglib.php index c95e0c28c..34111c69d 100644 --- a/engine/lib/elgglib.php +++ b/engine/lib/elgglib.php @@ -746,7 +746,7 @@ function elgg_unregister_event_handler($event, $object_type, $callback) { * @tip When referring to events, the preferred syntax is "event, type". * * @internal Only rarely should events be changed, added, or removed in core. - * When making changes to events, be sure to first create a ticket in trac. + * When making changes to events, be sure to first create a ticket on Github. * * @internal @tip Think of $object_type as the primary namespace element, and * $event as the secondary namespace. -- cgit v1.2.3 From 33260fd7a88e5e92fbee6ee0719ab4286e9ce221 Mon Sep 17 00:00:00 2001 From: Paweł Sroka Date: Sun, 27 Oct 2013 19:27:14 +0100 Subject: Refs #6117 - Added docs + changed plugins_deactivate_dependency_check to _plugins_deactivate_dependency_check --- engine/lib/plugins.php | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/engine/lib/plugins.php b/engine/lib/plugins.php index c296346fc..d5d3db466 100644 --- a/engine/lib/plugins.php +++ b/engine/lib/plugins.php @@ -1104,7 +1104,17 @@ function plugins_test($hook, $type, $value, $params) { return $value; } -function plugins_deactivate_dependency_check($event, $type, $params) { +/** + * Checks on deactivate plugin event if disabling it won't create unmet dependencies and blocks disable in such case. + * + * @param string $event deactivate + * @param string $type plugin + * @param array $params Parameters array containing entry with ELggPlugin instance under 'plugin_entity' key + * @return bool false to block plugin deactivation action + * + * @access private + */ +function _plugins_deactivate_dependency_check($event, $type, $params) { $plugin_id = $params['plugin_entity']->getManifest()->getPluginID(); $plugin_name = $params['plugin_entity']->getManifest()->getName(); @@ -1114,7 +1124,7 @@ function plugins_deactivate_dependency_check($event, $type, $params) { foreach ($active_plugins as $plugin) { $manifest = $plugin->getManifest(); $requires = $manifest->getRequires(); - + foreach ($requires as $required) { if ($required['type'] == 'plugin' && $required['name'] == $plugin_id) { // there are active dependents @@ -1130,11 +1140,11 @@ function plugins_deactivate_dependency_check($event, $type, $params) { $list .= '
  • ' . $dependent->getManifest()->getName() . '
  • '; } $list .= '
'; - + register_error(elgg_echo('ElggPlugin:Dependencies:ActiveDependent', array($plugin_name, $list))); - + return false; - } + } } /** @@ -1151,7 +1161,7 @@ function plugin_init() { // note - plugins are booted by the time this handler is registered // deactivation due to error may have already occurred - elgg_register_event_handler('deactivate', 'plugin', 'plugins_deactivate_dependency_check'); + elgg_register_event_handler('deactivate', 'plugin', '_plugins_deactivate_dependency_check'); elgg_register_action("plugins/settings/save", '', 'admin'); elgg_register_action("plugins/usersettings/save"); -- cgit v1.2.3 From 5a6b259a320256c2f75828193aa8da93eebe226e Mon Sep 17 00:00:00 2001 From: Juho Jaakkola Date: Thu, 31 Oct 2013 17:01:15 +0200 Subject: Fixes #6030 - Hide group bookmarklet from non-members --- mod/bookmarks/start.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mod/bookmarks/start.php b/mod/bookmarks/start.php index a5685388b..caea43587 100644 --- a/mod/bookmarks/start.php +++ b/mod/bookmarks/start.php @@ -285,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'); -- cgit v1.2.3 From d53447f7e6b3277f3249d9a70e56ec01a90c3a60 Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Thu, 11 Jul 2013 13:24:01 -0400 Subject: Disable loading external entities during XML parsing --- engine/classes/ElggAutoP.php | 14 ++++++++++++++ engine/classes/ElggXMLElement.php | 8 ++++++-- engine/tests/regression/trac_bugs.php | 10 ++++++++++ engine/tests/test_files/xxe/external_entity.txt | 1 + engine/tests/test_files/xxe/request.xml | 8 ++++++++ 5 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 engine/tests/test_files/xxe/external_entity.txt create mode 100644 engine/tests/test_files/xxe/request.xml diff --git a/engine/classes/ElggAutoP.php b/engine/classes/ElggAutoP.php index 71536c433..05842d1b2 100644 --- a/engine/classes/ElggAutoP.php +++ b/engine/classes/ElggAutoP.php @@ -110,12 +110,19 @@ class ElggAutoP { // http://www.php.net/manual/en/domdocument.loadhtml.php#95463 libxml_use_internal_errors(true); + // Do not load entities. May be unnecessary, better safe than sorry + $disable_load_entities = libxml_disable_entity_loader(true); + if (!$this->_doc->loadHTML("{$html}" . "")) { + + libxml_disable_entity_loader($disable_load_entities); return false; } + libxml_disable_entity_loader($disable_load_entities); + $this->_xpath = new DOMXPath($this->_doc); // start processing recursively at the BODY element $nodeList = $this->_xpath->query('//body[1]'); @@ -135,9 +142,16 @@ class ElggAutoP { // re-parse so we can handle new AUTOP elements + // Do not load entities. May be unnecessary, better safe than sorry + $disable_load_entities = libxml_disable_entity_loader(true); + if (!$this->_doc->loadHTML($html)) { + libxml_disable_entity_loader($disable_load_entities); return false; } + + libxml_disable_entity_loader($disable_load_entities); + // must re-create XPath object after DOM load $this->_xpath = new DOMXPath($this->_doc); diff --git a/engine/classes/ElggXMLElement.php b/engine/classes/ElggXMLElement.php index 6f2633e25..cbd3fc5ce 100644 --- a/engine/classes/ElggXMLElement.php +++ b/engine/classes/ElggXMLElement.php @@ -20,7 +20,12 @@ class ElggXMLElement { if ($xml instanceof SimpleXMLElement) { $this->_element = $xml; } else { + // do not load entities + $disable_load_entities = libxml_disable_entity_loader(true); + $this->_element = new SimpleXMLElement($xml); + + libxml_disable_entity_loader($disable_load_entities); } } @@ -123,5 +128,4 @@ class ElggXMLElement { } return false; } - -} \ No newline at end of file +} diff --git a/engine/tests/regression/trac_bugs.php b/engine/tests/regression/trac_bugs.php index ef1348cf6..e6773c8af 100644 --- a/engine/tests/regression/trac_bugs.php +++ b/engine/tests/regression/trac_bugs.php @@ -373,4 +373,14 @@ class ElggCoreRegressionBugsTest extends ElggCoreUnitTest { //delete group and annotations $group->delete(); } + + public function test_ElggXMLElement_does_not_load_external_entities() { + $payload = file_get_contents(dirname(dirname(__FILE__)) . '/test_files/xxe/request.xml'); + $payload = sprintf($payload, 'file://' . realpath(dirname(dirname(__FILE__)) . '/test_files/xxe/external_entity.txt')); + + $el = new ElggXMLElement($payload); + $chidren = $el->getChildren(); + $content = $chidren[0]->getContent(); + $this->assertNoPattern('/secret/', $content); + } } diff --git a/engine/tests/test_files/xxe/external_entity.txt b/engine/tests/test_files/xxe/external_entity.txt new file mode 100644 index 000000000..536aca34d --- /dev/null +++ b/engine/tests/test_files/xxe/external_entity.txt @@ -0,0 +1 @@ +secret \ No newline at end of file diff --git a/engine/tests/test_files/xxe/request.xml b/engine/tests/test_files/xxe/request.xml new file mode 100644 index 000000000..4390f9db2 --- /dev/null +++ b/engine/tests/test_files/xxe/request.xml @@ -0,0 +1,8 @@ + + + +]> + + test&xxe;test + -- cgit v1.2.3 From 6eec301f33ff3e618d591d429de7edf30277e972 Mon Sep 17 00:00:00 2001 From: Paweł Sroka Date: Tue, 23 Jul 2013 08:28:30 +0200 Subject: Enhanced test --- engine/tests/regression/trac_bugs.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/engine/tests/regression/trac_bugs.php b/engine/tests/regression/trac_bugs.php index e6773c8af..ea39253df 100644 --- a/engine/tests/regression/trac_bugs.php +++ b/engine/tests/regression/trac_bugs.php @@ -375,12 +375,26 @@ class ElggCoreRegressionBugsTest extends ElggCoreUnitTest { } public function test_ElggXMLElement_does_not_load_external_entities() { + $elLast = libxml_disable_entity_loader(false); + $payload = file_get_contents(dirname(dirname(__FILE__)) . '/test_files/xxe/request.xml'); - $payload = sprintf($payload, 'file://' . realpath(dirname(dirname(__FILE__)) . '/test_files/xxe/external_entity.txt')); + $path = realpath(dirname(dirname(__FILE__)) . '/test_files/xxe/external_entity.txt'); + $path = str_replace('\\', '/', $path); + if ($path[0] != '/') { + $path = '/' . $path; + } + $path = 'file://' . $path; + $payload = sprintf($payload, $path); $el = new ElggXMLElement($payload); $chidren = $el->getChildren(); $content = $chidren[0]->getContent(); $this->assertNoPattern('/secret/', $content); + + //make sure the test is valid + $element = new SimpleXMLElement($payload); + $this->assertPattern('/secret/', (string)$element->methodName); + + libxml_disable_entity_loader($elLast); } } -- cgit v1.2.3 From 7cacdc8bc26c98a58dc8986acfd911d6542608af Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Wed, 31 Jul 2013 13:34:55 -0400 Subject: Emit notice if XXE can't be tested and skip test --- engine/tests/regression/trac_bugs.php | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/engine/tests/regression/trac_bugs.php b/engine/tests/regression/trac_bugs.php index ea39253df..689275661 100644 --- a/engine/tests/regression/trac_bugs.php +++ b/engine/tests/regression/trac_bugs.php @@ -377,6 +377,7 @@ class ElggCoreRegressionBugsTest extends ElggCoreUnitTest { public function test_ElggXMLElement_does_not_load_external_entities() { $elLast = libxml_disable_entity_loader(false); + // build payload that should trigger loading of external entity $payload = file_get_contents(dirname(dirname(__FILE__)) . '/test_files/xxe/request.xml'); $path = realpath(dirname(dirname(__FILE__)) . '/test_files/xxe/external_entity.txt'); $path = str_replace('\\', '/', $path); @@ -384,16 +385,20 @@ class ElggCoreRegressionBugsTest extends ElggCoreUnitTest { $path = '/' . $path; } $path = 'file://' . $path; - $payload = sprintf($payload, $path); + $payload = sprintf($payload, $path); - $el = new ElggXMLElement($payload); - $chidren = $el->getChildren(); - $content = $chidren[0]->getContent(); - $this->assertNoPattern('/secret/', $content); - - //make sure the test is valid + // make sure we can actually this in this environment $element = new SimpleXMLElement($payload); - $this->assertPattern('/secret/', (string)$element->methodName); + $can_load_entity = preg_match('/secret/', (string)$element->methodName); + + $this->skipUnless($can_load_entity, "XXE vulnerability cannot be tested on this system"); + + if ($can_load_entity) { + $el = new ElggXMLElement($payload); + $chidren = $el->getChildren(); + $content = $chidren[0]->getContent(); + $this->assertNoPattern('/secret/', $content); + } libxml_disable_entity_loader($elLast); } -- cgit v1.2.3 From e98f933857548be9cd078416a93011ea9c2f3e3a Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Mon, 10 Jun 2013 23:16:45 -0400 Subject: Allow regenerating site secret --- actions/admin/site/regenerate_secret.php | 11 ++ engine/classes/ElggCrypto.php | 134 +++++++++++++++++++++ engine/lib/actions.php | 27 ++++- engine/lib/admin.php | 2 + ...3060900-1.8.15-site_secret-404fc165cf9e0ac9.php | 13 ++ languages/en.php | 18 ++- .../admin/settings/advanced/site_secret.php | 11 ++ views/default/css/admin.php | 20 +++ .../default/forms/admin/site/regenerate_secret.php | 24 ++++ 9 files changed, 257 insertions(+), 3 deletions(-) create mode 100644 actions/admin/site/regenerate_secret.php create mode 100644 engine/classes/ElggCrypto.php create mode 100644 engine/lib/upgrades/2013060900-1.8.15-site_secret-404fc165cf9e0ac9.php create mode 100644 views/default/admin/settings/advanced/site_secret.php create mode 100644 views/default/forms/admin/site/regenerate_secret.php diff --git a/actions/admin/site/regenerate_secret.php b/actions/admin/site/regenerate_secret.php new file mode 100644 index 000000000..3112fb5f3 --- /dev/null +++ b/actions/admin/site/regenerate_secret.php @@ -0,0 +1,11 @@ +='); + } + // /dev/urandom is available on many *nix systems and is considered the + // best commonly available pseudo-random source. + if ($fh = @fopen('/dev/urandom', 'rb')) { + // PHP only performs buffered reads, so in reality it will always read + // at least 4096 bytes. Thus, it costs nothing extra to read and store + // that much so as to speed any additional invocations. + $bytes .= fread($fh, max(4096, $count)); + fclose($fh); + } elseif ($php_compatible && function_exists('openssl_random_pseudo_bytes')) { + // openssl_random_pseudo_bytes() will find entropy in a system-dependent + // way. + $bytes .= openssl_random_pseudo_bytes($count - strlen($bytes)); + } + // If /dev/urandom is not available or returns no bytes, this loop will + // generate a good set of pseudo-random bytes on any system. + // Note that it may be important that our $random_state is passed + // through hash() prior to being rolled into $output, that the two hash() + // invocations are different, and that the extra input into the first one - + // the microtime() - is prepended rather than appended. This is to avoid + // directly leaking $random_state via the $output stream, which could + // allow for trivial prediction of further "random" numbers. + while (strlen($bytes) < $count) { + $random_state = hash('sha256', microtime() . mt_rand() . $random_state); + $bytes .= hash('sha256', mt_rand() . $random_state, true); + } + } + $output = substr($bytes, 0, $count); + $bytes = substr($bytes, $count); + return $output; + } + + /** + * Generate a random string of specified length. + * + * Uses supplied character list for generating the new string. + * If no character list provided - uses Base64 URL character set. + * + * @param int $length Desired length of the string + * @param string|null $chars Characters to be chosen from randomly. If not given, the Base64 URL + * charset will be used. + * + * @return string The random string + * + * @throws InvalidArgumentException + * + * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * + * @see https://github.com/zendframework/zf2/blob/master/library/Zend/Math/Rand.php#L179 + */ + public static function getRandomString($length, $chars = null) + { + if ($length < 1) { + throw new InvalidArgumentException('Length should be >= 1'); + } + + if (empty($chars)) { + $numBytes = ceil($length * 0.75); + $bytes = self::getRandomBytes($numBytes); + $string = substr(rtrim(base64_encode($bytes), '='), 0, $length); + + // Base64 URL + return strtr($string, '+/', '-_'); + } + + $listLen = strlen($chars); + + if ($listLen == 1) { + return str_repeat($chars, $length); + } + + $bytes = self::getRandomBytes($length); + $pos = 0; + $result = ''; + for ($i = 0; $i < $length; $i++) { + $pos = ($pos + ord($bytes[$i])) % $listLen; + $result .= $chars[$pos]; + } + + return $result; + } +} diff --git a/engine/lib/actions.php b/engine/lib/actions.php index 56936f582..8047914ac 100644 --- a/engine/lib/actions.php +++ b/engine/lib/actions.php @@ -364,16 +364,19 @@ function generate_action_token($timestamp) { } /** - * Initialise the site secret hash. + * Initialise the site secret (32 bytes: "z" to indicate format + 186-bit key in Base64 URL). * * Used during installation and saves as a datalist. * + * Note: Old secrets were hex encoded. + * * @return mixed The site secret hash or false * @access private * @todo Move to better file. */ function init_site_secret() { - $secret = md5(rand() . microtime()); + $secret = 'z' . ElggCrypto::getRandomString(31); + if (datalist_set('__site_secret__', $secret)) { return $secret; } @@ -399,6 +402,26 @@ function get_site_secret() { return $secret; } +/** + * Get the strength of the site secret + * + * @return string "strong", "moderate", or "weak" + * @access private + */ +function _elgg_get_site_secret_strength() { + $secret = get_site_secret(); + if ($secret[0] !== 'z') { + $rand_max = getrandmax(); + if ($rand_max < pow(2, 16)) { + return 'weak'; + } + if ($rand_max < pow(2, 32)) { + return 'moderate'; + } + } + return 'strong'; +} + /** * Check if an action is registered and its script exists. * diff --git a/engine/lib/admin.php b/engine/lib/admin.php index 7f82108c0..f36f29668 100644 --- a/engine/lib/admin.php +++ b/engine/lib/admin.php @@ -236,6 +236,7 @@ function admin_init() { elgg_register_action('admin/site/update_advanced', '', 'admin'); elgg_register_action('admin/site/flush_cache', '', 'admin'); elgg_register_action('admin/site/unlock_upgrade', '', 'admin'); + elgg_register_action('admin/site/regenerate_secret', '', 'admin'); elgg_register_action('admin/menu/save', '', 'admin'); @@ -291,6 +292,7 @@ function admin_init() { elgg_register_admin_menu_item('configure', 'settings', null, 100); elgg_register_admin_menu_item('configure', 'basic', 'settings', 10); elgg_register_admin_menu_item('configure', 'advanced', 'settings', 20); + elgg_register_admin_menu_item('configure', 'advanced/site_secret', 'settings', 25); elgg_register_admin_menu_item('configure', 'menu_items', 'appearance', 30); elgg_register_admin_menu_item('configure', 'profile_fields', 'appearance', 40); // default widgets is added via an event handler elgg_default_widgets_init() in widgets.php diff --git a/engine/lib/upgrades/2013060900-1.8.15-site_secret-404fc165cf9e0ac9.php b/engine/lib/upgrades/2013060900-1.8.15-site_secret-404fc165cf9e0ac9.php new file mode 100644 index 000000000..b5b614762 --- /dev/null +++ b/engine/lib/upgrades/2013060900-1.8.15-site_secret-404fc165cf9e0ac9.php @@ -0,0 +1,13 @@ + 'Settings', 'admin:settings:basic' => 'Basic Settings', 'admin:settings:advanced' => 'Advanced Settings', + 'admin:settings:advanced/site_secret' => 'Site Secret', 'admin:site:description' => "This admin panel allows you to control global settings for your site. Choose an option below to get started.", + 'admin:settings:advanced:site_secret' => 'Site Secret', 'admin:site:opt:linktext' => "Configure site...", 'admin:site:access:warning' => "Changing the access setting only affects the permissions on content created in the future.", + 'admin:site:secret:intro' => 'Elgg uses a key to create security tokens for various purposes.', + 'admin:site:secret_regenerated' => "Your site secret has been regenerated.", + 'admin:site:secret:regenerate' => "Regenerate site secret", + 'admin:site:secret:regenerate:help' => "Note: This may inconvenience some users by invalidating tokens used in \"remember me\" cookies, e-mail validation requests, invitation codes, etc.", + 'site_secret:current_strength' => 'Key Strength', + 'site_secret:strength:weak' => "Weak", + 'site_secret:strength_msg:weak' => "We strongly recommend that you regenerate your site secret.", + 'site_secret:strength:moderate' => "Moderate", + 'site_secret:strength_msg:moderate' => "We recommend you regenerate your site secret for the best site security.", + 'site_secret:strength:strong' => "Strong", + 'site_secret:strength_msg:strong' => "✓ Your site secret is sufficiently strong.", + 'admin:dashboard' => 'Dashboard', 'admin:widget:online_users' => 'Online users', 'admin:widget:online_users:help' => 'Lists the users currently on the site', @@ -1064,7 +1078,7 @@ Once you have logged in, we highly recommend that you change your password. 'upgrade:unlock' => 'Unlock upgrade', 'upgrade:unlock:confirm' => "The database is locked for another upgrade. Running concurrent upgrades is dangerous. You should only continue if you know there is not another upgrade running. Unlock?", 'upgrade:locked' => "Cannot upgrade. Another upgrade is running. To clear the upgrade lock, visit the Admin section.", - 'upgrade:unlock:success' => "Upgrade unlocked suscessfully.", + 'upgrade:unlock:success' => "Upgrade unlocked successfully.", 'upgrade:unable_to_upgrade' => 'Unable to upgrade.', 'upgrade:unable_to_upgrade_info' => 'This installation cannot be upgraded because legacy views @@ -1079,6 +1093,8 @@ Once you have logged in, we highly recommend that you change your password. 'update:twitter_api:deactivated' => 'Twitter API (previously Twitter Service) was deactivated during the upgrade. Please activate it manually if required.', 'update:oauth_api:deactivated' => 'OAuth API (previously OAuth Lib) was deactivated during the upgrade. Please activate it manually if required.', + 'upgrade:site_secret_warning:moderate' => "You are encouraged to regenerate your site key to improve system security. See Configure > Site Secret", + 'upgrade:site_secret_warning:weak' => "You are strongly encouraged to regenerate your site key to improve system security. See Configure > Site Secret", 'deprecated:function' => '%s() was deprecated by %s()', diff --git a/views/default/admin/settings/advanced/site_secret.php b/views/default/admin/settings/advanced/site_secret.php new file mode 100644 index 000000000..e70ac7ab6 --- /dev/null +++ b/views/default/admin/settings/advanced/site_secret.php @@ -0,0 +1,11 @@ + _elgg_get_site_secret_strength(), +)); diff --git a/views/default/css/admin.php b/views/default/css/admin.php index 3896ded5d..c435621b2 100644 --- a/views/default/css/admin.php +++ b/views/default/css/admin.php @@ -1543,6 +1543,26 @@ table.mceLayout { margin: 0 0 1em 2em; } +/* *************************************** + SITE SECRET +*************************************** */ +.elgg-form-admin-site-regenerate-secret table { + width: 60%; + margin: 1em auto; +} +td.elgg-strength-strong, +td.elgg-strength-strong h4 { + background: #DFF0D8; color: #468847; +} +td.elgg-strength-moderate, +td.elgg-strength-moderate h4 { + background: #FCF8E3; color: #C09853; +} +td.elgg-strength-weak, +td.elgg-strength-weak h4 { + background: #F2DEDE; color: #B94A48; +} + /* *************************************** HELPERS *************************************** */ diff --git a/views/default/forms/admin/site/regenerate_secret.php b/views/default/forms/admin/site/regenerate_secret.php new file mode 100644 index 000000000..af269b801 --- /dev/null +++ b/views/default/forms/admin/site/regenerate_secret.php @@ -0,0 +1,24 @@ + +

+ + + + + + +
+

+
+
+ +
+ elgg_echo('admin:site:secret:regenerate'), + 'class' => 'elgg-requires-confirmation elgg-button elgg-button-submit', + )); ?> +

+
-- cgit v1.2.3 From 4bcca223409915e075dd08f0aaca9f23ea63f610 Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Thu, 13 Jun 2013 14:05:33 -0400 Subject: PRNG replace Drupal's with George Argyros' --- engine/classes/ElggCrypto.php | 181 +++++++++++++++++++++++++++++------------- 1 file changed, 128 insertions(+), 53 deletions(-) diff --git a/engine/classes/ElggCrypto.php b/engine/classes/ElggCrypto.php index 364af4542..358b721ea 100644 --- a/engine/classes/ElggCrypto.php +++ b/engine/classes/ElggCrypto.php @@ -15,70 +15,145 @@ class ElggCrypto { const CHARS_PASSWORD = 'bcdfghjklmnpqrstvwxyz2346789'; /** - * Returns a string of highly randomized bytes (over the full 8-bit range). + * Generate a string of highly randomized bytes (over the full 8-bit range). * - * This function is better than simply calling mt_rand() or any other built-in - * PHP function because it can return a long string of bytes (compared to < 4 - * bytes normally from mt_rand()) and uses the best available pseudo-random - * source. + * @param int $length Number of bytes needed + * @return string Random bytes * - * @param int $count The number of characters (bytes) to return in the string. - * @return string + * @author George Argyros + * @copyright 2012, George Argyros. All rights reserved. + * @license Modified BSD + * @link https://github.com/GeorgeArgyros/Secure-random-bytes-in-PHP/blob/master/srand.php Original * - * @copyright Copyright 2001 - 2012 by the original authors - * https://github.com/drupal/drupal/blob/7.x/COPYRIGHT.txt - * @license https://github.com/drupal/drupal/blob/7.x/LICENSE.txt GPL 2 + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. * - * @see https://github.com/drupal/drupal/blob/7.x/includes/bootstrap.inc#L1942 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL GEORGE ARGYROS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - public static function getRandomBytes($count) { - // $random_state does not use drupal_static as it stores random bytes. - static $random_state, $bytes, $php_compatible; - // Initialize on the first call. The contents of $_SERVER includes a mix of - // user-specific and system information that varies a little with each page. - if (!isset($random_state)) { - $random_state = print_r($_SERVER, true); - if (function_exists('getmypid')) { - // Further initialize with the somewhat random PHP process ID. - $random_state .= getmypid(); + public function getRandomBytes($length) { + /** + * Our primary choice for a cryptographic strong randomness function is + * openssl_random_pseudo_bytes. + */ + $SSLstr = '4'; // http://xkcd.com/221/ + if (function_exists('openssl_random_pseudo_bytes') + && (version_compare(PHP_VERSION, '5.3.4') >= 0 || substr(PHP_OS, 0, 3) !== 'WIN')) { + $SSLstr = openssl_random_pseudo_bytes($length, $strong); + if ($strong) { + return $SSLstr; } - $bytes = ''; } - if (strlen($bytes) < $count) { - // PHP versions prior 5.3.4 experienced openssl_random_pseudo_bytes() - // locking on Windows and rendered it unusable. - if (!isset($php_compatible)) { - $php_compatible = version_compare(PHP_VERSION, '5.3.4', '>='); + + /** + * If mcrypt extension is available then we use it to gather entropy from + * the operating system's PRNG. This is better than reading /dev/urandom + * directly since it avoids reading larger blocks of data than needed. + * Older versions of mcrypt_create_iv may be broken or take too much time + * to finish so we only use this function with PHP 5.3.7 and above. + * @see https://bugs.php.net/bug.php?id=55169 + */ + if (function_exists('mcrypt_create_iv') + && (version_compare(PHP_VERSION, '5.3.7') >= 0 || substr(PHP_OS, 0, 3) !== 'WIN')) { + $str = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM); + if ($str !== false) { + return $str; + } + } + + /** + * No build-in crypto randomness function found. We collect any entropy + * available in the PHP core PRNGs along with some filesystem info and memory + * stats. To make this data cryptographically strong we add data either from + * /dev/urandom or if its unavailable, we gather entropy by measuring the + * time needed to compute a number of SHA-1 hashes. + */ + $str = ''; + $bits_per_round = 2; // bits of entropy collected in each clock drift round + $msec_per_round = 400; // expected running time of each round in microseconds + $hash_len = 20; // SHA-1 Hash length + $total = $length; // total bytes of entropy to collect + + $handle = @fopen('/dev/urandom', 'rb'); + if ($handle && function_exists('stream_set_read_buffer')) { + @stream_set_read_buffer($handle, 0); + } + + do { + $bytes = ($total > $hash_len)? $hash_len : $total; + $total -= $bytes; + + //collect any entropy available from the PHP system and filesystem + $entropy = rand() . uniqid(mt_rand(), true) . $SSLstr; + $entropy .= implode('', @fstat(@fopen( __FILE__, 'r'))); + $entropy .= memory_get_usage() . getmypid(); + $entropy .= serialize($_ENV) . serialize($_SERVER); + if (function_exists('posix_times')) { + $entropy .= serialize(posix_times()); } - // /dev/urandom is available on many *nix systems and is considered the - // best commonly available pseudo-random source. - if ($fh = @fopen('/dev/urandom', 'rb')) { - // PHP only performs buffered reads, so in reality it will always read - // at least 4096 bytes. Thus, it costs nothing extra to read and store - // that much so as to speed any additional invocations. - $bytes .= fread($fh, max(4096, $count)); - fclose($fh); - } elseif ($php_compatible && function_exists('openssl_random_pseudo_bytes')) { - // openssl_random_pseudo_bytes() will find entropy in a system-dependent - // way. - $bytes .= openssl_random_pseudo_bytes($count - strlen($bytes)); + if (function_exists('zend_thread_id')) { + $entropy .= zend_thread_id(); } - // If /dev/urandom is not available or returns no bytes, this loop will - // generate a good set of pseudo-random bytes on any system. - // Note that it may be important that our $random_state is passed - // through hash() prior to being rolled into $output, that the two hash() - // invocations are different, and that the extra input into the first one - - // the microtime() - is prepended rather than appended. This is to avoid - // directly leaking $random_state via the $output stream, which could - // allow for trivial prediction of further "random" numbers. - while (strlen($bytes) < $count) { - $random_state = hash('sha256', microtime() . mt_rand() . $random_state); - $bytes .= hash('sha256', mt_rand() . $random_state, true); + + if ($handle) { + $entropy .= @fread($handle, $bytes); + } else { + // Measure the time that the operations will take on average + for ($i = 0; $i < 3; $i++) { + $c1 = microtime(true); + $var = sha1(mt_rand()); + for ($j = 0; $j < 50; $j++) { + $var = sha1($var); + } + $c2 = microtime(true); + $entropy .= $c1 . $c2; + } + + // Based on the above measurement determine the total rounds + // in order to bound the total running time. + $rounds = (int) ($msec_per_round * 50 / (int) (($c2 - $c1) * 1000000)); + + // Take the additional measurements. On average we can expect + // at least $bits_per_round bits of entropy from each measurement. + $iter = $bytes * (int) (ceil(8 / $bits_per_round)); + + for ($i = 0; $i < $iter; $i++) { + $c1 = microtime(); + $var = sha1(mt_rand()); + for ($j = 0; $j < $rounds; $j++) { + $var = sha1($var); + } + $c2 = microtime(); + $entropy .= $c1 . $c2; + } } + + // We assume sha1 is a deterministic extractor for the $entropy variable. + $str .= sha1($entropy, true); + + } while ($length > strlen($str)); + + if ($handle) { + @fclose($handle); } - $output = substr($bytes, 0, $count); - $bytes = substr($bytes, $count); - return $output; + + return substr($str, 0, $length); } /** -- cgit v1.2.3 From 003946eff06fcafe60db5894e1ade0abee7314b4 Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Thu, 13 Jun 2013 14:10:35 -0400 Subject: code style --- engine/classes/ElggCrypto.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/engine/classes/ElggCrypto.php b/engine/classes/ElggCrypto.php index 358b721ea..b6a8b2024 100644 --- a/engine/classes/ElggCrypto.php +++ b/engine/classes/ElggCrypto.php @@ -175,8 +175,7 @@ class ElggCrypto { * * @see https://github.com/zendframework/zf2/blob/master/library/Zend/Math/Rand.php#L179 */ - public static function getRandomString($length, $chars = null) - { + public static function getRandomString($length, $chars = null) { if ($length < 1) { throw new InvalidArgumentException('Length should be >= 1'); } -- cgit v1.2.3 From 0900be47471476e9aaeac2877779d0051b2c2d4e Mon Sep 17 00:00:00 2001 From: Sem Date: Mon, 4 Nov 2013 21:30:24 +0100 Subject: Upgrade htmLawed to 1.1.16 --- mod/htmlawed/vendors/htmLawed/htmLawed.php | 82 +- mod/htmlawed/vendors/htmLawed/htmLawedTest.php | 89 +- mod/htmlawed/vendors/htmLawed/htmLawed_README.htm | 4336 ++++++++++---------- mod/htmlawed/vendors/htmLawed/htmLawed_README.txt | 3433 ++++++++-------- .../vendors/htmLawed/htmLawed_TESTCASE.txt | 66 +- 5 files changed, 4089 insertions(+), 3917 deletions(-) mode change 100755 => 100644 mod/htmlawed/vendors/htmLawed/htmLawed.php mode change 100755 => 100644 mod/htmlawed/vendors/htmLawed/htmLawedTest.php mode change 100755 => 100644 mod/htmlawed/vendors/htmLawed/htmLawed_README.txt mode change 100755 => 100644 mod/htmlawed/vendors/htmLawed/htmLawed_TESTCASE.txt diff --git a/mod/htmlawed/vendors/htmLawed/htmLawed.php b/mod/htmlawed/vendors/htmLawed/htmLawed.php old mode 100755 new mode 100644 index 0d9624961..63f8c4162 --- a/mod/htmlawed/vendors/htmLawed/htmLawed.php +++ b/mod/htmlawed/vendors/htmLawed/htmLawed.php @@ -1,9 +1,9 @@ ', $x, ''; + } + 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(' ]*(?)\s+`', '`\s+`', '`(<\w[^>]*(?) `'), array(' $1', ' ', '$1'), preg_replace_callback(array('`(<(!\[CDATA\[))(.+?)(\]\]>)`sm', '`(<(!--))(.+?)(-->)`sm', '`(<(pre|script|textarea)[^>]*?>)(.+?)()`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)[^>]*?>)(.+?)()`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 old mode 100755 new mode 100644 index 806aa4641..3a5b92155 --- a/mod/htmlawed/vendors/htmLawed/htmLawedTest.php +++ b/mod/htmlawed/vendors/htmLawed/htmLawedTest.php @@ -1,10 +1,10 @@ /g, '>'); - i = i.replace(/'; if(f.style){f.style.display = 'none';} else{f.visibility = 'hidden';} - f.innerHTML = '

'; + f.innerHTML = '

'; 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(/'; if(f.style){f.style.display = 'none';} else{f.visibility = 'hidden';} - f.innerHTML = '

'; + f.innerHTML = '

'; 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&&100c);u++){for(var n=-u+q;n<=u-s;n+=2){var l=g+n,m;m=n==-u||n!=u&&j[l-1]d)s+=2;else if(r>e)q+=2;else if(p&&(l=g+k-n,0<=l&&l= +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]d)v+=2;else if(m>e)o+=2;else if(!p&&(l=g+k-n,0<=l&&l=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;fd?a=a.substring(c-d):c=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.lengthd[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=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=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;fb)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,f=/\n/g,g=0;g¬
");switch(h){case 1:b[g]=''+j+"";break;case -1:b[g]=''+j+"";break;case 0:b[g]=""+j+""}}return b.join("")}; +diff_match_patch.prototype.diff_text1=function(a){for(var b=[],c=0;cthis.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<=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=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;cthis.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;ie[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;c2*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 htmLawed (<?php echo hl_version();?>) test @@ -545,7 +600,7 @@ if($do){ $st = microtime(); $out = htmLawed($_POST['text'], $cfg, $_POST['spec']); $et = microtime(); - echo '
Input code » ', strlen($_POST['text']), ' chars, ~', ($tag = round((substr_count($_POST['text'], '>') + substr_count($_POST['text'], '<'))/2)), ' tag', ($tag > 1 ? 's' : ''), ' ', (!isset($_POST['text'][$_hlimit]) ? ' Input binary » ' : ''), ' Finalized internal settings »  ', '
Output » htmLawed processing time ', number_format(((substr($et,0,9)) + (substr($et,-10)) - (substr($st,0,9)) - (substr($st,-10))),4), ' s', (($mem = memory_get_peak_usage()) !== false ? ', peak memory usage '. round(($mem-$pre_mem)/1048576, 2). ' MB' : ''), '
'; + echo '
Input code » ', strlen($_POST['text']), ' chars, ~', ($tag = round((substr_count($_POST['text'], '>') + substr_count($_POST['text'], '<'))/2)), ' tag', ($tag > 1 ? 's' : ''), ' ', (!isset($_POST['text'][$_hlimit]) ? ' Input binary » ' : ''), ' Finalized internal settings »  ', '
Output » htmLawed processing time ', number_format(((substr($et,0,9)) + (substr($et,-10)) - (substr($st,0,9)) - (substr($st,-10))),4), ' s', (($mem = memory_get_peak_usage()) !== false ? ', peak memory usage '. round(($mem-$pre_mem)/1048576, 2). ' MB' : ''), '
'; if($_w3c_validate && $validation) { ?> @@ -555,7 +610,7 @@ if($do){
Output code »
', format($out), '
', (!isset($_POST['text'][$_hlimit]) ? '
Output binary »' : ''), '
Output rendered »
', $out, '
'; + echo '

Output code »
', format($out), '
', (!isset($_POST['text'][$_hlimit]) ? ' Output binary »' : ''), ' Diff »
Output rendered »
', $out, '
'; } else{ ?> diff --git a/mod/htmlawed/vendors/htmLawed/htmLawed_README.htm b/mod/htmlawed/vendors/htmLawed/htmLawed_README.htm index 6dd78fb2e..819b39e06 100644 --- a/mod/htmlawed/vendors/htmLawed/htmLawed_README.htm +++ b/mod/htmlawed/vendors/htmLawed/htmLawed_README.htm @@ -1,2160 +1,2178 @@ - - - - - - - - -htmLawed documentation | htmLawed PHP software is a free, open-source, customizable HTML input purifier and filter - - -
-

htmLawed documentation

- -
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
- -
-
-
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 
-
- -

-1  About htmLawed -

(to top)
-
-  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 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 -

(to top)
-
-  *  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 -

(to top)
-
-  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 -

(to top)
-
-  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 -

(to top)
-
-  htmLawed is free and open-source software dual licensed under LGPL license version 3 and GPL license version 2 or later, and copyrighted by Santosh Patnaik, MD, PhD.
- -
-

-1.5  Terms used here -

(to top)
-
-  *  administrator - or admin; person setting up the code to pass input through htmLawed; also, user
-  *  attributes - name-value pairs like href="http://x.com" in opening tags
-  *  author - writer
-  *  character - atomic unit of text; internally represented by a numeric code-point as specified by the encoding or charset in use
-  *  entity - markup like &gt; and &#160; used to refer to a character
-  *  element - HTML element like a and img
-  *  element content -  content between the opening and closing tags of an element, like click of <a href="x">click</a>
-  *  HTML - implies XHTML unless specified otherwise
-  *  input - text string given to htmLawed to process
-  *  processing - involves filtering, correction, etc., of input
-  *  safe - absence or reduction of certain characters and HTML elements and attributes in the input that can otherwise potentially and circumstantially expose web-site users to security vulnerabilities like cross-site scripting attacks (XSS)
-  *  scheme - URL protocol like http and ftp
-  *  specs - standard specifications
-  *  style property - terms like border and height for which declarations are made in values for the style attribute of elements
-  *  tag - markers like <a href="x"> and </a> delineating element content; the opening tag can contain attributes
-  *  tag content - consists of tag markers < and >, element names like div, and possibly attributes
-  *  user - administrator
-  *  writer - end-user like a blog commenter providing the input that is to be processed; also, author
- -
-
-

-2  Usage -

(to top)
-
-  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 (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 website; the filtering itself can be configured, etc., as described here.
- -

-2.1  Simple -

(to top)
-
-  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 -

(to top)
-
$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 -

(to top)
-
-  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 -

(to top)
-
-  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 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 -

(to top)
-
-  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 -

(to top)
-
-  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 -

(to top)
-
-  htmLawed can work with ill-written HTML code in the input. However, HTML that is too ill-written may not be read as HTML, and be considered mere plain text instead. Following statements indicate the degree of looseness that htmLawed can work with, and can be provided in instructions to writers:
-
-  *  Tags must be flanked by < and > with no > inside -- any needed > should be put in as &gt;. It is possible for tag content (element name and attributes) to be spread over many lines instead of being on one. A space may be present between the tag content and >, like <div > and <img / >, but not after the <.
-
-  *  Element and attribute names need not be lower-cased.
-
-  *  Attribute string of elements may be liberally spaced with tabs, line-breaks, etc.
-
-  *  Attribute values may not be double-quoted, or may be single-quoted.
-
-  *  Left-padding of numeric entities (like, &#0160;, &x07ff;) with 0 is okay as long as the number of characters between between the & and the ; does not exceed 8. All entities must end with ; though.
-
-  *  Named character entities must be properly cased. E.g., &Lt; or &TILDE; will not be let through without modification.
-
-  *  HTML comments should not be inside element tags (okay between tags), and should begin with <!-- and end with -->. Characters like <, >, and & may be allowed inside depending on $config, but any --> inside should be put in as --&gt;. Any -- inside will be automatically converted to -, and a space will be added before the comment delimiter -->.
-
-  *  CDATA sections should not be inside element tags, and can be in element content only if plain text is allowed for that element. They should begin with <[CDATA[ and end with ]]>. Characters like <, >, and & may be allowed inside depending on $config, but any ]]> inside should be put in as ]]&gt;.
-
-  *  For attribute values, character entities &lt;, &gt; and &amp; should be used instead of characters < and >, and & (when & is not part of a character entity). This applies even for Javascript code in values of attributes like onclick.
-
-  *  Characters <, >, & and " that are part of actual Javascript, etc., code in script elements should be used as such and not be put in as entities like &gt;. Otherwise, though the HTML will be valid, the code may fail to work. Further, if such characters have to be used, then they should be put inside CDATA sections.
-
-  *  Simple instructions like "an opening tag cannot be present between two closing tags" and "nested elements should be closed in the reverse order of how they were opened" can help authors write balanced HTML. If tags are imbalanced, htmLawed will try to balance them, but in the process, depending on $config["keep_bad"], some code/text may be lost.
-
-  *  Input authors should be notified of admin-specified allowed elements, attributes, configuration values (like conversion of named entities to numeric ones), etc.
-
-  *  With $config["unique_ids"] not 0 and the id attribute being permitted, writers should carefully avoid using duplicate or invalid id values as even though htmLawed will correct/remove the values, the final output may not be the one desired. E.g., when <a id="home"></a><input id="home" /><label for="home"></label> is processed into
-<a id="home"></a><input id="prefix_home" /><label for="home"></label>.
-
-  *  Note that even if intended HTML is lost in a highly ill-written input, the processed output will be more secure and standard-compliant.
-
-  *  For URLs, unless $config["scheme"] is appropriately set, writers should avoid using escape characters or entities in schemes. E.g., htt&#112; (which many browsers will read as the harmless http) may be considered bad by htmLawed.
-
-  *  htmLawed will attempt to put plain text present directly inside blockquote, form, map and noscript elements (illegal as per the specs) inside auto-generated div elements.
- -
-

-2.8  Limitations & work-arounds -

(to top)
-
-  htmLawed's main objective is to make the input text more standard-compliant, secure for web-page readers, and free of HTML elements and attributes considered undesirable by the administrator. Some of its current limitations, regardless of this objective, are noted below along with work-arounds.
-
-  It should be borne in mind that no browser application is 100% standard-compliant, and that some of the standard specs (like asking for normalization of white-spacing within textarea elements) are clearly wrong. Regarding security, note that unsafe HTML code is not necessarily legally invalid.
-
-  *  htmLawed is meant for input that goes into the body of HTML documents. HTML's head-level elements are not supported, nor are the frameset elements frameset, frame and noframes.
-
-  *  It cannot transform the non-standard embed elements to the standard-compliant object elements. Yet, it can allow embed elements if permitted (embed is widely used and supported). Admins can certainly use the hook_tag parameter (section 3.4.9) to deploy a custom embed-to-object converter function.
-
-  *  The only non-standard element that may be permitted is embed; others like noembed and nobr cannot be permitted without modifying the htmLawed code.
-
-  *  It cannot handle input that has non-HTML code like SVG and MathML. One way around is to break the input into pieces and passing only those without non-HTML code to htmLawed. Another is described in section 3.9. A third way may be to some how take advantage of the $config["and_mark"] parameter (see section 3.2).
-
-  *  By default, htmLawed won't check many attribute values for standard compliance. E.g., width="20m" with the dimension in non-standard m is let through. Implementing universal and strict attribute value checks can make htmLawed slow and resource-intensive. Admins should look at the hook_tag parameter (section 3.4.9) or $spec to enforce finer checks.
-
-  *  The attributes, deprecated (which can be transformed too) or not, that it supports are largely those that are in the specs. Only a few of the proprietary attributes are supported.
-
-  *  Except for contained URLs and dynamic expressions (also optional), htmLawed does not check CSS style property values. Admins should look at using the hook_tag parameter (section 3.4.9) or $spec for finer checks. Perhaps the best option is to disallow style but allow class attributes with the right oneof or match values for class, and have the various class style properties in .css CSS stylesheet files.
-
-  *  htmLawed does not parse emoticons, decode BBcode, or wikify, auto-converting text to proper HTML. Similarly, it won't convert line-breaks to br elements. Such functions are beyond its purview. Admins should use other code to pre- or post-process the input for such purposes.
-
-  *  htmLawed cannot be used to have links force-opened in new windows (by auto-adding appropriate target and onclick attributes to a). Admins should look at Javascript-based DOM-modifying solutions for this. Admins may also be able to use a custom hook function to enforce such checks (hook_tag parameter; see section 3.4.9).
-
-  *  Nesting-based checks are not possible. E.g., one cannot disallow p elements specifically inside td while permitting it elsewhere. Admins may be able to use a custom hook function to enforce such checks (hook_tag parameter; see section 3.4.9).
-
-  *  Except for optionally converting absolute or relative URLs to the other type, htmLawed will not alter URLs (e.g., to change the value of query strings or to convert http to https. Having absolute URLs may be a standard-requirement, e.g., when HTML is embedded in email messages, whereas altering URLs for other purposes is beyond htmLawed's goals. Admins may be able to use a custom hook function to enforce such checks (hook_tag parameter; see section 3.4.9).
-
-  *  Pairs of opening and closing tags that do not enclose any content (like <em></em>) are not removed. This may be against the standard specs for certain elements (e.g., table). However, presence of such standard-incompliant code will not break the display or layout of content. Admins can also use simple regex-based code to filter out such code.
-
-  *  htmLawed does not check for certain element orderings described in the standard specs (e.g., in a table, tbody is allowed before tfoot). Admins may be able to use a custom hook function to enforce such checks (hook_tag parameter; see section 3.4.9).
-
-  *  htmLawed does not check the number of nested elements. E.g., it will allow two caption elements in a table element, illegal as per the specs. Admins may be able to use a custom hook function to enforce such checks (hook_tag parameter; see section 3.4.9).
-
-  *  htmLawed might convert certain entities to actual characters and remove backslashes and CSS comment-markers (/*) in style attribute values in order to detect malicious HTML like crafted IE-specific dynamic expressions like &#101;xpression.... If this is too harsh, admins can allow CSS expressions through htmLawed core but then use a custom function through the hook_tag parameter (section 3.4.9) to more specifically identify CSS expressions in the style attribute values. Also, using $config["style_pass"], it is possible to have htmLawed pass style attribute values without even looking at them (section 3.4.8).
-
-  *  htmLawed does not correct certain possible attribute-based security vulnerabilities (e.g., <a href="http://x%22+style=%22background-image:xss">x</a>). These arise when browsers mis-identify markup in escaped text, defeating the very purpose of escaping text (a bad browser will read the given example as <a href="http://x" style="background-image:xss">x</a>).
-
-  *  Because of poor Unicode support in PHP, htmLawed does not remove the high value HTML-invalid characters with multi-byte code-points. Such characters however are extremely unlikely to be in the input. (see section 3.1).
-
-  *  htmLawed does not check or correct the character encoding of the input it receives. In conjunction with permitting circumstances such as when the character encoding is left undefined through HTTP headers or HTML meta tags, this can permit an exploit (like Google's UTF-7/XSS vulnerability of the past).
-
-  *  Like any script using PHP's PCRE regex functions, PHP setup-specific low PCRE limit values can cause htmLawed to at least partially fail with very long input texts.
- -
-

-2.9  Examples of usage -

(to top)
-
-  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 -

(to top)
-

-3.1  Invalid/dangerous characters -

(to top)
-
-  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 -

(to top)
-
-  Valid character entities take the form &*; where * is #x followed by a hexadecimal number (hexadecimal numeric entity; like &#xA0; for non-breaking space), or alphanumeric like gt (external or named entity; like &nbsp; for non-breaking space), or # followed by a number (decimal numeric entity; like &#160; for non-breaking space). Character entities referring to the soft-hyphen character (the &shy; or \xad character; hexadecimal code-point ad [decimal 173]) in URL-accepting attribute values are always replaced with spaces; soft-hyphens in attribute values introduce vulnerabilities in some older versions of the Opera and Mozilla [Firefox] browsers.
-
-  htmLawed (function hl_ent()):
-
-  *  Neutralizes entities with multiple leading zeroes or missing semi-colons (potentially dangerous)
-
-  *  Lowercases the X (for XML-compliance) and A-F of hexadecimal numeric entities
-
-  *  Neutralizes entities referring to characters that are HTML-invalid (see section 3.1)
-
-  *  Neutralizes entities referring to characters that are HTML-discouraged (code-points, hexadecimally, 7f to 84, 86 to 9f, and fdd0 to fddf, or decimally, 127 to 132, 134 to 159, and 64991 to 64976). Entities referring to the remaining discouraged characters (see section 5.1 for a full list) are let through.
-
-  *  Neutralizes named entities that are not in the specs.
-
-  *  Optionally converts valid HTML-specific named entities except &gt;, &lt;, &quot;, and &amp; to decimal numeric ones (hexadecimal if $config["hexdec_entity"] is 2) for generic XML-compliance. For this, $config["named_entity"] should be 1.
-
-  *  Optionally converts hexadecimal numeric entities to the more widely supported decimal ones. For this, $config["hexdec_entity"] should be 0.
-
-  *  Optionally converts decimal numeric entities to the hexadecimal ones. For this, $config["hexdec_entity"] should be 2.
-
Neutralization refers to the entitification of & to &amp;.
-
Note: htmLawed does not convert entities to the actual characters represented by them; one can pass the htmLawed output through PHP's html_entity_decode function for that.
-
Note: If $config["and_mark"] is set, and set to a value other than 0, then the & characters in the original input are replaced with the control character for the hexadecimal code-point 6 (\x06; & characters introduced by htmLawed, e.g., after converting < to &lt;, are not affected). This allows one to distinguish, say, an &gt; introduced by htmLawed and an &gt; put in by the input writer, and can be helpful in further processing of the htmLawed-processed text (e.g., to identify the character sequence o(><)o to generate an emoticon image). When this feature is active, admins should ensure that the htmLawed output is not directly used in web pages or XML documents as the presence of the \x06 can break documents. Before use in such documents, and preferably before any storage, any remaining \x06 should be changed back to &, e.g., with:
-
- -    $final = str_replace("\x06", '&', $prelim); -
-
-  Also, see section 3.9.
- -
-

-3.3  HTML elements -

(to top)
-
-  htmLawed can be configured to allow only certain HTML elements (tags) in the input. Disallowed elements (just tag-content, and not element-content), based on $config["keep_bad"], are either neutralized (converted to plain text by entitification of < and >) or removed.
-
-  E.g., with only em permitted:
-
-  Input:
-
- -      <em>My</em> website is <a href="http://a.com>a.com</a>. -
-
-  Output, with $config["keep_bad"] = 0:
-
- -      <em>My</em> website is a.com. -
-
-  Output, with $config["keep_bad"] not 0:
-
- -      <em>My</em> website is &lt;a href=""&gt;a.com&lt;/a&gt;. -
-
-  See section 3.3.3 for differences between the various non-zero $config["keep_bad"] values.
-
-  htmLawed by default permits these 86 elements:
-
- -    a, abbr, acronym, address, applet, area, b, bdo, big, blockquote, br, button, caption, center, cite, code, col, colgroup, dd, del, dfn, dir, div, dl, dt, em, embed, fieldset, font, form, h1, h2, h3, h4, h5, h6, hr, i, iframe, img, input, ins, isindex, kbd, label, legend, li, map, menu, noscript, object, ol, optgroup, option, p, param, pre, q, rb, rbc, rp, rt, rtc, ruby, s, samp, script, select, small, span, strike, strong, sub, sup, table, tbody, td, textarea, tfoot, th, thead, tr, tt, u, ul, var -
-
-  Except for embed (included because of its wide-spread use) and the Ruby elements (rb, rbc, rp, rt, rtc, ruby; part of XHTML 1.1), these are all the elements in the HTML 4/XHTML 1 specs. Strict-specific specs. exclude center, dir, font, isindex, menu, s, strike, and u.
-
-  With $config["safe"] = 1, the default set will exclude applet, embed, iframe, object and script; see section 3.6.
-
-  When $config["elements"], which specifies allowed elements, is properly defined, and neither empty nor set to 0 or *, the default set is not used. To have elements added to or removed from the default set, a +/- notation is used. E.g., *-script-object implies that only script and object are disallowed, whereas *+embed means that noembed is also allowed. Elements can also be specified as comma separated names. E.g., a, b, i means only a, b and i are permitted. In this notation, *, + and - have no significance and can actually cause a mis-reading.
-
-  Some more examples of $config["elements"] values indicating permitted elements (note that empty spaces are liberally allowed for clarity):
-
-  *  a, blockquote, code, em, strong -- only a, blockquote, code, em, and strong
-  *  *-script -- all excluding script
-  *  * -center -dir -font -isindex -menu -s -strike -u -- only XHTML-Strict elements
-  *  *+noembed-script -- all including noembed excluding script
-
-  Some mis-usages (and the resulting permitted elements) that can be avoided:
-
-  *  -* -- none; instead of htmLawed, one might just use, e.g., the htmlspecialchars() PHP function
-  *  *, -script -- all except script; admin probably meant *-script
-  *  -*, a, em, strong -- all; admin probably meant a, em, strong
-  *  * -- all; admin need not have set elements
-  *  *-form+form -- all; a + will always over-ride any -
-  *  *, noembed -- only noembed; admin probably meant *+noembed
-  *  a, +b, i -- only a and i; admin probably meant a, b, i
-
-  Basically, when using the +/- notation, commas (,) should not be used, and vice versa, and * should be used with the former but not the latter.
-
Note: Even if an element that is not in the default set is allowed through $config["elements"], like noembed in the last example, it will eventually be removed during tag balancing unless such balancing is turned off ($config["balance"] set to 0). Currently, the only way around this, which actually is simple, is to edit the various arrays in the function hl_bal() to accommodate the element and its nesting properties.
-
A possibly second way to specify allowed elements is to set $config["parent"] to an element name that supposedly will hold the input, and to set $config["balance"] to 1. During tag balancing (see section 3.3.3), all elements that cannot legally nest inside the parent element will be removed. The parent element is auto-reset to div if $config["parent"] is empty, body, or an element not in htmLawed's default set of 86 elements.
-
Tag transformation is possible for improving XHTML-Strict compliance -- most of the deprecated elements are removed or converted to valid XHTML-Strict ones; see section 3.3.2.
- -

-3.3.1  Handling of comments and CDATA sections -

(to top)
-
CDATA sections have the format <![CDATA[...anything but not "]]>"...]]>, and HTML comments, <!--...anything but not "-->"... -->. Neither HTML comments nor CDATA sections can reside inside tags. HTML comments can exist anywhere else, but CDATA sections can exist only where plain text is allowed (e.g., immediately inside td element content but not immediately inside tr element content).
-
-  htmLawed (function hl_cmtcd()) handles HTML comments or CDATA sections depending on the values of $config["comment"] or $config["cdata"]. If 0, such markup is not looked for and the text is processed like plain text. If 1, it is removed completely. If 2, it is preserved but any <, > and & inside are changed to entities. If 3, they are left as such.
-
-  Note that for the last two cases, HTML comments and CDATA sections will always be removed from tag content (function hl_tag()).
-
-  Examples:
-
-  Input:
- -    <!-- home link --><a href="home.htm"><![CDATA[x=&y]]>Home</a> -
-  Output ($config["comment"] = 0, $config["cdata"] = 2):
- -    &lt;-- home link --&gt;<a href="home.htm"><![CDATA[x=&amp;y]]>Home</a> -
-  Output ($config["comment"] = 1, $config["cdata"] = 2):
- -    <a href="home.htm"><![CDATA[x=&amp;y]]>Home</a> -
-  Output ($config["comment"] = 2, $config["cdata"] = 2):
- -    <!-- home link --><a href="home.htm"><![CDATA[x=&amp;y]]>Home</a> -
-  Output ($config["comment"] = 2, $config["cdata"] = 1):
- -    <!-- home link --><a href="home.htm">Home</a> -
-  Output ($config["comment"] = 3, $config["cdata"] = 3):
- -    <!-- home link --><a href="home.htm"><![CDATA[x=&y]]>Home</a> -
-
-  For standard-compliance, comments are given the form <!--comment -->, and any -- in the content is made -.
-
-  When $config["safe"] = 1, CDATA sections and comments are considered plain text unless $config["comment"] or $config["cdata"] is explicitly specified; see section 3.6.
- -
-

-3.3.2  Tag-transformation for better XHTML-Strict -

(to top)
-
-  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)
-  *  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 -

(to top)
-
-  If $config["balance"] is set to 1, htmLawed (function hl_bal()) checks and corrects the input to have properly balanced tags and legal element content (i.e., any element nesting should be valid, and plain text may be present only in the content of elements that allow them).
-
-  Depending on the value of $config["keep_bad"] (see section 2.2 and section 3.3), illegal content may be removed or neutralized to plain text by converting < and > to entities:
-
0 - remove; this option is available only to maintain Kses-compatibility and should not be used otherwise (see section 2.6)
1 - neutralize tags and keep element content
2 - remove tags but keep element content
3 and 4 - like 1 and 2, but keep element content only if text (pcdata) is valid in parent element as per specs
5 and 6 -  like 3 and 4, but line-breaks, tabs and spaces are left
-
-  Example input (disallowing the p element):
-
- -    <*> Pseudo-tags <*> -
- -    <xml>Non-HTML tag xml</xml> -
- -    <p> -
- -    Disallowed tag p -
- -    </p> -
- -    <ul>Bad<li>OK</li></ul> -
-
-  The output with $config["keep_bad"] = 1:
-
- -    &lt;*&gt; Pseudo-tags &lt;*&gt; -
- -    &lt;xml&gt;Non-HTML tag xml&lt;/xml&gt; -
- -    &lt;p&gt; -
- -    Disallowed tag p -
- -    &lt;/p&gt; -
- -    <ul>Bad<li>OK</li></ul> -
-
-  The output with $config["keep_bad"] = 3:
-
- -    &lt;*&gt; Pseudo-tags &lt;*&gt; -
- -    &lt;xml&gt;Non-HTML tag xml&lt;/xml&gt; -
- -    &lt;p&gt; -
- -    Disallowed tag p -
- -    &lt;/p&gt; -
- -    <ul><li>OK</li></ul> -
-
-  The output with $config["keep_bad"] = 6:
-
- -    &lt;*&gt; Pseudo-tags &lt;*&gt; -
- -    Non-HTML tag xml -
-
- -    Disallowed tag p -
-
- -    <ul><li>OK</li></ul> -
-
-  An option like 1 is useful, e.g., when a writer previews his submission, whereas one like 3 is useful before content is finalized and made available to all.
-
Note: In the example above, unlike <*>, <xml> gets considered as a tag (even though there is no HTML element named xml). In general, text matching the regular expression pattern <(/?)([a-zA-Z][a-zA-Z1-6]*)([^>]*?)\s?> is considered a tag (phrase enclosed by the angled brackets < and >, and starting [with an optional slash preceding] with an alphanumeric word that starts with an alphabet...).
-
-  Nesting/content rules for each of the 86 elements in htmLawed's default set (see section 3.3) are defined in function hl_bal(). This means that if a non-standard element besides embed is being permitted through $config["elements"], the element's tag content will end up getting removed if $config["balance"] is set to 1.
-
-  Plain text and/or certain elements nested inside blockquote, form, map and noscript need to be in block-level elements. This point is often missed during manual writing of HTML code. htmLawed attempts to address this during balancing. E.g., if the parent container is set as form, the input B:<input type="text" value="b" />C:<input type="text" value="c" /> is converted to <div>B:<input type="text" value="b" />C:<input type="text" value="c" /></div>.
- -
-

-3.3.4  Elements requiring child elements -

(to top)
-
-  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 -

(to top)
-
-  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 -

(to top)
-
-  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 -

(to top)
-
-  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 -

(to top)
-
-  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 -

(to top)
-
-  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 -

(to top)
-
-  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.
- -
-

-3.4.5  Lower-cased, standard attribute values -

(to top)
-
-  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 -

(to top)
-
-  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 -

(to top)
-
-  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 -

(to top)
-
-  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 -

(to top)
-
-  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 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 website.
- -
-
-

-3.5  Simple configuration directive for most valid XHTML -

(to top)
-
-  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 -

(to top)
-
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 may be available here.
- -
-

-3.7  Using a hook function -

(to top)
-
-  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 website.
- -
-

-3.8  Obtaining finalized parameter values -

(to top)
-
-  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 -

(to top)
-
-  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 -

(to top)
-

-4.1  Support -

(to top)
-
-  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 -

(to top)
-
-  See section 2.8.
-
-  Readers are advised to cross-check information given in this document.
- -
-

-4.3  Change-log -

(to top)
-
-  (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 -

(to top)
-
-  To test htmLawed using a form interface, a demo 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 is also provided.
- -
-

-4.5  Upgrade, & old versions -

(to top)
-
-  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 -

(to top)
-
-  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 for updated information.
- -
-

-4.7  Use through application plug-ins/modules -

(to top)
-
-  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.
- -
-

-4.8  Use in non-PHP applications -

(to top)
-
-  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.
- -
-

-4.9  Donate -

(to top)
-
-  A donation in any currency and amount to appreciate or support this software can be sent by PayPal to this email address: drpatnaik at yahoo dot com.
- -
-

-4.10  Acknowledgements -

(to top)
-
-  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 -

(to top)
-

-5.1  Characters discouraged in XHTML -

(to top)
-
-  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 -

(to top)
-
-  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 -

(to top)
-
-  background
-  background-image
-  content
-  cue-after
-  cue-before
-  cursor
-  list-style
-  list-style-image
-  play-during
- -
-

-5.4  Microsoft Windows 1252 character replacements -

(to top)
-
-  Key: d double, l left, q quote, r right, s. single
-
-  Code-point (decimal) - hexadecimal value - replacement entity - represented character
-
-  127 - 7f - (removed) - (not used)
-  128 - 80 - &#8364; - euro
-  129 - 81 - (removed) - (not used)
-  130 - 82 - &#8218; - baseline s. q
-  131 - 83 - &#402; - florin
-  132 - 84 - &#8222; - baseline d q
-  133 - 85 - &#8230; - ellipsis
-  134 - 86 - &#8224; - dagger
-  135 - 87 - &#8225; - d dagger
-  136 - 88 - &#710; - circumflex accent
-  137 - 89 - &#8240; - permile
-  138 - 8a - &#352; - S Hacek
-  139 - 8b - &#8249; - l s. guillemet
-  140 - 8c - &#338; - OE ligature
-  141 - 8d - (removed) - (not used)
-  142 - 8e - &#381; - Z dieresis
-  143 - 8f - (removed) - (not used)
-  144 - 90 - (removed) - (not used)
-  145 - 91 - &#8216; - l s. q
-  146 - 92 - &#8217; - r s. q
-  147 - 93 - &#8220; - l d q
-  148 - 94 - &#8221; - r d q
-  149 - 95 - &#8226; - bullet
-  150 - 96 - &#8211; - en dash
-  151 - 97 - &#8212; - em dash
-  152 - 98 - &#732; - tilde accent
-  153 - 99 - &#8482; - trademark
-  154 - 9a - &#353; - s Hacek
-  155 - 9b - &#8250; - r s. guillemet
-  156 - 9c - &#339; - oe ligature
-  157 - 9d - (removed) - (not used)
-  158 - 9e - &#382; - z dieresis
-  159 - 9f - &#376; - Y dieresis
- -
-

-5.5  URL format -

(to top)
-
-  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 -

(to top)
-
-  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. -
-
-
-


HTM version of htmLawed_README.txt generated on 06 Jun, 2012 using rTxt2htm from PHP Labware -
-
- + + + + + + + + +htmLawed documentation | htmLawed PHP software is a free, open-source, customizable HTML input purifier and filter + + +
+

htmLawed documentation

+ +
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
+ +
+
+
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 
+
+ +

+1  About htmLawed +

(to top)
+
+  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 application.
+ +

+1.1  Example uses +

(to top)
+
+  *  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 +

(to top)
+
+  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 +

(to top)
+
+  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 +

(to top)
+
+  htmLawed is free and open-source software dual copyrighted by Santosh Patnaik, MD, PhD, and licensed under LGPL license version 3, and GPL license version 2 (or later).
+ +
+

+1.5  Terms used here +

(to top)
+
+  In this document, only HTML body-level elements are considered. htmLawed does not have support for head-level elements, body, and the frame-level elements, frameset, frame and noframes, and these elements are ignored here.
+
+  *  administrator - or admin; person setting up the code that utilizes htmLawed; also, user
+  *  attributes - name-value pairs like href="http://x.com" in opening tags
+  *  author - see writer
+  *  character - atomic unit of text; internally represented by a numeric code-point as specified by the encoding or charset in use
+  *  entity - markup like &gt; and &#160; used to refer to a character
+  *  element - HTML element like a and img
+  *  element content -  content between the opening and closing tags of an element, like click of the <a href="x">click</a> element
+  *  HTML - implies XHTML unless specified otherwise
+  *  HTML body - Complete HTML documents typically have a head and a body container. Information in head specifies title of the document, etc., whereas that in the body informs what is to be displayed on a web-page; it is only the elements for body, except frames, frameset and noframes that htmLawed is concerned with
+  *  input - text given to htmLawed to process
+  *  processing - involves filtering, correction, etc., of input
+  *  safe - absence or reduction of certain characters and HTML elements and attributes in HTML of text that can otherwise potentially, and circumstantially, expose text readers to security vulnerabilities like cross-site scripting attacks (XSS)
+  *  scheme - a URL protocol like http and ftp
+  *  specifications - standard specifications, for HTML4, HTML5, Ruby, etc.
+  *  style property - terms like border and height for which declarations are made in values for the style attribute of elements
+  *  tag - markers like <a href="x"> and </a> delineating element content; the opening tag can contain attributes
+  *  tag content - consists of tag markers < and >, element names like div, and possibly attributes
+  *  user - administrator
+  *  writer - end-user like a blog commenter providing the input that is to be processed; also, author
+ +
+

+1.6  Availability +

(to top)
+
+  htmLawed can be downloaded for free at its website. Besides the htmLawed.php file, the download has the htmLawed documentation (this document) in plain text and HTML formats, a script for testing, and a text file for test-cases. htmLawed is also available as a PHP class (OOP code) on its website.
+ +
+
+

+2  Usage +

(to top)
+
+  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 +

(to top)
+
+  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 +

(to top)
+
$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 +

(to top)
+
+  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 +

(to top)
+
+  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 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 +

(to top)
+
+  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 +

(to top)
+
+  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 +

(to top)
+
+  htmLawed can work with ill-written HTML code in the input. However, HTML that is too ill-written may not be read as HTML, and may therefore get identified as mere plain text. Following statements indicate the degree of looseness that htmLawed can work with, and can be provided in instructions to writers:
+
+  *  Tags must be flanked by < and > with no > inside -- any needed > should be put in as &gt;. It is possible for tag content (element name and attributes) to be spread over many lines instead of being on one. A space may be present between the tag content and >, like <div > and <img / >, but not after the <.
+
+  *  Element and attribute names need not be lower-cased.
+
+  *  Attribute string of elements may be liberally spaced with tabs, line-breaks, etc.
+
+  *  Attribute values may be single- and not double-quoted.
+
+  *  Left-padding of numeric entities (like, &#0160;, &x07ff;) with 0 is okay as long as the number of characters between between the & and the ; does not exceed 8. All entities must end with ; though.
+
+  *  Named character entities must be properly cased. Thus, &Lt; or &TILDE; will not be recognized as entities and will be neutralized.
+
+  *  HTML comments should not be inside element tags (they can be between tags), and should begin with <!-- and end with -->. Characters like <, >, and & may be allowed inside depending on $config, but any --> inside should be put in as --&gt;. Any -- inside will be automatically converted to -, and a space will be added before the comment delimiter -->.
+
+  *  CDATA sections should not be inside element tags, and can be in element content only if plain text is allowed for that element. They should begin with <[CDATA[ and end with ]]>. Characters like <, >, and & may be allowed inside depending on $config, but any ]]> inside should be put in as ]]&gt;.
+
+  *  For attribute values, character entities &lt;, &gt; and &amp; should be used instead of characters < and >, and & (when & is not part of a character entity). This applies even for Javascript code in values of attributes like onclick.
+
+  *  Characters <, >, & and " that are part of actual Javascript, etc., code in script elements should be used as such and not be put in as entities like &gt;. Otherwise, though the HTML will be valid, the code may fail to work. Further, if such characters have to be used, then they should be put inside CDATA sections.
+
+  *  Simple instructions like "an opening tag cannot be present between two closing tags" and "nested elements should be closed in the reverse order of how they were opened" can help authors write balanced HTML. If tags are imbalanced, htmLawed will try to balance them, but in the process, depending on $config["keep_bad"], some code/text may be lost.
+
+  *  Input authors should be notified of admin-specified allowed elements, attributes, configuration values (like conversion of named entities to numeric ones), etc.
+
+  *  With $config["unique_ids"] not 0 and the id attribute being permitted, writers should carefully avoid using duplicate or invalid id values as even though htmLawed will correct/remove the values, the final output may not be the one desired. E.g., when <a id="home"></a><input id="home" /><label for="home"></label> is processed into
+<a id="home"></a><input id="prefix_home" /><label for="home"></label>.
+
+  *  Even if intended HTML is lost from an ill-written input, the processed output will be more secure and standard-compliant.
+
+  *  For URLs, unless $config["scheme"] is appropriately set, writers should avoid using escape characters or entities in schemes. E.g., htt&#112; (which many browsers will read as the harmless http) may be considered bad by htmLawed.
+
+  *  htmLawed will attempt to put plain text present directly inside blockquote, form, map and noscript elements (illegal as per the specifications) inside auto-generated div elements.
+ +
+

+2.8  Limitations & work-arounds +

(to top)
+
+  htmLawed's main objective is to make the input text more standard-compliant, secure for readers, and free of HTML elements and attributes considered undesirable by the administrator. Some of its current limitations, regardless of this objective, are noted below along with work-arounds.
+
+  It should be borne in mind that no browser application is 100% standard-compliant, and that some of the standard specifications (like asking for normalization of white-spacing within textarea elements) are clearly wrong. Regarding security, note that unsafe HTML code is not legally invalid per se.
+
+  *  htmLawed is meant for input that goes into the body of HTML documents. HTML's head-level elements are not supported, nor are the frameset elements frameset, frame and noframes. Content of the latter elements can, however, be individually filtered through htmLawed.
+
+  *  It cannot transform the non-standard embed elements to the standard-compliant object elements. Yet, it can allow embed elements if permitted (embed is widely used and supported). Admins can certainly use the hook_tag parameter (section 3.4.9) to deploy a custom embed-to-object converter function.
+
+  *  The only non-standard element that may be permitted is embed; others like noembed and nobr cannot be permitted without modifying the htmLawed code.
+
+  *  It cannot handle input that has non-HTML code like SVG and MathML. One way around is to break the input into pieces and passing only those without non-HTML code to htmLawed. Another is described in section 3.9. A third way may be to some how take advantage of the $config["and_mark"] parameter (see section 3.2).
+
+  *  By default, htmLawed won't check many attribute values for standard compliance. E.g., width="20m" with the dimension in non-standard m is let through. Implementing universal and strict attribute value checks can make htmLawed slow and resource-intensive. Admins should look at the hook_tag parameter (section 3.4.9) or $spec to enforce finer checks.
+
+  *  The attributes, deprecated (which can be transformed too) or not, that it supports are largely those that are in the specifications. Only a few of the proprietary attributes are supported.
+
+  *  Except for contained URLs and dynamic expressions (also optional), htmLawed does not check CSS style property values. Admins should look at using the hook_tag parameter (section 3.4.9) or $spec for finer checks. Perhaps the best option is to disallow style but allow class attributes with the right oneof or match values for class, and have the various class style properties in .css CSS stylesheet files.
+
+  *  htmLawed does not parse emoticons, decode BBcode, or wikify, auto-converting text to proper HTML. Similarly, it won't convert line-breaks to br elements. Such functions are beyond its purview. Admins should use other code to pre- or post-process the input for such purposes.
+
+  *  htmLawed cannot be used to have links force-opened in new windows (by auto-adding appropriate target and onclick attributes to a). Admins should look at Javascript-based DOM-modifying solutions for this. Admins may also be able to use a custom hook function to enforce such checks (hook_tag parameter; see section 3.4.9).
+
+  *  Nesting-based checks are not possible. E.g., one cannot disallow p elements specifically inside td while permitting it elsewhere. Admins may be able to use a custom hook function to enforce such checks (hook_tag parameter; see section 3.4.9).
+
+  *  Except for optionally converting absolute or relative URLs to the other type, htmLawed will not alter URLs (e.g., to change the value of query strings or to convert http to https. Having absolute URLs may be a standard-requirement, e.g., when HTML is embedded in email messages, whereas altering URLs for other purposes is beyond htmLawed's goals. Admins may be able to use a custom hook function to enforce such checks (hook_tag parameter; see section 3.4.9).
+
+  *  Pairs of opening and closing tags that do not enclose any content (like <em></em>) are not removed. This may be against the standard specifications for certain elements (e.g., table). However, presence of such standard-incompliant code will not break the display or layout of content. Admins can also use simple regex-based code to filter out such code.
+
+  *  htmLawed does not check for certain element orderings described in the standard specifications (e.g., in a table, tbody is allowed before tfoot). Admins may be able to use a custom hook function to enforce such checks (hook_tag parameter; see section 3.4.9).
+
+  *  htmLawed does not check the number of nested elements. E.g., it will allow two caption elements in a table element, illegal as per the specifications. Admins may be able to use a custom hook function to enforce such checks (hook_tag parameter; see section 3.4.9).
+
+  *  htmLawed might convert certain entities to actual characters and remove backslashes and CSS comment-markers (/*) in style attribute values in order to detect malicious HTML like crafted IE-specific dynamic expressions like &#101;xpression.... If this is too harsh, admins can allow CSS expressions through htmLawed core but then use a custom function through the hook_tag parameter (section 3.4.9) to more specifically identify CSS expressions in the style attribute values. Also, using $config["style_pass"], it is possible to have htmLawed pass style attribute values without even looking at them (section 3.4.8).
+
+  *  htmLawed does not correct certain possible attribute-based security vulnerabilities (e.g., <a href="http://x%22+style=%22background-image:xss">x</a>). These arise when browsers mis-identify markup in escaped text, defeating the very purpose of escaping text (a bad browser will read the given example as <a href="http://x" style="background-image:xss">x</a>).
+
+  *  Because of poor Unicode support in PHP, htmLawed does not remove the high value HTML-invalid characters with multi-byte code-points. Such characters however are extremely unlikely to be in the input. (see section 3.1).
+
+  *  htmLawed does not check or correct the character encoding of the input it receives. In conjunction with permitting circumstances such as when the character encoding is left undefined through HTTP headers or HTML meta tags, this can permit an exploit (like Google's UTF-7/XSS vulnerability of the past). Also, htmLawed can mangle input text if it is not well-formed in terms of character encoding. Administrators can consider using code available elsewhere to check well-formedness of input text characters to correct any defect.
+
+  *  htmLawed is expected to work with input texts in ASCII-compatible single byte encodings such as national variants of ASCII (like ISO-646-DE/German of the ISO 646 standard), extended ASCII variants (like ISO 8859-10/Turkish of the ISO 8859/ISO Latin standard), ISO 8859-based Windows variants (like Windows 1252), EBCDIC, Shift JIS (Japanese), GB-Roman (Chinese), and KS-Roman (Korean). It should also properly handle texts with variable byte encodings like UTF-7 (Unicode) and UTF-8 (Unicode). However, htmLawed may mangle input texts with double byte encodings like UTF-16 (Unicode), JIS X 0208:1997 (Japanese) and K SX 1001:1992 (Korean), or the UTF-32 (Unicode) quadruple byte encoding. If an input text has such an encoding, administrators can use PHP's iconv 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 +

(to top)
+
+  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 +

(to top)
+

+3.1  Invalid/dangerous characters +

(to top)
+
+  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 +

(to top)
+
+  Valid character entities take the form &*; where * is #x followed by a hexadecimal number (hexadecimal numeric entity; like &#xA0; for non-breaking space), or alphanumeric like gt (external or named entity; like &nbsp; for non-breaking space), or # followed by a number (decimal numeric entity; like &#160; for non-breaking space). Character entities referring to the soft-hyphen character (the &shy; or \xad character; hexadecimal code-point ad [decimal 173]) in URL-accepting attribute values are always replaced with spaces; soft-hyphens in attribute values introduce vulnerabilities in some older versions of the Opera and Mozilla [Firefox] browsers.
+
+  htmLawed (function hl_ent()):
+
+  *  Neutralizes entities with multiple leading zeroes or missing semi-colons (potentially dangerous)
+
+  *  Lowercases the X (for XML-compliance) and A-F of hexadecimal numeric entities
+
+  *  Neutralizes entities referring to characters that are HTML-invalid (see section 3.1)
+
+  *  Neutralizes entities referring to characters that are HTML-discouraged (code-points, hexadecimally, 7f to 84, 86 to 9f, and fdd0 to fddf, or decimally, 127 to 132, 134 to 159, and 64991 to 64976). Entities referring to the remaining discouraged characters (see section 5.1 for a full list) are let through.
+
+  *  Neutralizes named entities that are not in the specs.
+
+  *  Optionally converts valid HTML-specific named entities except &gt;, &lt;, &quot;, and &amp; to decimal numeric ones (hexadecimal if $config["hexdec_entity"] is 2) for generic XML-compliance. For this, $config["named_entity"] should be 1.
+
+  *  Optionally converts hexadecimal numeric entities to the more widely supported decimal ones. For this, $config["hexdec_entity"] should be 0.
+
+  *  Optionally converts decimal numeric entities to the hexadecimal ones. For this, $config["hexdec_entity"] should be 2.
+
Neutralization refers to the entitification of & to &amp;.
+
Note: htmLawed does not convert entities to the actual characters represented by them; one can pass the htmLawed output through PHP's html_entity_decode function for that.
+
Note: If $config["and_mark"] is set, and set to a value other than 0, then the & characters in the original input are replaced with the control character for the hexadecimal code-point 6 (\x06; & characters introduced by htmLawed, e.g., after converting < to &lt;, are not affected). This allows one to distinguish, say, an &gt; introduced by htmLawed and an &gt; put in by the input writer, and can be helpful in further processing of the htmLawed-processed text (e.g., to identify the character sequence o(><)o to generate an emoticon image). When this feature is active, admins should ensure that the htmLawed output is not directly used in web pages or XML documents as the presence of the \x06 can break documents. Before use in such documents, and preferably before any storage, any remaining \x06 should be changed back to &, e.g., with:
+
+ +    $final = str_replace("\x06", '&', $prelim); +
+
+  Also, see section 3.9.
+ +
+

+3.3  HTML elements +

(to top)
+
+  htmLawed can be configured to allow only certain HTML elements (tags) in the input. Disallowed elements (just tag-content, and not element-content), based on $config["keep_bad"], are either neutralized (converted to plain text by entitification of < and >) or removed.
+
+  E.g., with only em permitted:
+
+  Input:
+
+ +      <em>My</em> website is <a href="http://a.com>a.com</a>. +
+
+  Output, with $config["keep_bad"] = 0:
+
+ +      <em>My</em> website is a.com. +
+
+  Output, with $config["keep_bad"] not 0:
+
+ +      <em>My</em> website is &lt;a href=""&gt;a.com&lt;/a&gt;. +
+
+  See section 3.3.3 for differences between the various non-zero $config["keep_bad"] values.
+
+  htmLawed by default permits these 86 elements:
+
+ +    a, abbr, acronym, address, applet, area, b, bdo, big, blockquote, br, button, caption, center, cite, code, col, colgroup, dd, del, dfn, dir, div, dl, dt, em, embed, fieldset, font, form, h1, h2, h3, h4, h5, h6, hr, i, iframe, img, input, ins, isindex, kbd, label, legend, li, map, menu, noscript, object, ol, optgroup, option, p, param, pre, q, rb, rbc, rp, rt, rtc, ruby, s, samp, script, select, small, span, strike, strong, sub, sup, table, tbody, td, textarea, tfoot, th, thead, tr, tt, u, ul, var +
+
+  Except for embed (included because of its wide-spread use) and the Ruby elements (rb, rbc, rp, rt, rtc, ruby; part of XHTML 1.1), these are all the elements in the HTML 4/XHTML 1 specs. Strict-specific specs. exclude center, dir, font, isindex, menu, s, strike, and u.
+
+  With $config["safe"] = 1, the default set will exclude applet, embed, iframe, object and script; see section 3.6.
+
+  When $config["elements"], which specifies allowed elements, is properly defined, and neither empty nor set to 0 or *, the default set is not used. To have elements added to or removed from the default set, a +/- notation is used. E.g., *-script-object implies that only script and object are disallowed, whereas *+embed means that noembed is also allowed. Elements can also be specified as comma separated names. E.g., a, b, i means only a, b and i are permitted. In this notation, *, + and - have no significance and can actually cause a mis-reading.
+
+  Some more examples of $config["elements"] values indicating permitted elements (note that empty spaces are liberally allowed for clarity):
+
+  *  a, blockquote, code, em, strong -- only a, blockquote, code, em, and strong
+  *  *-script -- all excluding script
+  *  * -center -dir -font -isindex -menu -s -strike -u -- only XHTML-Strict elements
+  *  *+noembed-script -- all including noembed excluding script
+
+  Some mis-usages (and the resulting permitted elements) that can be avoided:
+
+  *  -* -- none; instead of htmLawed, one might just use, e.g., the htmlspecialchars() PHP function
+  *  *, -script -- all except script; admin probably meant *-script
+  *  -*, a, em, strong -- all; admin probably meant a, em, strong
+  *  * -- all; admin need not have set elements
+  *  *-form+form -- all; a + will always over-ride any -
+  *  *, noembed -- only noembed; admin probably meant *+noembed
+  *  a, +b, i -- only a and i; admin probably meant a, b, i
+
+  Basically, when using the +/- notation, commas (,) should not be used, and vice versa, and * should be used with the former but not the latter.
+
Note: Even if an element that is not in the default set is allowed through $config["elements"], like noembed in the last example, it will eventually be removed during tag balancing unless such balancing is turned off ($config["balance"] set to 0). Currently, the only way around this, which actually is simple, is to edit the various arrays in the function hl_bal() to accommodate the element and its nesting properties.
+
A possibly second way to specify allowed elements is to set $config["parent"] to an element name that supposedly will hold the input, and to set $config["balance"] to 1. During tag balancing (see section 3.3.3), all elements that cannot legally nest inside the parent element will be removed. The parent element is auto-reset to div if $config["parent"] is empty, body, or an element not in htmLawed's default set of 86 elements.
+
Tag transformation is possible for improving XHTML-Strict compliance -- most of the deprecated elements are removed or converted to valid XHTML-Strict ones; see section 3.3.2.
+ +

+3.3.1  Handling of comments and CDATA sections +

(to top)
+
CDATA sections have the format <![CDATA[...anything but not "]]>"...]]>, and HTML comments, <!--...anything but not "-->"... -->. Neither HTML comments nor CDATA sections can reside inside tags. HTML comments can exist anywhere else, but CDATA sections can exist only where plain text is allowed (e.g., immediately inside td element content but not immediately inside tr element content).
+
+  htmLawed (function hl_cmtcd()) handles HTML comments or CDATA sections depending on the values of $config["comment"] or $config["cdata"]. If 0, such markup is not looked for and the text is processed like plain text. If 1, it is removed completely. If 2, it is preserved but any <, > and & inside are changed to entities. If 3, they are left as such.
+
+  Note that for the last two cases, HTML comments and CDATA sections will always be removed from tag content (function hl_tag()).
+
+  Examples:
+
+  Input:
+ +    <!-- home link --><a href="home.htm"><![CDATA[x=&y]]>Home</a> +
+  Output ($config["comment"] = 0, $config["cdata"] = 2):
+ +    &lt;-- home link --&gt;<a href="home.htm"><![CDATA[x=&amp;y]]>Home</a> +
+  Output ($config["comment"] = 1, $config["cdata"] = 2):
+ +    <a href="home.htm"><![CDATA[x=&amp;y]]>Home</a> +
+  Output ($config["comment"] = 2, $config["cdata"] = 2):
+ +    <!-- home link --><a href="home.htm"><![CDATA[x=&amp;y]]>Home</a> +
+  Output ($config["comment"] = 2, $config["cdata"] = 1):
+ +    <!-- home link --><a href="home.htm">Home</a> +
+  Output ($config["comment"] = 3, $config["cdata"] = 3):
+ +    <!-- home link --><a href="home.htm"><![CDATA[x=&y]]>Home</a> +
+
+  For standard-compliance, comments are given the form <!--comment -->, and any -- in the content is made -.
+
+  When $config["safe"] = 1, CDATA sections and comments are considered plain text unless $config["comment"] or $config["cdata"] is explicitly specified; see section 3.6.
+ +
+

+3.3.2  Tag-transformation for better XHTML-Strict +

(to top)
+
+  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)
+  *  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 +

(to top)
+
+  If $config["balance"] is set to 1, htmLawed (function hl_bal()) checks and corrects the input to have properly balanced tags and legal element content (i.e., any element nesting should be valid, and plain text may be present only in the content of elements that allow them).
+
+  Depending on the value of $config["keep_bad"] (see section 2.2 and section 3.3), illegal content may be removed or neutralized to plain text by converting < and > to entities:
+
0 - remove; this option is available only to maintain Kses-compatibility and should not be used otherwise (see section 2.6)
1 - neutralize tags and keep element content
2 - remove tags but keep element content
3 and 4 - like 1 and 2, but keep element content only if text (pcdata) is valid in parent element as per specs
5 and 6 -  like 3 and 4, but line-breaks, tabs and spaces are left
+
+  Example input (disallowing the p element):
+
+ +    <*> Pseudo-tags <*> +
+ +    <xml>Non-HTML tag xml</xml> +
+ +    <p> +
+ +    Disallowed tag p +
+ +    </p> +
+ +    <ul>Bad<li>OK</li></ul> +
+
+  The output with $config["keep_bad"] = 1:
+
+ +    &lt;*&gt; Pseudo-tags &lt;*&gt; +
+ +    &lt;xml&gt;Non-HTML tag xml&lt;/xml&gt; +
+ +    &lt;p&gt; +
+ +    Disallowed tag p +
+ +    &lt;/p&gt; +
+ +    <ul>Bad<li>OK</li></ul> +
+
+  The output with $config["keep_bad"] = 3:
+
+ +    &lt;*&gt; Pseudo-tags &lt;*&gt; +
+ +    &lt;xml&gt;Non-HTML tag xml&lt;/xml&gt; +
+ +    &lt;p&gt; +
+ +    Disallowed tag p +
+ +    &lt;/p&gt; +
+ +    <ul><li>OK</li></ul> +
+
+  The output with $config["keep_bad"] = 6:
+
+ +    &lt;*&gt; Pseudo-tags &lt;*&gt; +
+ +    Non-HTML tag xml +
+
+ +    Disallowed tag p +
+
+ +    <ul><li>OK</li></ul> +
+
+  An option like 1 is useful, e.g., when a writer previews his submission, whereas one like 3 is useful before content is finalized and made available to all.
+
Note: In the example above, unlike <*>, <xml> gets considered as a tag (even though there is no HTML element named xml). Thus, the keep_bad parameter's value affects <xml> but not <*>. In general, text matching the regular expression pattern <(/?)([a-zA-Z][a-zA-Z1-6]*)([^>]*?)\s?> is considered a tag (phrase enclosed by the angled brackets < and >, and starting [with an optional slash preceding] with an alphanumeric word that starts with an alphabet...), and is subjected to the keep_bad value.
+
+  Nesting/content rules for each of the 86 elements in htmLawed's default set (see section 3.3) are defined in function hl_bal(). This means that if a non-standard element besides embed is being permitted through $config["elements"], the element's tag content will end up getting removed if $config["balance"] is set to 1.
+
+  Plain text and/or certain elements nested inside blockquote, form, map and noscript need to be in block-level elements. This point is often missed during manual writing of HTML code. htmLawed attempts to address this during balancing. E.g., if the parent container is set as form, the input B:<input type="text" value="b" />C:<input type="text" value="c" /> is converted to <div>B:<input type="text" value="b" />C:<input type="text" value="c" /></div>.
+ +
+

+3.3.4  Elements requiring child elements +

(to top)
+
+  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 +

(to top)
+
+  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 +

(to top)
+
+  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 +

(to top)
+
+  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 +

(to top)
+
+  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 +

(to top)
+
+  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 +

(to top)
+
+  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.
+ +
+

+3.4.5  Lower-cased, standard attribute values +

(to top)
+
+  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 +

(to top)
+
+  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 +

(to top)
+
+  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 +

(to top)
+
+  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 +

(to top)
+
+  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 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 website.
+ +
+
+

+3.5  Simple configuration directive for most valid XHTML +

(to top)
+
+  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 +

(to top)
+
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 may be available here.
+ +
+

+3.7  Using a hook function +

(to top)
+
+  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 website.
+ +
+

+3.8  Obtaining finalized parameter values +

(to top)
+
+  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 +

(to top)
+
+  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 +

(to top)
+

+4.1  Support +

(to top)
+
+  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 +

(to top)
+
+  See section 2.8.
+ +
+

+4.3  Change-log +

(to top)
+
+  (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 +

(to top)
+
+  To test htmLawed using a form interface, a demo 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 is also provided.
+ +
+

+4.5  Upgrade, & old versions +

(to top)
+
+  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 +

(to top)
+
+  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 for updated information.
+ +
+

+4.7  Use through application plug-ins/modules +

(to top)
+
+  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.
+ +
+

+4.8  Use in non-PHP applications +

(to top)
+
+  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.
+ +
+

+4.9  Donate +

(to top)
+
+  A donation in any currency and amount to appreciate or support this software can be sent by PayPal to this email address: drpatnaik at yahoo dot com.
+ +
+

+4.10  Acknowledgements +

(to top)
+
+  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 +

(to top)
+

+5.1  Characters discouraged in XHTML +

(to top)
+
+  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 +

(to top)
+
+  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 +

(to top)
+
+  background
+  background-image
+  content
+  cue-after
+  cue-before
+  cursor
+  list-style
+  list-style-image
+  play-during
+ +
+

+5.4  Microsoft Windows 1252 character replacements +

(to top)
+
+  Key: d double, l left, q quote, r right, s. single
+
+  Code-point (decimal) - hexadecimal value - replacement entity - represented character
+
+  127 - 7f - (removed) - (not used)
+  128 - 80 - &#8364; - euro
+  129 - 81 - (removed) - (not used)
+  130 - 82 - &#8218; - baseline s. q
+  131 - 83 - &#402; - florin
+  132 - 84 - &#8222; - baseline d q
+  133 - 85 - &#8230; - ellipsis
+  134 - 86 - &#8224; - dagger
+  135 - 87 - &#8225; - d dagger
+  136 - 88 - &#710; - circumflex accent
+  137 - 89 - &#8240; - permile
+  138 - 8a - &#352; - S Hacek
+  139 - 8b - &#8249; - l s. guillemet
+  140 - 8c - &#338; - OE ligature
+  141 - 8d - (removed) - (not used)
+  142 - 8e - &#381; - Z dieresis
+  143 - 8f - (removed) - (not used)
+  144 - 90 - (removed) - (not used)
+  145 - 91 - &#8216; - l s. q
+  146 - 92 - &#8217; - r s. q
+  147 - 93 - &#8220; - l d q
+  148 - 94 - &#8221; - r d q
+  149 - 95 - &#8226; - bullet
+  150 - 96 - &#8211; - en dash
+  151 - 97 - &#8212; - em dash
+  152 - 98 - &#732; - tilde accent
+  153 - 99 - &#8482; - trademark
+  154 - 9a - &#353; - s Hacek
+  155 - 9b - &#8250; - r s. guillemet
+  156 - 9c - &#339; - oe ligature
+  157 - 9d - (removed) - (not used)
+  158 - 9e - &#382; - z dieresis
+  159 - 9f - &#376; - Y dieresis
+ +
+

+5.5  URL format +

(to top)
+
+  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 +

(to top)
+
+  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. +
+
+
+


HTM version of htmLawed_README.txt generated on 29 Aug, 2013 using rTxt2htm from PHP Labware +
+
+ \ No newline at end of file diff --git a/mod/htmlawed/vendors/htmLawed/htmLawed_README.txt b/mod/htmlawed/vendors/htmLawed/htmLawed_README.txt old mode 100755 new mode 100644 index e4027e465..5e19605e5 --- 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 '

' (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 'click' - * `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 '' and '' 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` '' are shown below. - - `Rule`: 'input=title(maxlen=60/minlen=6), value' - `Output`: '' - - `Rule`: 'input=title(), value(maxval=8/default=6)' - `Output`: '' - - `Rule`: 'input=title(nomatch=%w.d%i), value(match=%em%/default=6em)' - `Output`: '' - - `Rule`: 'input=title(oneof=height|depth/default=depth), value(noneof=5|6)' - `Output`: '' - - *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., '' - - * 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 '
' and '', 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 ''. 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 '' is processed into -''. - - * 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 '') 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., 'x'). 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 'x'). - - * 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: - - My website is My website is a.com. - - Output, with '$config["keep_bad"]' not '0': - - My 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 '"...]]>', and HTML comments, '"... -->'. 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 - Output ('$config["comment"] = 0, $config["cdata"] = 2'): - <-- home link -->Home - Output ('$config["comment"] = 1, $config["cdata"] = 2'): - Home - Output ('$config["comment"] = 2, $config["cdata"] = 2'): - Home - Output ('$config["comment"] = 2, $config["cdata"] = 1'): - Home - Output ('$config["comment"] = 3, $config["cdata"] = 3'): - Home - - For standard-compliance, comments are given the form '', 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: - -
- The PHP software script used for this web-page web-page is htmLawedTest.php, from PHP Labware. -
- - The output: - -
- The PHP software script used for this web-page web-page is htmLawedTest.php, from PHP Labware. -
- - --- 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 <*> - Non-HTML tag xml -

- Disallowed tag p -

-
    Bad
  • OK
- - The output with '$config["keep_bad"] = 1': - - <*> Pseudo-tags <*> - <xml>Non-HTML tag xml</xml> - <p> - Disallowed tag p - </p> -
    Bad
  • OK
- - The output with '$config["keep_bad"] = 3': - - <*> Pseudo-tags <*> - <xml>Non-HTML tag xml</xml> - <p> - Disallowed tag p - </p> -
  • OK
- - The output with '$config["keep_bad"] = 6': - - <*> Pseudo-tags <*> - Non-HTML tag xml - - Disallowed tag p - -
  • OK
- - 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 '<*>', '' 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:C:' is converted to '
B:C:
'. - - --- 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 '' becomes '', and if Javascript is not permitted '' becomes ''. - - 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: !', 'CNN' will become 'CNN'. - - *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: - - imageimage -
-
- image - - - - - -
-
-

Section

-

Para

-
  1. First item
-
-
-
  1. First item
-
-
- - And the output with '$config["no_deprecated_attr"] = 1': - - imageimage -
-
- image - - - - - -
-
-

Section

-

Para

-
  1. First item
-
-
-
  1. First item
-
-
- - 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., '@' (makes the example address 'a@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 '' (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 '' tag of the element 'CNN'. 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 ''). - - 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 ""; - } - - 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 = ''; - ++$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', '', $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 '

' (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 'click' 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 '' and '' 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` '' are shown below. + + `Rule`: 'input=title(maxlen=60/minlen=6), value' + `Output`: '' + + `Rule`: 'input=title(), value(maxval=8/default=6)' + `Output`: '' + + `Rule`: 'input=title(nomatch=%w.d%i), value(match=%em%/default=6em)' + `Output`: '' + + `Rule`: 'input=title(oneof=height|depth/default=depth), value(noneof=5|6)' + `Output`: '' + + *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., '' + + * 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 '
' and '', 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 ''. 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 '' is processed into +''. + + * 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 '') 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., 'x'). 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 'x'). + + * 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: + + My website is My website is a.com. + + Output, with '$config["keep_bad"]' not '0': + + My 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 '"...]]>', and HTML comments, '"... -->'. 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 + Output ('$config["comment"] = 0, $config["cdata"] = 2'): + <-- home link -->Home + Output ('$config["comment"] = 1, $config["cdata"] = 2'): + Home + Output ('$config["comment"] = 2, $config["cdata"] = 2'): + Home + Output ('$config["comment"] = 2, $config["cdata"] = 1'): + Home + Output ('$config["comment"] = 3, $config["cdata"] = 3'): + Home + + For standard-compliance, comments are given the form '', 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: + +
+ The PHP software script used for this web-page web-page is htmLawedTest.php, from PHP Labware. +
+ + The output: + +
+ The PHP software script used for this web-page web-page is htmLawedTest.php, from PHP Labware. +
+ + +-- 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 <*> + Non-HTML tag xml +

+ Disallowed tag p +

+
    Bad
  • OK
+ + The output with '$config["keep_bad"] = 1': + + <*> Pseudo-tags <*> + <xml>Non-HTML tag xml</xml> + <p> + Disallowed tag p + </p> +
    Bad
  • OK
+ + The output with '$config["keep_bad"] = 3': + + <*> Pseudo-tags <*> + <xml>Non-HTML tag xml</xml> + <p> + Disallowed tag p + </p> +
  • OK
+ + The output with '$config["keep_bad"] = 6': + + <*> Pseudo-tags <*> + Non-HTML tag xml + + Disallowed tag p + +
  • OK
+ + 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 '<*>', '' gets considered as a tag (even though there is no HTML element named 'xml'). Thus, the 'keep_bad' parameter's value affects '' 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:C:' is converted to '
B:C:
'. + + +-- 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 '' becomes '', and if Javascript is not permitted '' becomes ''. + + 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: !', 'CNN' will become 'CNN'. + + *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: + + imageimage +
+
+ image + + + + + +
+
+

Section

+

Para

+
  1. First item
+
+
+
  1. First item
+
+
+ + And the output with '$config["no_deprecated_attr"] = 1': + + imageimage +
+
+ image + + + + + +
+
+

Section

+

Para

+
  1. First item
+
+
+
  1. First item
+
+
+ + 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., '@' (makes the example address 'a@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 '' (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 '' tag of the element 'CNN'. 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 ''). + + 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 ""; + } + + 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 = ''; + ++$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', '', $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 old mode 100755 new mode 100644 index 793a5a6a7..c5cccaaba --- 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 Duplicated: a
Deprecated: a,

Casing:
+Custom: image
+Data-*: a
Admin-restricted?:
Attribute values
@@ -46,6 +48,11 @@ character encoding to Unicode/UTF-8
abc
def

abc
def
ghi

abc
def
ghi
+
QQQ
x

+
x
QQQ

+
x
QQQ
x

+
x
QQQ

x


+
(try with blockquote parent)
CDATA sections
@@ -128,8 +135,9 @@ Disallowed tag p Invalid: a
Empty: a, a, atext
Content invalid: 12
-Content invalid?:

(try setting 'form' as parent) -Casing: +Content invalid?:

(try setting 'form' as parent)
+Casing:
+Check for tidy:



hi
Entities
@@ -198,6 +206,13 @@ text none t e x t Malformed: , < ![CDATA check ]]>, < ![CDATA check ] ]>
Invalid: >comment in tag content, +
HTML5
+ +figure and figcaption:
picture
Caption for the awesome picture
+article:

A

B

C

E

F

G

+meter:

Heat 150.

+datalist: +
Ins-Del
(depending on context, these elements can be of either block or inline type)
@@ -258,6 +273,10 @@ Invalid: >comment in tag content, +Menu:
  • + +
  • +
    Microdata
    @@ -266,6 +285,16 @@ I am X but people call me www.xy.com +
    Microsoft Word
    + +Proprietary tag:

     


    +XML declaration:
    +XML-invalid character code-point (may not replicate):

    “Where is he?” asked both Mary – the one so lovely – and Jane.

    + +
    Nesting
    + +Block or inline a:

    text

    hi

    +
    Non-English text-1
    Inscrieţi-vă acum la a Zecea Conferinţă Internaţională
    @@ -313,6 +342,7 @@ na Alemanha. (aaa) +
    Tables
    Omitted closing tags: @@ -339,6 +369,14 @@ na Alemanha.
    r2c1r2c2

    +
    Tag transformation
    +Font element intended as 'inline' element:

    hi


    +Font element intended as 'block' element:
    hi

    +Font element intended as 'block' element:
    hi
    QQQ

    + +
    Tidy
    +White-space handling: abc def ghi abc def ghi +
    URLs
    Relative and absolute: , , , , , ,
    @@ -364,6 +402,7 @@ src=javascript:al test
    Bad IE7: x
    +Opera: link Bad IE7: xxx
    Bad IE7: xxx
    Bad IE7: xxx
    @@ -393,4 +432,19 @@ script:eval(document.all.mycode.expr)')">hi
    3 < 4
    3 > 4
    - > 3
    \ No newline at end of file + > 3
    +<._.> hi!
    +<<< ALERT >>>
    + some stuff
    +
    +
    +
    +if(13age){say 'teen'}
    +age >51 and a smoking history of >51 pack-years was
    +age > 51 and a smoking history of >51 pack-years was
    +age <51 and a smoking history of <51 pack-years was
    +age < 51 and a smoking history of < 51 pack-years was
    +age >51 and a smoking history of >51 pack-years
    +age > 51 and a smoking history of >51 pack-years
    +age <51 and a smoking history of <51 pack-years
    +age < 51 and a smoking history of < 51 pack-years
    \ No newline at end of file -- cgit v1.2.3 From ec735cc9f846455041a99dd6e2d183d60f5c476e Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Wed, 20 Nov 2013 23:10:11 -0500 Subject: Fixing permissions changes from #6201 --- mod/htmlawed/vendors/htmLawed/htmLawed.php | 0 mod/htmlawed/vendors/htmLawed/htmLawedTest.php | 0 mod/htmlawed/vendors/htmLawed/htmLawed_README.htm | 0 mod/htmlawed/vendors/htmLawed/htmLawed_README.txt | 0 mod/htmlawed/vendors/htmLawed/htmLawed_TESTCASE.txt | 0 5 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 mod/htmlawed/vendors/htmLawed/htmLawed.php mode change 100644 => 100755 mod/htmlawed/vendors/htmLawed/htmLawedTest.php mode change 100644 => 100755 mod/htmlawed/vendors/htmLawed/htmLawed_README.htm mode change 100644 => 100755 mod/htmlawed/vendors/htmLawed/htmLawed_README.txt mode change 100644 => 100755 mod/htmlawed/vendors/htmLawed/htmLawed_TESTCASE.txt diff --git a/mod/htmlawed/vendors/htmLawed/htmLawed.php b/mod/htmlawed/vendors/htmLawed/htmLawed.php old mode 100644 new mode 100755 diff --git a/mod/htmlawed/vendors/htmLawed/htmLawedTest.php b/mod/htmlawed/vendors/htmLawed/htmLawedTest.php old mode 100644 new mode 100755 diff --git a/mod/htmlawed/vendors/htmLawed/htmLawed_README.htm b/mod/htmlawed/vendors/htmLawed/htmLawed_README.htm old mode 100644 new mode 100755 diff --git a/mod/htmlawed/vendors/htmLawed/htmLawed_README.txt b/mod/htmlawed/vendors/htmLawed/htmLawed_README.txt old mode 100644 new mode 100755 diff --git a/mod/htmlawed/vendors/htmLawed/htmLawed_TESTCASE.txt b/mod/htmlawed/vendors/htmLawed/htmLawed_TESTCASE.txt old mode 100644 new mode 100755 -- cgit v1.2.3 From 55c39ae0980bced8a03739fc25c6d876979d3572 Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Sat, 23 Nov 2013 18:35:11 -0500 Subject: Removes border-radius from radios/checkboxes --- views/default/css/elements/forms.php | 1 + 1 file changed, 1 insertion(+) diff --git a/views/default/css/elements/forms.php b/views/default/css/elements/forms.php index f55e57fb4..068cc8fd6 100644 --- a/views/default/css/elements/forms.php +++ b/views/default/css/elements/forms.php @@ -69,6 +69,7 @@ input[type="radio"] { margin:0 3px 0 0; padding:0; border:none; + border-radius:0; width:auto; } .elgg-input-checkboxes.elgg-horizontal li, -- cgit v1.2.3 From 76b7a8f78975098a5cd09f794feb34d4f7a4ee76 Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Sun, 1 Dec 2013 21:50:20 -0500 Subject: Make sure new lang key available during upgrade --- engine/lib/upgrades/2013060900-1.8.15-site_secret-404fc165cf9e0ac9.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/engine/lib/upgrades/2013060900-1.8.15-site_secret-404fc165cf9e0ac9.php b/engine/lib/upgrades/2013060900-1.8.15-site_secret-404fc165cf9e0ac9.php index b5b614762..538d74dd6 100644 --- a/engine/lib/upgrades/2013060900-1.8.15-site_secret-404fc165cf9e0ac9.php +++ b/engine/lib/upgrades/2013060900-1.8.15-site_secret-404fc165cf9e0ac9.php @@ -9,5 +9,8 @@ $strength = _elgg_get_site_secret_strength(); if ($strength !== 'strong') { + // a new key is needed immediately + register_translations(elgg_get_root_path() . 'languages/'); + elgg_add_admin_notice('weak_site_key', elgg_echo("upgrade:site_secret_warning:$strength")); } -- cgit v1.2.3 From 47f209929b1913a73b8051d35d5545d28a37dba7 Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Sun, 1 Dec 2013 21:56:48 -0500 Subject: Code style fixes in ElggCrypto --- engine/classes/ElggCrypto.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/engine/classes/ElggCrypto.php b/engine/classes/ElggCrypto.php index b6a8b2024..317d371e4 100644 --- a/engine/classes/ElggCrypto.php +++ b/engine/classes/ElggCrypto.php @@ -96,12 +96,12 @@ class ElggCrypto { } do { - $bytes = ($total > $hash_len)? $hash_len : $total; + $bytes = ($total > $hash_len) ? $hash_len : $total; $total -= $bytes; //collect any entropy available from the PHP system and filesystem $entropy = rand() . uniqid(mt_rand(), true) . $SSLstr; - $entropy .= implode('', @fstat(@fopen( __FILE__, 'r'))); + $entropy .= implode('', @fstat(@fopen(__FILE__, 'r'))); $entropy .= memory_get_usage() . getmypid(); $entropy .= serialize($_ENV) . serialize($_SERVER); if (function_exists('posix_times')) { @@ -113,7 +113,7 @@ class ElggCrypto { if ($handle) { $entropy .= @fread($handle, $bytes); - } else { + } else { // Measure the time that the operations will take on average for ($i = 0; $i < 3; $i++) { $c1 = microtime(true); @@ -162,9 +162,9 @@ class ElggCrypto { * Uses supplied character list for generating the new string. * If no character list provided - uses Base64 URL character set. * - * @param int $length Desired length of the string - * @param string|null $chars Characters to be chosen from randomly. If not given, the Base64 URL - * charset will be used. + * @param int $length Desired length of the string + * @param string|null $chars Characters to be chosen from randomly. If not given, the Base64 URL + * charset will be used. * * @return string The random string * -- cgit v1.2.3 From beab3edd8f0b821b7e90e288add261342505321f Mon Sep 17 00:00:00 2001 From: Ed Lyons Date: Mon, 2 Dec 2013 06:34:08 -0500 Subject: Fix #6238 Return blank arrays I committed this simple change to return the empty array rather than false for empty settings result for ElggPlugin::getAllSettings and getAllUsersSettings. Put this into 1.8 branch. --- engine/classes/ElggPlugin.php | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/engine/classes/ElggPlugin.php b/engine/classes/ElggPlugin.php index 7bf6eb1df..81831d8cd 100644 --- a/engine/classes/ElggPlugin.php +++ b/engine/classes/ElggPlugin.php @@ -299,17 +299,16 @@ class ElggPlugin extends ElggObject { $private_settings = get_data($q); + $return = array(); + if ($private_settings) { - $return = array(); - + foreach ($private_settings as $setting) { $return[$setting->name] = $setting->value; } + } - return $return; - } - - return false; + return $return; } /** @@ -423,9 +422,10 @@ class ElggPlugin extends ElggObject { $private_settings = get_data($q); - if ($private_settings) { - $return = array(); - + $return = array(); + + if ($private_settings) { + foreach ($private_settings as $setting) { $name = substr($setting->name, $ps_prefix_len); $value = $setting->value; @@ -433,10 +433,9 @@ class ElggPlugin extends ElggObject { $return[$name] = $value; } - return $return; } - return false; + return $return; } /** -- cgit v1.2.3 From 908628e622d798c5036f7e25bc5b7d6d36d64754 Mon Sep 17 00:00:00 2001 From: Jerome Bakker Date: Mon, 2 Dec 2013 14:56:20 +0100 Subject: fixes #3143 login part --- engine/lib/memcache.php | 20 ++++++++++++++++++++ engine/lib/sessions.php | 6 ++++++ 2 files changed, 26 insertions(+) diff --git a/engine/lib/memcache.php b/engine/lib/memcache.php index f79fba4a9..79b87e850 100644 --- a/engine/lib/memcache.php +++ b/engine/lib/memcache.php @@ -35,3 +35,23 @@ function is_memcache_available() { return $memcache_available; } + +/** + * Invalidate an entity in memcache + * + * @param int $entity_guid The GUID of the entity to invalidate + * + * @return void + * @access private + */ +function _elgg_invalidate_memcache_for_entity($entity_guid) { + static $newentity_cache; + + if ((!$newentity_cache) && (is_memcache_available())) { + $newentity_cache = new ElggMemcache('new_entity_cache'); + } + + if ($newentity_cache) { + $newentity_cache->delete($entity_guid); + } +} \ No newline at end of file diff --git a/engine/lib/sessions.php b/engine/lib/sessions.php index fb28e1e9a..e3d5ce9cd 100644 --- a/engine/lib/sessions.php +++ b/engine/lib/sessions.php @@ -326,6 +326,12 @@ function login(ElggUser $user, $persistent = false) { set_last_login($_SESSION['guid']); reset_login_failure_count($user->guid); // Reset any previous failed login attempts + // if memcache is enabled, invalidate the user in memcache @see https://github.com/Elgg/Elgg/issues/3143 + if (is_memcache_available()) { + // this needs to happen with a shutdown function because of the timing with set_last_login() + register_shutdown_function("_elgg_invalidate_memcache_for_entity", $_SESSION['guid']); + } + return true; } -- cgit v1.2.3 From 63db31c0237d24247b3b75b8365d610be936c283 Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Sat, 7 Dec 2013 19:27:18 -0500 Subject: Handle case if get_user_notification_settings() returns false --- engine/lib/notification.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/engine/lib/notification.php b/engine/lib/notification.php index b6399b3c6..2506867d5 100644 --- a/engine/lib/notification.php +++ b/engine/lib/notification.php @@ -110,12 +110,15 @@ function notify_user($to, $from, $subject, $message, array $params = NULL, $meth // Are we overriding delivery? $methods = $methods_override; if (!$methods) { - $tmp = (array)get_user_notification_settings($guid); + $tmp = get_user_notification_settings($guid); $methods = array(); - foreach ($tmp as $k => $v) { - // Add method if method is turned on for user! - if ($v) { - $methods[] = $k; + // $tmp may be false. don't cast + if (is_array($tmp)) { + foreach ($tmp as $k => $v) { + // Add method if method is turned on for user! + if ($v) { + $methods[] = $k; + } } } } -- cgit v1.2.3 From f4420e017f6dc2039e8e3910e5b98c0eb17a7be2 Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Sat, 7 Dec 2013 20:27:32 -0500 Subject: Fixes #6012: Gets correct client IP behind proxy (1.8) --- engine/lib/system_log.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/engine/lib/system_log.php b/engine/lib/system_log.php index 5a153afb2..bed863755 100644 --- a/engine/lib/system_log.php +++ b/engine/lib/system_log.php @@ -187,7 +187,11 @@ function system_log($object, $event) { $object_subtype = $object->getSubtype(); $event = sanitise_string($event); $time = time(); - $ip_address = sanitise_string($_SERVER['REMOTE_ADDR']); + if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { + $ip_address = array_pop(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])); + } else { + $ip_address = sanitise_string($_SERVER['REMOTE_ADDR']); + } $performed_by = elgg_get_logged_in_user_guid(); if (isset($object->access_id)) { -- cgit v1.2.3 From 5866c12f7b5cc7c9fa922324a26c419a66fc5ea3 Mon Sep 17 00:00:00 2001 From: Paweł Sroka Date: Sun, 8 Dec 2013 04:13:02 +0100 Subject: Checks X-Real-Ip header as well when determining client IP --- engine/lib/system_log.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/engine/lib/system_log.php b/engine/lib/system_log.php index bed863755..84302632e 100644 --- a/engine/lib/system_log.php +++ b/engine/lib/system_log.php @@ -187,11 +187,16 @@ function system_log($object, $event) { $object_subtype = $object->getSubtype(); $event = sanitise_string($event); $time = time(); + if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ip_address = array_pop(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])); + } elseif (!empty($_SERVER['HTTP_X_REAL_IP'])) { + $ip_address = array_pop(explode(',', $_SERVER['HTTP_X_REAL_IP'])); } else { - $ip_address = sanitise_string($_SERVER['REMOTE_ADDR']); + $ip_address = $_SERVER['REMOTE_ADDR']; } + $ip_address = sanitise_string($ip_address); + $performed_by = elgg_get_logged_in_user_guid(); if (isset($object->access_id)) { -- cgit v1.2.3 From 54d559f8ce84877f4dc2ccf84d00fd5047669d88 Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Mon, 9 Dec 2013 09:26:28 -0500 Subject: Code style fixes for recent PR --- engine/classes/ElggPlugin.php | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/engine/classes/ElggPlugin.php b/engine/classes/ElggPlugin.php index 81831d8cd..545b9a53c 100644 --- a/engine/classes/ElggPlugin.php +++ b/engine/classes/ElggPlugin.php @@ -300,13 +300,12 @@ class ElggPlugin extends ElggObject { $private_settings = get_data($q); $return = array(); - + if ($private_settings) { - foreach ($private_settings as $setting) { $return[$setting->name] = $setting->value; } - } + } return $return; } @@ -423,16 +422,14 @@ class ElggPlugin extends ElggObject { $private_settings = get_data($q); $return = array(); - - if ($private_settings) { - + + if ($private_settings) { foreach ($private_settings as $setting) { $name = substr($setting->name, $ps_prefix_len); $value = $setting->value; $return[$name] = $value; } - } return $return; -- cgit v1.2.3 From d1d37b4116338ebfa0871f74776c36a5549a4591 Mon Sep 17 00:00:00 2001 From: Juho Jaakkola Date: Wed, 11 Dec 2013 09:06:42 +0200 Subject: Makes sure all group pages respect the limited_groups setting --- mod/groups/lib/groups.php | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/mod/groups/lib/groups.php b/mod/groups/lib/groups.php index 77d7c09cc..f07ab5dc6 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'); @@ -115,7 +115,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 +152,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 +185,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 +208,7 @@ function groups_handle_edit_page($page, $guid = 0) { $content = elgg_echo('groups:noaccess'); } } - + $params = array( 'content' => $content, 'title' => $title, @@ -266,7 +270,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 +279,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 +338,7 @@ function groups_handle_activity_page($guid) { if (!$content) { $content = '

    ' . elgg_echo('groups:activity:none') . '

    '; } - + $params = array( 'content' => $content, 'title' => $title, @@ -427,7 +431,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) { @@ -443,7 +447,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', -- cgit v1.2.3 From b0a62995706d2b7796ad30c7e0bc6471cb268de0 Mon Sep 17 00:00:00 2001 From: Brett Profitt Date: Wed, 11 Dec 2013 09:28:08 -0500 Subject: Fixed directions for going to the site secret generation page. --- languages/en.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/languages/en.php b/languages/en.php index 4eed63d4c..07407d1e1 100644 --- a/languages/en.php +++ b/languages/en.php @@ -1093,8 +1093,8 @@ Once you have logged in, we highly recommend that you change your password. 'update:twitter_api:deactivated' => 'Twitter API (previously Twitter Service) was deactivated during the upgrade. Please activate it manually if required.', 'update:oauth_api:deactivated' => 'OAuth API (previously OAuth Lib) was deactivated during the upgrade. Please activate it manually if required.', - 'upgrade:site_secret_warning:moderate' => "You are encouraged to regenerate your site key to improve system security. See Configure > Site Secret", - 'upgrade:site_secret_warning:weak' => "You are strongly encouraged to regenerate your site key to improve system security. See Configure > Site Secret", + 'upgrade:site_secret_warning:moderate' => "You are encouraged to regenerate your site key to improve system security. See Configure > Settings > Site Secret", + 'upgrade:site_secret_warning:weak' => "You are strongly encouraged to regenerate your site key to improve system security. See Configure > Settings > Site Secret", 'deprecated:function' => '%s() was deprecated by %s()', -- cgit v1.2.3 From c6ec6a0e01f50f72e742bbf20431f3f3aa04a2fa Mon Sep 17 00:00:00 2001 From: Paweł Sroka Date: Sat, 28 Dec 2013 18:55:36 +0100 Subject: Added missing @deprecated tags --- engine/lib/deprecated-1.7.php | 2 ++ engine/lib/deprecated-1.8.php | 2 ++ 2 files changed, 4 insertions(+) diff --git a/engine/lib/deprecated-1.7.php b/engine/lib/deprecated-1.7.php index 519eea89d..ee95b5611 100644 --- a/engine/lib/deprecated-1.7.php +++ b/engine/lib/deprecated-1.7.php @@ -1137,6 +1137,7 @@ function make_register_object($register_name, $register_value, $children_array = * @param int $guid GUID * * @return 1 + * @deprecated 1.7 */ function delete_object_entity($guid) { system_message(elgg_echo('deprecatedfunction', array('delete_user_entity'))); @@ -1154,6 +1155,7 @@ function delete_object_entity($guid) { * @param int $guid User GUID * * @return 1 + * @deprecated 1.7 */ function delete_user_entity($guid) { system_message(elgg_echo('deprecatedfunction', array('delete_user_entity'))); diff --git a/engine/lib/deprecated-1.8.php b/engine/lib/deprecated-1.8.php index 6aa42a81d..91068d047 100644 --- a/engine/lib/deprecated-1.8.php +++ b/engine/lib/deprecated-1.8.php @@ -3414,6 +3414,7 @@ function list_annotations($entity_guid, $name = "", $limit = 25, $asc = true) { * @param unknown_type $timeupper * @param unknown_type $calculation * @internal Don't use this at all. + * @deprecated 1.8 Use elgg_get_annotations() */ function elgg_deprecated_annotation_calculation($entity_guid = 0, $entity_type = "", $entity_subtype = "", $name = "", $value = "", $value_type = "", $owner_guid = 0, $timelower = 0, @@ -4667,6 +4668,7 @@ function display_widget(ElggObject $widget) { * * @param ElggEntity $entity * @return int Number of comments + * @deprecated 1.8 Use ElggEntity->countComments() */ function elgg_count_comments($entity) { elgg_deprecated_notice('elgg_count_comments() is deprecated by ElggEntity->countComments()', 1.8); -- cgit v1.2.3 From 82b30f63043eba9c18999bd2a15301d62ead4a76 Mon Sep 17 00:00:00 2001 From: Paweł Sroka Date: Wed, 1 Jan 2014 12:42:10 +0100 Subject: Preparing 1.8.17 release --- CHANGES.txt | 43 +++++++++++++++++++++++++++++++++++++++++++ version.php | 4 ++-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 187dc7e25..819378e12 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,46 @@ +Version 1.8.17 +(January 1, 2014 from https://github.com/Elgg/Elgg/tree/1.8) + Contributing Developers: + * Brett Profitt + * Cash Costello + * Ed Lyons + * Evan Winslow + * Jeroen Dalsem + * Jerome Bakker + * Juho Jaakkola + * Matt Beckett + * Paweł Sroka + * Sem + * Steve Clay + + Security Fixes: + * Specially-crafted request could return the contents of sensitive files. + * Reflected XSS attack was possible against 1.8 systems. + * The cryptographic key used for various purposes may have been generated with weak entropy, particularly on Windows. + + Bugfixes: + * URLs with non-ASCII usernames again work + * Floated images are now properly cleared in content areas + * The activity page title now matches the document title + * Search again supports multiple comments on the same entity + * Blog archive sidebar now reverse chronological + * URLs with matching parens can now be auto-linked + * Log browser links for users now work + * Disabling over 50 objects should no longer result in an infinite loop + * Radio/checkbox inputs no longer have border radius (for IE10) + * User picker: the Only Friends checkbox again works + * Group bookmarklet no longer shown to non-members + * Widget reordering fixed when moving across columns + * Refuse to deactivate plugins needed as dependencies + + Enhancements: + * Group member listings are ordered by name + * The system_log table can now store IPv6 addresses + * Web services auth_gettoken() now accepts email address + * List functions: no need to specify pagination for unlimited queries + * Htmlawed was upgraded to 1.1.16 + + Version 1.8.16 (June 25, 2013 from https://github.com/Elgg/Elgg/tree/1.8) Contributing Developers: diff --git a/version.php b/version.php index a94bf9d31..c29b44c07 100644 --- a/version.php +++ b/version.php @@ -11,7 +11,7 @@ // YYYYMMDD = Elgg Date // XX = Interim incrementer -$version = 2013052900; +$version = 2014010100; // Human-friendly version name -$release = '1.8.16'; +$release = '1.8.17'; -- cgit v1.2.3 From 2634e94664045e08b3960dc83487d6357ec081a7 Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Mon, 6 Jan 2014 23:39:29 -0500 Subject: Fixes #6309: Fixes notify_user() broken by 63db31c --- engine/lib/notification.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/lib/notification.php b/engine/lib/notification.php index 2506867d5..be0c359d4 100644 --- a/engine/lib/notification.php +++ b/engine/lib/notification.php @@ -113,7 +113,7 @@ function notify_user($to, $from, $subject, $message, array $params = NULL, $meth $tmp = get_user_notification_settings($guid); $methods = array(); // $tmp may be false. don't cast - if (is_array($tmp)) { + if (is_object($tmp)) { foreach ($tmp as $k => $v) { // Add method if method is turned on for user! if ($v) { @@ -168,7 +168,7 @@ function notify_user($to, $from, $subject, $message, array $params = NULL, $meth * * @param int $user_guid The user id * - * @return stdClass + * @return stdClass|false */ function get_user_notification_settings($user_guid = 0) { $user_guid = (int)$user_guid; -- cgit v1.2.3 From 0dd36c458d41e77521c36ae572fe73114ad4bc5a Mon Sep 17 00:00:00 2001 From: Steve Clay Date: Sat, 11 Jan 2014 19:54:34 -0500 Subject: Prepare for 1.8.18 release --- CHANGES.txt | 10 ++++++++++ version.php | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 819378e12..f6974a3ae 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,13 @@ +Version 1.8.18 +(January 11, 2014 from https://github.com/Elgg/Elgg/tree/1.8) + Contributing Developers: + * Juho Jaakkola + * Steve Clay + + Bugfixes: + * Fixes notify_user() broken in 1.8.17 + + Version 1.8.17 (January 1, 2014 from https://github.com/Elgg/Elgg/tree/1.8) Contributing Developers: diff --git a/version.php b/version.php index c29b44c07..a7a4776a4 100644 --- a/version.php +++ b/version.php @@ -11,7 +11,7 @@ // YYYYMMDD = Elgg Date // XX = Interim incrementer -$version = 2014010100; +$version = 2014110100; // Human-friendly version name -$release = '1.8.17'; +$release = '1.8.18'; -- cgit v1.2.3