From 6487daa4a9c4ce6700e9fcaa2e1fc5031910923b Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 29 Apr 2020 06:28:06 +0000 Subject: [PATCH 01/28] Issue 8550: Check for a good table_definition cache value --- src/Database/DBA.php | 11 +++++++++++ src/Database/Database.php | 15 +++++++++++++++ src/Module/Admin/Summary.php | 10 ++++++++++ 3 files changed, 36 insertions(+) diff --git a/src/Database/DBA.php b/src/Database/DBA.php index 9c3c3a52e..9825d06c6 100644 --- a/src/Database/DBA.php +++ b/src/Database/DBA.php @@ -741,6 +741,17 @@ class DBA return DI::dba()->processlist(); } + /** + * Fetch a database variable + * + * @param string $name + * @return string content + */ + public static function getVariable(string $name) + { + return DI::dba()->getVariable($name); + } + /** * Checks if $array is a filled array with at least one entry. * diff --git a/src/Database/Database.php b/src/Database/Database.php index db906bd01..9892fc19a 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -1645,6 +1645,21 @@ class Database return (["list" => $statelist, "amount" => $processes]); } + /** + * Fetch a database variable + * + * @param string $name + * @return string content + */ + public function getVariable(string $name) + { + $result = $this->fetchFirst("SHOW GLOBAL VARIABLES WHERE `Variable_name` = ?", $name); + if (!isset($result['Value'])) { + return null; + } + return $result['Value']; + } + /** * Checks if $array is a filled array with at least one entry. * diff --git a/src/Module/Admin/Summary.php b/src/Module/Admin/Summary.php index f676792d8..7bd7901f7 100644 --- a/src/Module/Admin/Summary.php +++ b/src/Module/Admin/Summary.php @@ -55,6 +55,16 @@ class Summary extends BaseAdmin $warningtext[] = DI::l10n()->t('Your DB still runs with InnoDB tables in the Antelope file format. You should change the file format to Barracuda. Friendica is using features that are not provided by the Antelope format. See here for a guide that may be helpful converting the table engines. You may also use the command php bin/console.php dbstructure toinnodb of your Friendica installation for an automatic conversion.
', 'https://dev.mysql.com/doc/refman/5.7/en/innodb-file-format.html'); } + // Avoid the database error 1615 "Prepared statement needs to be re-prepared", see https://github.com/friendica/friendica/issues/8550 + $table_definition_cache = DBA::getVariable('table_definition_cache'); + $table_open_cache = DBA::getVariable('table_open_cache'); + if (!empty($table_definition_cache) && !empty($table_open_cache)) { + $suggested_definition_cache = max(400, round($table_open_cache / 2, 1)); + if ($suggested_definition_cache > $table_definition_cache) { + $warningtext[] = DI::l10n()->t('Your table_definition_cache is too low (%d). This can lead to the database error "Prepared statement needs to be re-prepared". Please set it at least to %d (or -1 for autosizing). See here for more information.
', $table_definition_cache, $suggested_definition_cache, 'https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_table_definition_cache'); + } + } + // Check if github.com/friendica/master/VERSION is higher then // the local version of Friendica. Check is opt-in, source may be master or devel branch if (DI::config()->get('system', 'check_new_version_url', 'none') != 'none') { From e1d21e736a08840ce8a96b33fe233aee781e5647 Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Wed, 29 Apr 2020 19:13:23 +0200 Subject: [PATCH 02/28] Update src/Database/Database.php Co-Authored-By: Hypolite Petovan --- src/Database/Database.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Database/Database.php b/src/Database/Database.php index 9892fc19a..ad0c85796 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -1654,10 +1654,7 @@ class Database public function getVariable(string $name) { $result = $this->fetchFirst("SHOW GLOBAL VARIABLES WHERE `Variable_name` = ?", $name); - if (!isset($result['Value'])) { - return null; - } - return $result['Value']; + return $result['Value'] ?? null; } /** From 11406fd3b66fadece61c33defdf16f61be0421ca Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 2 May 2020 05:14:30 +0000 Subject: [PATCH 03/28] Fix for "Undefined index: uri-id"" --- src/Protocol/ActivityPub/Processor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Protocol/ActivityPub/Processor.php b/src/Protocol/ActivityPub/Processor.php index 43343f1e6..479d8d55c 100644 --- a/src/Protocol/ActivityPub/Processor.php +++ b/src/Protocol/ActivityPub/Processor.php @@ -387,7 +387,7 @@ class Processor if (empty($activity['directmessage']) && ($item['thr-parent'] != $item['uri']) && ($item['gravity'] == GRAVITY_COMMENT)) { $item_private = !in_array(0, $activity['item_receiver']); - $parent = Item::selectFirst(['id', 'private', 'author-link', 'alias'], ['uri' => $item['thr-parent']]); + $parent = Item::selectFirst(['id', 'uri-id', 'private', 'author-link', 'alias'], ['uri' => $item['thr-parent']]); if (!DBA::isResult($parent)) { Logger::warning('Unknown parent item.', ['uri' => $item['thr-parent']]); return false; From 2a1cdfa36b05897221831af5206f6e71d4b4932e Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 2 May 2020 08:52:11 +0000 Subject: [PATCH 04/28] Diaspora participations are now atored as activities --- database.sql | 42 +++++++++-------- src/Protocol/Diaspora.php | 96 ++++++++++++++++++++++++++------------- src/Worker/Notifier.php | 2 +- static/dbview.config.php | 14 ------ 4 files changed, 89 insertions(+), 65 deletions(-) diff --git a/database.sql b/database.sql index a68f3d4e5..6c4758c24 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2020.06-dev (Red Hot Poker) --- DB_UPDATE_VERSION 1341 +-- DB_UPDATE_VERSION 1343 -- ------------------------------------------ @@ -666,7 +666,8 @@ CREATE TABLE IF NOT EXISTS `item` ( INDEX `uid_eventid` (`uid`,`event-id`), INDEX `icid` (`icid`), INDEX `iaid` (`iaid`), - INDEX `psid_wall` (`psid`,`wall`) + INDEX `psid_wall` (`psid`,`wall`), + INDEX `uri-id` (`uri-id`) ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Structure for all posts'; -- @@ -1539,22 +1540,6 @@ CREATE VIEW `owner-view` AS SELECT INNER JOIN `contact` ON `contact`.`uid` = `user`.`uid` AND `contact`.`self` INNER JOIN `profile` ON `profile`.`uid` = `user`.`uid`; --- --- VIEW participation-view --- -DROP VIEW IF EXISTS `participation-view`; -CREATE VIEW `participation-view` AS SELECT - `participation`.`iid` AS `iid`, - `contact`.`id` AS `id`, - `contact`.`url` AS `url`, - `contact`.`name` AS `name`, - `contact`.`protocol` AS `protocol`, - CASE `contact`.`batch` WHEN '' THEN `fcontact`.`batch` ELSE `contact`.`batch` END AS `batch`, - CASE `fcontact`.`network` WHEN '' THEN `contact`.`network` ELSE `fcontact`.`network` END AS `network` - FROM `participation` - INNER JOIN `contact` ON `contact`.`id` = `participation`.`cid` AND NOT `contact`.`archive` - INNER JOIN `fcontact` ON `fcontact`.`id` = `participation`.`fid`; - -- -- VIEW pending-view -- @@ -1577,6 +1562,27 @@ CREATE VIEW `pending-view` AS SELECT INNER JOIN `contact` ON `register`.`uid` = `contact`.`uid` INNER JOIN `user` ON `register`.`uid` = `user`.`uid`; +-- +-- VIEW tag-search-view +-- +DROP VIEW IF EXISTS `tag-search-view`; +CREATE VIEW `tag-search-view` AS SELECT + `post-tag`.`uri-id` AS `uri-id`, + `item`.`id` AS `iid`, + `item`.`uri` AS `uri`, + `item`.`guid` AS `guid`, + `item`.`uid` AS `uid`, + `item`.`private` AS `private`, + `item`.`wall` AS `wall`, + `item`.`origin` AS `origin`, + `item`.`gravity` AS `gravity`, + `item`.`received` AS `received`, + `tag`.`name` AS `name` + FROM `post-tag` + INNER JOIN `tag` ON `tag`.`id` = `post-tag`.`tid` + INNER JOIN `item` ON `item`.`uri-id` = `post-tag`.`uri-id` + WHERE `post-tag`.`type` = 1; + -- -- VIEW workerqueue-view -- diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index c8d218ac2..a0047d008 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -252,19 +252,25 @@ class Diaspora * One of the parameters is a contact array. * This is done to avoid duplicates. * - * @param integer $thread The id of the thread - * @param array $contacts The previously fetched contacts + * @param array $parent The parent post + * @param array $contacts The previously fetched contacts * * @return array of relay servers * @throws \Exception */ - public static function participantsForThread($thread, array $contacts) + public static function participantsForThread(array $parent, array $contacts) { - $participation = DBA::select('participation-view', [], ['iid' => $thread]); + if (!in_array($parent['private'], [Item::PUBLIC, Item::UNLISTED])) { + return $contacts; + } - while ($contact = DBA::fetch($participation)) { - if (empty($contact['protocol'])) { - $contact['protocol'] = $contact['network']; + $items = Item::select(['author-id'], ['parent' => $parent['id']], ['group_by' => ['author-id']]); + while ($item = DBA::fetch($items)) { + $contact = DBA::selectFirst('contact', ['id', 'url', 'name', 'protocol', 'batch', 'network'], + ['id' => $item['author-id']]); + if (!DBA::isResult($contact)) { + // Shouldn't happen + continue; } $exists = false; @@ -275,11 +281,11 @@ class Diaspora } if (!$exists) { + Logger::info('Add participant to receiver list', ['item' => $parent['guid'], 'participant' => $contact['url']]); $contacts[] = $contact; } } - - DBA::close($participation); + DBA::close($items); return $contacts; } @@ -2251,18 +2257,32 @@ class Diaspora * @param array $importer Array of the importer user * @param object $data The message object * - * @return bool always true + * @return bool success * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ private static function receiveParticipation(array $importer, $data) { $author = strtolower(Strings::escapeTags(XML::unescape($data->author))); + $guid = Strings::escapeTags(XML::unescape($data->guid)); $parent_guid = Strings::escapeTags(XML::unescape($data->parent_guid)); - $contact_id = Contact::getIdForURL($author); - if (!$contact_id) { - Logger::log('Contact not found: '.$author); + $contact = self::allowedContactByHandle($importer, $author, true); + if (!$contact) { + return false; + } + + if (self::messageExists($importer["uid"], $guid)) { + return true; + } + + $parent_item = self::parentItem($importer["uid"], $parent_guid, $author, $contact); + if (!$parent_item) { + return false; + } + + if (!in_array($parent_item['private'], [Item::PUBLIC, Item::UNLISTED])) { + Logger::info('Item is not public, participation is ignored', ['parent_guid' => $parent_guid, 'guid' => $guid, 'author' => $author]); return false; } @@ -2272,35 +2292,47 @@ class Diaspora return false; } - $item = Item::selectFirst(['id'], ['guid' => $parent_guid, 'origin' => true, 'private' => [Item::PUBLIC, Item::UNLISTED]]); - if (!DBA::isResult($item)) { - Logger::log('Item not found, no origin or private: '.$parent_guid); - return false; - } + $author_contact = self::authorContactByUrl($contact, $person, $importer["uid"]); - $author_parts = explode('@', $author); - if (isset($author_parts[1])) { - $server = $author_parts[1]; - } else { - // Should never happen - $server = $author; - } + // Store participation + $datarray = []; - Logger::log('Received participation for ID: '.$item['id'].' - Contact: '.$contact_id.' - Server: '.$server, Logger::DEBUG); + $datarray["protocol"] = Conversation::PARCEL_DIASPORA; - if (!DBA::exists('participation', ['iid' => $item['id'], 'server' => $server])) { - DBA::insert('participation', ['iid' => $item['id'], 'cid' => $contact_id, 'fid' => $person['id'], 'server' => $server]); - } + $datarray["uid"] = $importer["uid"]; + $datarray["contact-id"] = $author_contact["cid"]; + $datarray["network"] = $author_contact["network"]; + + $datarray["owner-link"] = $datarray["author-link"] = $person["url"]; + $datarray["owner-id"] = $datarray["author-id"] = Contact::getIdForURL($person["url"], 0); + + $datarray["guid"] = $guid; + $datarray["uri"] = self::getUriFromGuid($author, $guid); + + $datarray["verb"] = Activity::FOLLOW; + $datarray["gravity"] = GRAVITY_ACTIVITY; + $datarray["parent-uri"] = $parent_item["uri"]; + + $datarray["object-type"] = Activity\ObjectType::NOTE; + + $datarray["body"] = Activity::FOLLOW; + + // Diaspora doesn't provide a date for a participation + $datarray["changed"] = $datarray["created"] = $datarray["edited"] = DateTimeFormat::utcNow(); + + $message_id = Item::insert($datarray); + + Logger::info('Participation stored', ['id' => $message_id, 'guid' => $guid, 'parent_guid' => $parent_guid, 'author' => $author]); // Send all existing comments and likes to the requesting server - $comments = Item::select(['id', 'parent', 'verb', 'self'], ['parent' => $item['id']]); + $comments = Item::select(['id', 'parent', 'verb', 'self'], ['parent' => $parent_item['id']]); while ($comment = Item::fetch($comments)) { if ($comment['id'] == $comment['parent']) { continue; } - Logger::info('Deliver participation', ['item' => $comment['id'], 'contact' => $contact_id]); - if (Worker::add(PRIORITY_HIGH, 'Delivery', Delivery::POST, $comment['id'], $contact_id)) { + Logger::info('Deliver participation', ['item' => $comment['id'], 'contact' => $author_contact["cid"]]); + if (Worker::add(PRIORITY_HIGH, 'Delivery', Delivery::POST, $comment['id'], $author_contact["cid"])) { ItemDeliveryData::incrementQueueCount($comment['id'], 1); } } diff --git a/src/Worker/Notifier.php b/src/Worker/Notifier.php index 35a228fce..c9a328e4c 100644 --- a/src/Worker/Notifier.php +++ b/src/Worker/Notifier.php @@ -441,7 +441,7 @@ class Notifier // Fetch the participation list // The function will ensure that there are no duplicates - $relay_list = Diaspora::participantsForThread($target_id, $relay_list); + $relay_list = Diaspora::participantsForThread($parent, $relay_list); // Add the relay to the list, avoid duplicates. // Don't send community posts to the relay. Forum posts via the Diaspora protocol are looking ugly. diff --git a/static/dbview.config.php b/static/dbview.config.php index 0f0d69108..13408d27a 100755 --- a/static/dbview.config.php +++ b/static/dbview.config.php @@ -186,20 +186,6 @@ return [ INNER JOIN `contact` ON `contact`.`uid` = `user`.`uid` AND `contact`.`self` INNER JOIN `profile` ON `profile`.`uid` = `user`.`uid`" ], - "participation-view" => [ - "fields" => [ - "iid" => ["participation", "iid"], - "id" => ["contact", "id"], - "url" => ["contact", "url"], - "name" => ["contact", "name"], - "protocol" => ["contact", "protocol"], - "batch" => "CASE `contact`.`batch` WHEN '' THEN `fcontact`.`batch` ELSE `contact`.`batch` END", - "network" => "CASE `fcontact`.`network` WHEN '' THEN `contact`.`network` ELSE `fcontact`.`network` END", - ], - "query" => "FROM `participation` - INNER JOIN `contact` ON `contact`.`id` = `participation`.`cid` AND NOT `contact`.`archive` - INNER JOIN `fcontact` ON `fcontact`.`id` = `participation`.`fid`" - ], "pending-view" => [ "fields" => [ "id" => ["register", "id"], From 00035f7ba040b4047fffdaa9ab9e55107ce86139 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 2 May 2020 13:12:11 +0000 Subject: [PATCH 05/28] Notifications are now storing the uri-id as well --- include/enotify.php | 43 +++++++++++++++++++++++------------ static/dbstructure.config.php | 8 ++++--- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/include/enotify.php b/include/enotify.php index 2ae7a9915..bd96ca5d2 100644 --- a/include/enotify.php +++ b/include/enotify.php @@ -107,12 +107,24 @@ function notification($params) $item_id = 0; } + if (isset($params['item']['uri-id'])) { + $uri_id = $params['item']['uri-id']; + } else { + $uri_id = 0; + } + if (isset($params['parent'])) { $parent_id = $params['parent']; } else { $parent_id = 0; } + if (isset($params['item']['parent-uri-id'])) { + $parent_uri_id = $params['item']['parent-uri-id']; + } else { + $parent_uri_id = 0; + } + $epreamble = ''; $preamble = ''; $subject = ''; @@ -452,17 +464,19 @@ function notification($params) if ($show_in_notification_page) { $notification = DI::notify()->insert([ - 'name' => $params['source_name'] ?? '', - 'name_cache' => substr(strip_tags(BBCode::convert($params['source_name'] ?? '')), 0, 255), - 'url' => $params['source_link'] ?? '', - 'photo' => $params['source_photo'] ?? '', - 'link' => $itemlink ?? '', - 'uid' => $params['uid'] ?? 0, - 'iid' => $item_id ?? 0, - 'parent' => $parent_id ?? 0, - 'type' => $params['type'] ?? '', - 'verb' => $params['verb'] ?? '', - 'otype' => $params['otype'] ?? '', + 'name' => $params['source_name'] ?? '', + 'name_cache' => substr(strip_tags(BBCode::convert($params['source_name'] ?? '')), 0, 255), + 'url' => $params['source_link'] ?? '', + 'photo' => $params['source_photo'] ?? '', + 'link' => $itemlink ?? '', + 'uid' => $params['uid'] ?? 0, + 'iid' => $item_id ?? 0, + 'uri-id' => $uri_id ?? 0, + 'parent' => $parent_id ?? 0, + 'parent-uri-id' => $parent_uri_id ?? 0, + 'type' => $params['type'] ?? '', + 'verb' => $params['verb'] ?? '', + 'otype' => $params['otype'] ?? '', ]); $notification->msg = Renderer::replaceMacros($epreamble, ['$itemlink' => $notification->link]); @@ -486,8 +500,9 @@ function notification($params) if (!DBA::exists('notify-threads', ['master-parent-item' => $params['parent'], 'receiver-uid' => $params['uid']])) { Logger::log("notify_id:" . intval($notify_id) . ", parent: " . intval($params['parent']) . "uid: " . intval($params['uid']), Logger::DEBUG); - $fields = ['notify-id' => $notify_id, 'master-parent-item' => $params['parent'], - 'receiver-uid' => $params['uid'], 'parent-item' => 0]; + $fields = ['notify-id' => $notify_id, 'master-parent-item' => $params['parent'], + 'master-parent-uri-id' => $parent_uri_id, + 'receiver-uid' => $params['uid'], 'parent-item' => 0]; DBA::insert('notify-threads', $fields); $additional_mail_header .= "Message-ID: <${id_for_parent}>\n"; @@ -574,7 +589,7 @@ function check_user_notification($itemid) { * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ function check_item_notification($itemid, $uid, $notification_type) { - $fields = ['id', 'mention', 'tag', 'parent', 'title', 'body', + $fields = ['id', 'uri-id', 'mention', 'tag', 'parent', 'parent-uri-id', 'title', 'body', 'author-link', 'author-name', 'author-avatar', 'author-id', 'guid', 'parent-uri', 'uri', 'contact-id', 'network']; $condition = ['id' => $itemid, 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT], 'deleted' => false]; diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 2c3a9fb5f..6f4c069e5 100755 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -51,7 +51,7 @@ use Friendica\Database\DBA; if (!defined('DB_UPDATE_VERSION')) { - define('DB_UPDATE_VERSION', 1343); + define('DB_UPDATE_VERSION', 1344); } return [ @@ -927,6 +927,8 @@ return [ "link" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], "iid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "relation" => ["item" => "id"], "comment" => "item.id"], "parent" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "relation" => ["item" => "id"], "comment" => ""], + "uri-id" => ["type" => "int unsigned", "relation" => ["item-uri" => "id"], "comment" => "Item-uri id of the related post"], + "parent-uri-id" => ["type" => "int unsigned", "relation" => ["item-uri" => "id"], "comment" => "Item-uri id of the parent of the related post"], "seen" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""], "verb" => ["type" => "varchar(100)", "not null" => "1", "default" => "", "comment" => ""], "otype" => ["type" => "varchar(10)", "not null" => "1", "default" => "", "comment" => ""], @@ -945,8 +947,8 @@ return [ "fields" => [ "id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => "sequential ID"], "notify-id" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "relation" => ["notify" => "id"], "comment" => ""], - "master-parent-item" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "relation" => ["item" => "id"], - "comment" => ""], + "master-parent-item" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "relation" => ["item" => "id"], "comment" => ""], + "master-parent-uri-id" => ["type" => "int unsigned", "relation" => ["item-uri" => "id"], "comment" => "Item-uri id of the parent of the related post"], "parent-item" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "comment" => ""], "receiver-uid" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "0", "relation" => ["user" => "uid"], "comment" => "User id"], From 098ba4a667bb5d4d8563f096f408c282c7cdf735 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 2 May 2020 19:34:02 +0000 Subject: [PATCH 06/28] "item-delivery-data" is now "post-delivery-data" --- database.sql | 62 +++++++++++------ src/Model/Item.php | 23 ++++--- .../DeliveryData.php} | 68 +++++++++---------- src/Protocol/Diaspora.php | 6 +- src/Worker/APDelivery.php | 10 ++- src/Worker/Delivery.php | 52 +++++++------- src/Worker/Notifier.php | 8 +-- static/dbstructure.config.php | 38 +++++------ 8 files changed, 147 insertions(+), 120 deletions(-) rename src/Model/{ItemDeliveryData.php => Post/DeliveryData.php} (63%) diff --git a/database.sql b/database.sql index a68f3d4e5..320ef38e7 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2020.06-dev (Red Hot Poker) --- DB_UPDATE_VERSION 1341 +-- DB_UPDATE_VERSION 1343 -- ------------------------------------------ @@ -666,7 +666,8 @@ CREATE TABLE IF NOT EXISTS `item` ( INDEX `uid_eventid` (`uid`,`event-id`), INDEX `icid` (`icid`), INDEX `iaid` (`iaid`), - INDEX `psid_wall` (`psid`,`wall`) + INDEX `psid_wall` (`psid`,`wall`), + INDEX `uri-id` (`uri-id`) ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Structure for all posts'; -- @@ -714,24 +715,6 @@ CREATE TABLE IF NOT EXISTS `item-content` ( INDEX `uri-id` (`uri-id`) ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Content for all posts'; --- --- TABLE item-delivery-data --- -CREATE TABLE IF NOT EXISTS `item-delivery-data` ( - `iid` int unsigned NOT NULL COMMENT 'Item id', - `postopts` text COMMENT 'External post connectors add their network name to this comma-separated string to identify that they should be delivered to these networks during delivery', - `inform` mediumtext COMMENT 'Additional receivers of the linked item', - `queue_count` mediumint NOT NULL DEFAULT 0 COMMENT 'Initial number of delivery recipients, used as item.delivery_queue_count', - `queue_done` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries, used as item.delivery_queue_done', - `queue_failed` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of unsuccessful deliveries, used as item.delivery_queue_failed', - `activitypub` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via ActivityPub', - `dfrn` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via DFRN', - `legacy_dfrn` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via legacy DFRN', - `diaspora` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via Diaspora', - `ostatus` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via OStatus', - PRIMARY KEY(`iid`) -) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Delivery data for items'; - -- -- TABLE item-uri -- @@ -1187,6 +1170,24 @@ CREATE TABLE IF NOT EXISTS `tag` ( INDEX `url` (`url`) ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='tags and mentions'; +-- +-- TABLE post-delivery-data +-- +CREATE TABLE IF NOT EXISTS `post-delivery-data` ( + `uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the item uri', + `postopts` text COMMENT 'External post connectors add their network name to this comma-separated string to identify that they should be delivered to these networks during delivery', + `inform` mediumtext COMMENT 'Additional receivers of the linked item', + `queue_count` mediumint NOT NULL DEFAULT 0 COMMENT 'Initial number of delivery recipients, used as item.delivery_queue_count', + `queue_done` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries, used as item.delivery_queue_done', + `queue_failed` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of unsuccessful deliveries, used as item.delivery_queue_failed', + `activitypub` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via ActivityPub', + `dfrn` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via DFRN', + `legacy_dfrn` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via legacy DFRN', + `diaspora` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via Diaspora', + `ostatus` mediumint NOT NULL DEFAULT 0 COMMENT 'Number of successful deliveries via OStatus', + PRIMARY KEY(`uri-id`) +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Delivery data for items'; + -- -- TABLE post-tag -- @@ -1577,6 +1578,27 @@ CREATE VIEW `pending-view` AS SELECT INNER JOIN `contact` ON `register`.`uid` = `contact`.`uid` INNER JOIN `user` ON `register`.`uid` = `user`.`uid`; +-- +-- VIEW tag-search-view +-- +DROP VIEW IF EXISTS `tag-search-view`; +CREATE VIEW `tag-search-view` AS SELECT + `post-tag`.`uri-id` AS `uri-id`, + `item`.`id` AS `iid`, + `item`.`uri` AS `uri`, + `item`.`guid` AS `guid`, + `item`.`uid` AS `uid`, + `item`.`private` AS `private`, + `item`.`wall` AS `wall`, + `item`.`origin` AS `origin`, + `item`.`gravity` AS `gravity`, + `item`.`received` AS `received`, + `tag`.`name` AS `name` + FROM `post-tag` + INNER JOIN `tag` ON `tag`.`id` = `post-tag`.`tid` + INNER JOIN `item` ON `item`.`uri-id` = `post-tag`.`uri-id` + WHERE `post-tag`.`type` = 1; + -- -- VIEW workerqueue-view -- diff --git a/src/Model/Item.php b/src/Model/Item.php index 1096c2213..c01535ab6 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -283,7 +283,7 @@ class Item // Fetch data from the item-content table whenever there is content there if (self::isLegacyMode()) { - $legacy_fields = array_merge(ItemDeliveryData::LEGACY_FIELD_LIST, self::MIXED_CONTENT_FIELDLIST); + $legacy_fields = array_merge(Post\DeliveryData::LEGACY_FIELD_LIST, self::MIXED_CONTENT_FIELDLIST); foreach ($legacy_fields as $field) { if (empty($row[$field]) && !empty($row['internal-item-' . $field])) { $row[$field] = $row['internal-item-' . $field]; @@ -687,7 +687,7 @@ class Item $fields['item-content'] = array_merge(self::CONTENT_FIELDLIST, self::MIXED_CONTENT_FIELDLIST); - $fields['item-delivery-data'] = array_merge(ItemDeliveryData::LEGACY_FIELD_LIST, ItemDeliveryData::FIELD_LIST); + $fields['post-delivery-data'] = array_merge(Post\DeliveryData::LEGACY_FIELD_LIST, Post\DeliveryData::FIELD_LIST); $fields['permissionset'] = ['allow_cid', 'allow_gid', 'deny_cid', 'deny_gid']; @@ -809,8 +809,8 @@ class Item $joins .= " LEFT JOIN `item-content` ON `item-content`.`uri-id` = `item`.`uri-id`"; } - if (strpos($sql_commands, "`item-delivery-data`.") !== false) { - $joins .= " LEFT JOIN `item-delivery-data` ON `item-delivery-data`.`iid` = `item`.`id`"; + if (strpos($sql_commands, "`post-delivery-data`.") !== false) { + $joins .= " LEFT JOIN `post-delivery-data` ON `post-delivery-data`.`uri-id` = `item`.`uri-id` AND `item`.`origin`"; } if (strpos($sql_commands, "`permissionset`.") !== false) { @@ -850,7 +850,7 @@ class Item $selected[] = 'internal-user-ignored'; } - $legacy_fields = array_merge(ItemDeliveryData::LEGACY_FIELD_LIST, self::MIXED_CONTENT_FIELDLIST); + $legacy_fields = array_merge(Post\DeliveryData::LEGACY_FIELD_LIST, self::MIXED_CONTENT_FIELDLIST); $selection = []; foreach ($fields as $table => $table_fields) { @@ -936,7 +936,7 @@ class Item } } - $delivery_data = ItemDeliveryData::extractFields($fields); + $delivery_data = Post\DeliveryData::extractFields($fields); $clear_fields = ['bookmark', 'type', 'author-name', 'author-avatar', 'author-link', 'owner-name', 'owner-avatar', 'owner-link', 'postopts', 'inform']; foreach ($clear_fields as $field) { @@ -1038,7 +1038,7 @@ class Item } } - ItemDeliveryData::update($item['id'], $delivery_data); + Post\DeliveryData::update($item['uri-id'], $delivery_data); self::updateThread($item['id']); @@ -1118,7 +1118,7 @@ class Item { Logger::info('Mark item for deletion by id', ['id' => $item_id, 'callstack' => System::callstack()]); // locate item to be deleted - $fields = ['id', 'uri', 'uid', 'parent', 'parent-uri', 'origin', + $fields = ['id', 'uri', 'uri-id', 'uid', 'parent', 'parent-uri', 'origin', 'deleted', 'file', 'resource-id', 'event-id', 'attach', 'verb', 'object-type', 'object', 'target', 'contact-id', 'icid', 'iaid', 'psid']; @@ -1202,7 +1202,7 @@ class Item self::markForDeletion(['uri' => $item['uri'], 'uid' => 0, 'deleted' => false], $priority); } - ItemDeliveryData::delete($item['id']); + Post\DeliveryData::delete($item['uri-id']); // We don't delete the item-activity here, since we need some of the data for ActivityPub @@ -1903,7 +1903,8 @@ class Item self::insertContent($item); } - $delivery_data = ItemDeliveryData::extractFields($item); + $postfields = $item; + $delivery_data = Post\DeliveryData::extractFields($postfields); unset($item['postopts']); unset($item['inform']); @@ -2007,7 +2008,7 @@ class Item } if (!empty($item['origin']) || !empty($item['wall']) || !empty($delivery_data['postopts']) || !empty($delivery_data['inform'])) { - ItemDeliveryData::insert($current_post, $delivery_data); + Post\DeliveryData::insert($item['uri-id'], $delivery_data); } DBA::commit(); diff --git a/src/Model/ItemDeliveryData.php b/src/Model/Post/DeliveryData.php similarity index 63% rename from src/Model/ItemDeliveryData.php rename to src/Model/Post/DeliveryData.php index a26c80b1c..0feb38281 100644 --- a/src/Model/ItemDeliveryData.php +++ b/src/Model/Post/DeliveryData.php @@ -19,12 +19,12 @@ * */ -namespace Friendica\Model; +namespace Friendica\Model\Post; use Friendica\Database\DBA; use \BadMethodCallException; -class ItemDeliveryData +class DeliveryData { const LEGACY_FIELD_LIST = [ // Legacy fields moved from item table @@ -55,7 +55,7 @@ class ItemDeliveryData public static function extractFields(array &$fields) { $delivery_data = []; - foreach (array_merge(ItemDeliveryData::FIELD_LIST, ItemDeliveryData::LEGACY_FIELD_LIST) as $key => $field) { + foreach (array_merge(self::FIELD_LIST, self::LEGACY_FIELD_LIST) as $key => $field) { if (is_int($key) && isset($fields[$field])) { // Legacy field moved from item table $delivery_data[$field] = $fields[$field]; @@ -71,16 +71,16 @@ class ItemDeliveryData } /** - * Increments the queue_done for the given item ID. + * Increments the queue_done for the given URI ID. * * Avoids racing condition between multiple delivery threads. * - * @param integer $item_id + * @param integer $uri_id * @param integer $protocol * @return bool * @throws \Exception */ - public static function incrementQueueDone($item_id, $protocol = 0) + public static function incrementQueueDone(int $uri_id, int $protocol = 0) { $sql = ''; @@ -102,69 +102,69 @@ class ItemDeliveryData break; } - return DBA::e('UPDATE `item-delivery-data` SET `queue_done` = `queue_done` + 1' . $sql . ' WHERE `iid` = ?', $item_id); + return DBA::e('UPDATE `post-delivery-data` SET `queue_done` = `queue_done` + 1' . $sql . ' WHERE `uri-id` = ?', $uri_id); } /** - * Increments the queue_failed for the given item ID. + * Increments the queue_failed for the given URI ID. * * Avoids racing condition between multiple delivery threads. * - * @param integer $item_id + * @param integer $uri_id * @return bool * @throws \Exception */ - public static function incrementQueueFailed($item_id) + public static function incrementQueueFailed(int $uri_id) { - return DBA::e('UPDATE `item-delivery-data` SET `queue_failed` = `queue_failed` + 1 WHERE `iid` = ?', $item_id); + return DBA::e('UPDATE `post-delivery-data` SET `queue_failed` = `queue_failed` + 1 WHERE `uri-id` = ?', $uri_id); } /** - * Increments the queue_count for the given item ID. + * Increments the queue_count for the given URI ID. * - * @param integer $item_id + * @param integer $uri_id * @param integer $increment * @return bool * @throws \Exception */ - public static function incrementQueueCount(int $item_id, int $increment = 1) + public static function incrementQueueCount(int $uri_id, int $increment = 1) { - return DBA::e('UPDATE `item-delivery-data` SET `queue_count` = `queue_count` + ? WHERE `iid` = ?', $increment, $item_id); + return DBA::e('UPDATE `post-delivery-data` SET `queue_count` = `queue_count` + ? WHERE `uri-id` = ?', $increment, $uri_id); } /** - * Insert a new item delivery data entry + * Insert a new URI delivery data entry * - * @param integer $item_id + * @param integer $uri_id * @param array $fields * @return bool * @throws \Exception */ - public static function insert($item_id, array $fields) + public static function insert(int $uri_id, array $fields) { - if (empty($item_id)) { - throw new BadMethodCallException('Empty item_id'); + if (empty($uri_id)) { + throw new BadMethodCallException('Empty URI_id'); } - $fields['iid'] = $item_id; + $fields['uri-id'] = $uri_id; - return DBA::insert('item-delivery-data', $fields); + return DBA::insert('post-delivery-data', $fields); } /** - * Update/Insert item delivery data + * Update/Insert URI delivery data * * If you want to update queue_done, please use incrementQueueDone instead. * - * @param integer $item_id + * @param integer $uri_id * @param array $fields * @return bool * @throws \Exception */ - public static function update($item_id, array $fields) + public static function update(int $uri_id, array $fields) { - if (empty($item_id)) { - throw new BadMethodCallException('Empty item_id'); + if (empty($uri_id)) { + throw new BadMethodCallException('Empty URI_id'); } if (empty($fields)) { @@ -172,22 +172,22 @@ class ItemDeliveryData return true; } - return DBA::update('item-delivery-data', $fields, ['iid' => $item_id], true); + return DBA::update('post-delivery-data', $fields, ['uri-id' => $uri_id], true); } /** - * Delete item delivery data + * Delete URI delivery data * - * @param integer $item_id + * @param integer $uri_id * @return bool * @throws \Exception */ - public static function delete($item_id) + public static function delete(int $uri_id) { - if (empty($item_id)) { - throw new BadMethodCallException('Empty item_id'); + if (empty($uri_id)) { + throw new BadMethodCallException('Empty URI_id'); } - return DBA::delete('item-delivery-data', ['iid' => $item_id]); + return DBA::delete('post-delivery-data', ['uri-id' => $uri_id]); } } diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index a91c5cdf8..0a97f39a5 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -36,8 +36,8 @@ use Friendica\Model\Conversation; use Friendica\Model\GContact; use Friendica\Model\Item; use Friendica\Model\ItemURI; -use Friendica\Model\ItemDeliveryData; use Friendica\Model\Mail; +use Friendica\Model\Post; use Friendica\Model\Tag; use Friendica\Model\User; use Friendica\Network\Probe; @@ -2293,7 +2293,7 @@ class Diaspora } // Send all existing comments and likes to the requesting server - $comments = Item::select(['id', 'parent', 'verb', 'self'], ['parent' => $item['id']]); + $comments = Item::select(['id', 'uri-id', 'parent', 'verb', 'self'], ['parent' => $item['id']]); while ($comment = Item::fetch($comments)) { if ($comment['id'] == $comment['parent']) { continue; @@ -2301,7 +2301,7 @@ class Diaspora Logger::info('Deliver participation', ['item' => $comment['id'], 'contact' => $contact_id]); if (Worker::add(PRIORITY_HIGH, 'Delivery', Delivery::POST, $comment['id'], $contact_id)) { - ItemDeliveryData::incrementQueueCount($comment['id'], 1); + Post\DeliveryData::incrementQueueCount($comment['uri-id'], 1); } } DBA::close($comments); diff --git a/src/Worker/APDelivery.php b/src/Worker/APDelivery.php index 60752896f..609bb8888 100644 --- a/src/Worker/APDelivery.php +++ b/src/Worker/APDelivery.php @@ -23,7 +23,8 @@ namespace Friendica\Worker; use Friendica\Core\Logger; use Friendica\Core\Worker; -use Friendica\Model\ItemDeliveryData; +use Friendica\Model\Item; +use Friendica\Model\Post; use Friendica\Protocol\ActivityPub; use Friendica\Util\HTTPSignature; @@ -67,10 +68,13 @@ class APDelivery } } + // This should never fail and is temporariy (until the move to ) + $item = Item::selectFirst(['uri-id'], ['id' => $target_id]); + if (!$success && !Worker::defer() && in_array($cmd, [Delivery::POST])) { - ItemDeliveryData::incrementQueueFailed($target_id); + Post\DeliveryData::incrementQueueFailed($item['uri-id']); } elseif ($success && in_array($cmd, [Delivery::POST])) { - ItemDeliveryData::incrementQueueDone($target_id, ItemDeliveryData::ACTIVITYPUB); + Post\DeliveryData::incrementQueueDone($item['uri-id'], Post\DeliveryData::ACTIVITYPUB); } } } diff --git a/src/Worker/Delivery.php b/src/Worker/Delivery.php index 94a0f4902..0a865cb3a 100644 --- a/src/Worker/Delivery.php +++ b/src/Worker/Delivery.php @@ -58,14 +58,14 @@ class Delivery if ($cmd == self::MAIL) { $target_item = DBA::selectFirst('mail', [], ['id' => $target_id]); if (!DBA::isResult($target_item)) { - self::setFailedQueue($cmd, $target_id); + self::setFailedQueue($cmd, $target_item); return; } $uid = $target_item['uid']; } elseif ($cmd == self::SUGGESTION) { $target_item = DBA::selectFirst('fsuggest', [], ['id' => $target_id]); if (!DBA::isResult($target_item)) { - self::setFailedQueue($cmd, $target_id); + self::setFailedQueue($cmd, $target_item); return; } $uid = $target_item['uid']; @@ -75,7 +75,7 @@ class Delivery } else { $item = Model\Item::selectFirst(['parent'], ['id' => $target_id]); if (!DBA::isResult($item) || empty($item['parent'])) { - self::setFailedQueue($cmd, $target_id); + self::setFailedQueue($cmd, $target_item); return; } $parent_id = intval($item['parent']); @@ -97,13 +97,13 @@ class Delivery if (empty($target_item)) { Logger::log('Item ' . $target_id . "wasn't found. Quitting here."); - self::setFailedQueue($cmd, $target_id); + self::setFailedQueue($cmd, $target_item); return; } if (empty($parent)) { Logger::log('Parent ' . $parent_id . ' for item ' . $target_id . "wasn't found. Quitting here."); - self::setFailedQueue($cmd, $target_id); + self::setFailedQueue($cmd, $target_item); return; } @@ -113,7 +113,7 @@ class Delivery $uid = $target_item['uid']; } else { Logger::log('Only public users for item ' . $target_id, Logger::DEBUG); - self::setFailedQueue($cmd, $target_id); + self::setFailedQueue($cmd, $target_item); return; } @@ -127,7 +127,7 @@ class Delivery if (!empty($contact_id) && Model\Contact::isArchived($contact_id)) { Logger::info('Contact is archived', ['id' => $contact_id, 'cmd' => $cmd, 'item' => $target_item['id']]); - self::setFailedQueue($cmd, $target_id); + self::setFailedQueue($cmd, $target_item); return; } @@ -187,7 +187,7 @@ class Delivery $owner = Model\User::getOwnerDataById($uid); if (!DBA::isResult($owner)) { - self::setFailedQueue($cmd, $target_id); + self::setFailedQueue($cmd, $target_item); return; } @@ -196,12 +196,12 @@ class Delivery ['id' => $contact_id, 'blocked' => false, 'pending' => false, 'self' => false] ); if (!DBA::isResult($contact)) { - self::setFailedQueue($cmd, $target_id); + self::setFailedQueue($cmd, $target_item); return; } if (Network::isUrlBlocked($contact['url'])) { - self::setFailedQueue($cmd, $target_id); + self::setFailedQueue($cmd, $target_item); return; } @@ -242,16 +242,16 @@ class Delivery /** * Increased the "failed" counter in the item delivery data * - * @param string $cmd Command - * @param integer $id Item id + * @param string $cmd Command + * @param array $item Item array */ - private static function setFailedQueue(string $cmd, int $id) + private static function setFailedQueue(string $cmd, array $item) { if (!in_array($cmd, [Delivery::POST, Delivery::POKE])) { return; } - Model\ItemDeliveryData::incrementQueueFailed($id); + Model\Post\DeliveryData::incrementQueueFailed($item['uri-id'] ?? $item['id']); } /** @@ -335,13 +335,13 @@ class Delivery DFRN::import($atom, $target_importer); if (in_array($cmd, [Delivery::POST, Delivery::POKE])) { - Model\ItemDeliveryData::incrementQueueDone($target_item['id'], Model\ItemDeliveryData::DFRN); + Model\Post\DeliveryData::incrementQueueDone($target_item['uri-id'], Model\Post\DeliveryData::DFRN); } return; } - $protocol = Model\ItemDeliveryData::DFRN; + $protocol = Model\Post\DeliveryData::DFRN; // We don't have a relationship with contacts on a public post. // Se we transmit with the new method and via Diaspora as a fallback @@ -357,9 +357,9 @@ class Delivery if (in_array($cmd, [Delivery::POST, Delivery::POKE])) { if (($deliver_status >= 200) && ($deliver_status <= 299)) { - Model\ItemDeliveryData::incrementQueueDone($target_item['id'], $protocol); + Model\Post\DeliveryData::incrementQueueDone($target_item['uri-id'], $protocol); } else { - Model\ItemDeliveryData::incrementQueueFailed($target_item['id']); + Model\Post\DeliveryData::incrementQueueFailed($target_item['uri-id']); } } return; @@ -376,11 +376,11 @@ class Delivery if ($deliver_status < 200) { // Legacy DFRN $deliver_status = DFRN::deliver($owner, $contact, $atom); - $protocol = Model\ItemDeliveryData::LEGACY_DFRN; + $protocol = Model\Post\DeliveryData::LEGACY_DFRN; } } else { $deliver_status = DFRN::deliver($owner, $contact, $atom); - $protocol = Model\ItemDeliveryData::LEGACY_DFRN; + $protocol = Model\Post\DeliveryData::LEGACY_DFRN; } Logger::info('DFRN Delivery', ['cmd' => $cmd, 'url' => $contact['url'], 'guid' => ($target_item['guid'] ?? '') ?: $target_item['id'], 'return' => $deliver_status]); @@ -390,7 +390,7 @@ class Delivery Model\Contact::unmarkForArchival($contact); if (in_array($cmd, [Delivery::POST, Delivery::POKE])) { - Model\ItemDeliveryData::incrementQueueDone($target_item['id'], $protocol); + Model\Post\DeliveryData::incrementQueueDone($target_item['uri-id'], $protocol); } } else { // The message could not be delivered. We mark the contact as "dead" @@ -398,7 +398,7 @@ class Delivery Logger::info('Delivery failed: defer message', ['id' => ($target_item['guid'] ?? '') ?: $target_item['id']]); if (!Worker::defer() && in_array($cmd, [Delivery::POST, Delivery::POKE])) { - Model\ItemDeliveryData::incrementQueueFailed($target_item['id']); + Model\Post\DeliveryData::incrementQueueFailed($target_item['uri-id']); } } } @@ -475,7 +475,7 @@ class Delivery Model\Contact::unmarkForArchival($contact); if (in_array($cmd, [Delivery::POST, Delivery::POKE])) { - Model\ItemDeliveryData::incrementQueueDone($target_item['id'], Model\ItemDeliveryData::DIASPORA); + Model\Post\DeliveryData::incrementQueueDone($target_item['uri-id'], Model\Post\DeliveryData::DIASPORA); } } else { // The message could not be delivered. We mark the contact as "dead" @@ -490,10 +490,10 @@ class Delivery Logger::info('Delivery failed: defer message', ['id' => ($target_item['guid'] ?? '') ?: $target_item['id']]); // defer message for redelivery if (!Worker::defer() && in_array($cmd, [Delivery::POST, Delivery::POKE])) { - Model\ItemDeliveryData::incrementQueueFailed($target_item['id']); + Model\Post\DeliveryData::incrementQueueFailed($target_item['uri-id']); } } elseif (in_array($cmd, [Delivery::POST, Delivery::POKE])) { - Model\ItemDeliveryData::incrementQueueFailed($target_item['id']); + Model\Post\DeliveryData::incrementQueueFailed($target_item['uri-id']); } } } @@ -603,7 +603,7 @@ class Delivery Email::send($addr, $subject, $headers, $target_item); - Model\ItemDeliveryData::incrementQueueDone($target_item['id'], Model\ItemDeliveryData::MAIL); + Model\Post\DeliveryData::incrementQueueDone($target_item['uri-id'], Model\Post\DeliveryData::MAIL); Logger::info('Delivered via mail', ['guid' => $target_item['guid'], 'to' => $addr, 'subject' => $subject]); } diff --git a/src/Worker/Notifier.php b/src/Worker/Notifier.php index 35a228fce..a9fadf251 100644 --- a/src/Worker/Notifier.php +++ b/src/Worker/Notifier.php @@ -32,7 +32,7 @@ use Friendica\Model\Contact; use Friendica\Model\Conversation; use Friendica\Model\Group; use Friendica\Model\Item; -use Friendica\Model\ItemDeliveryData; +use Friendica\Model\Post; use Friendica\Model\PushSubscriber; use Friendica\Model\User; use Friendica\Network\Probe; @@ -573,7 +573,7 @@ class Notifier /// @TODO Redeliver/queue these items on failure, though there is no contact record $delivery_queue_count++; Salmon::slapper($owner, $url, $slap); - ItemDeliveryData::incrementQueueDone($target_id, ItemDeliveryData::OSTATUS); + Post\DeliveryData::incrementQueueDone($target_item['uri-id'], Post\DeliveryData::OSTATUS); } } @@ -595,11 +595,11 @@ class Notifier // Workaround for pure connector posts if (in_array($cmd, [Delivery::POST, Delivery::POKE])) { if ($delivery_queue_count == 0) { - ItemDeliveryData::incrementQueueDone($target_item['id']); + Post\DeliveryData::incrementQueueDone($target_item['uri-id']); $delivery_queue_count = 1; } - ItemDeliveryData::incrementQueueCount($target_item['id'], $delivery_queue_count); + Post\DeliveryData::incrementQueueCount($target_item['uri-id'], $delivery_queue_count); } } diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 2c3a9fb5f..085d544fa 100755 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -804,25 +804,6 @@ return [ "uri-id" => ["uri-id"] ] ], - "item-delivery-data" => [ - "comment" => "Delivery data for items", - "fields" => [ - "iid" => ["type" => "int unsigned", "not null" => "1", "primary" => "1", "relation" => ["item" => "id"], "comment" => "Item id"], - "postopts" => ["type" => "text", "comment" => "External post connectors add their network name to this comma-separated string to identify that they should be delivered to these networks during delivery"], - "inform" => ["type" => "mediumtext", "comment" => "Additional receivers of the linked item"], - "queue_count" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Initial number of delivery recipients, used as item.delivery_queue_count"], - "queue_done" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of successful deliveries, used as item.delivery_queue_done"], - "queue_failed" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of unsuccessful deliveries, used as item.delivery_queue_failed"], - "activitypub" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of successful deliveries via ActivityPub"], - "dfrn" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of successful deliveries via DFRN"], - "legacy_dfrn" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of successful deliveries via legacy DFRN"], - "diaspora" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of successful deliveries via Diaspora"], - "ostatus" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of successful deliveries via OStatus"], - ], - "indexes" => [ - "PRIMARY" => ["iid"], - ] - ], "item-uri" => [ "comment" => "URI and GUID for items", "fields" => [ @@ -1306,6 +1287,25 @@ return [ "url" => ["url"] ] ], + "post-delivery-data" => [ + "comment" => "Delivery data for items", + "fields" => [ + "uri-id" => ["type" => "int unsigned", "not null" => "1", "primary" => "1", "relation" => ["item-uri" => "id"], "comment" => "Id of the item-uri table entry that contains the item uri"], + "postopts" => ["type" => "text", "comment" => "External post connectors add their network name to this comma-separated string to identify that they should be delivered to these networks during delivery"], + "inform" => ["type" => "mediumtext", "comment" => "Additional receivers of the linked item"], + "queue_count" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Initial number of delivery recipients, used as item.delivery_queue_count"], + "queue_done" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of successful deliveries, used as item.delivery_queue_done"], + "queue_failed" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of unsuccessful deliveries, used as item.delivery_queue_failed"], + "activitypub" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of successful deliveries via ActivityPub"], + "dfrn" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of successful deliveries via DFRN"], + "legacy_dfrn" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of successful deliveries via legacy DFRN"], + "diaspora" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of successful deliveries via Diaspora"], + "ostatus" => ["type" => "mediumint", "not null" => "1", "default" => "0", "comment" => "Number of successful deliveries via OStatus"], + ], + "indexes" => [ + "PRIMARY" => ["uri-id"], + ] + ], "post-tag" => [ "comment" => "post relation to tags", "fields" => [ From 1a4beaf061123cf74dc80230636efbf20bd13659 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 2 May 2020 21:27:37 +0000 Subject: [PATCH 07/28] post update function added --- src/Database/PostUpdate.php | 58 +++++++++++++++++++++++++++++++++-- static/dbstructure.config.php | 2 +- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/src/Database/PostUpdate.php b/src/Database/PostUpdate.php index 9ae7691d5..bb3b11160 100644 --- a/src/Database/PostUpdate.php +++ b/src/Database/PostUpdate.php @@ -72,6 +72,9 @@ class PostUpdate if (!self::update1342()) { return false; } + if (!self::update1345()) { + return false; + } return true; } @@ -559,7 +562,6 @@ class PostUpdate Logger::info('Start', ['item' => $id]); - $start_id = $id; $rows = 0; $items = DBA::p("SELECT `uri-id`,`body` FROM `item-content` WHERE @@ -613,7 +615,6 @@ class PostUpdate Logger::info('Start', ['item' => $id]); - $start_id = $id; $rows = 0; $terms = DBA::p("SELECT `term`.`tid`, `item`.`uri-id`, `term`.`type`, `term`.`term`, `term`.`url`, `item-content`.`body` @@ -667,4 +668,57 @@ class PostUpdate return false; } + + /** + * Fill the "post-delivery-data" table with data from the "item-delivery-data" table + * + * @return bool "true" when the job is done + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + private static function update1345() + { + // Was the script completed? + if (DI::config()->get('system', 'post_update_version') >= 1345) { + return true; + } + + $id = DI::config()->get('system', 'post_update_version_1345_id', 0); + + Logger::info('Start', ['item' => $id]); + + $rows = 0; + + $deliveries = DBA::p("SELECT `uri-id`, `iid`, `item-delivery-data`.`postopts`, `item-delivery-data`.`inform`, + `queue_count`, `queue_done`, `activitypub`, `dfrn`, `diaspora`, `ostatus`, `legacy_dfrn`, `queue_failed` + FROM `item-delivery-data` + INNER JOIN `item` ON `item`.`id` = `item-delivery-data`.`iid` + WHERE `iid` >= ? ORDER BY `iid` LIMIT 10000", $id); + + if (DBA::errorNo() != 0) { + Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]); + return false; + } + + while ($delivery = DBA::fetch($deliveries)) { + $id = $delivery['iid']; + unset($delivery['iid']); + DBA::insert('post-delivery-data', $delivery); + ++$rows; + } + DBA::close($deliveries); + + DI::config()->set('system', 'post_update_version_1345_id', $id); + + Logger::info('Processed', ['rows' => $rows, 'last' => $id]); + + // When there are less than 100 items processed this means that we reached the end + // The other entries will then be processed with the regular functionality + if ($rows < 100) { + DI::config()->set('system', 'post_update_version', 1345); + Logger::info('Done'); + return true; + } + + return false; + } } diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 085d544fa..9c5625980 100755 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -51,7 +51,7 @@ use Friendica\Database\DBA; if (!defined('DB_UPDATE_VERSION')) { - define('DB_UPDATE_VERSION', 1343); + define('DB_UPDATE_VERSION', 1345); } return [ From c141a3eb7951a7b171e700ebfdaba83a154f6f40 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 2 May 2020 22:02:29 +0000 Subject: [PATCH 08/28] Corrected variable name --- src/Model/Item.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Model/Item.php b/src/Model/Item.php index c01535ab6..334fddbd5 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -1903,8 +1903,7 @@ class Item self::insertContent($item); } - $postfields = $item; - $delivery_data = Post\DeliveryData::extractFields($postfields); + $delivery_data = Post\DeliveryData::extractFields($item); unset($item['postopts']); unset($item['inform']); From f291b96cbcca9292c90163d3b1a720602dc06a43 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 2 May 2020 22:49:47 +0000 Subject: [PATCH 09/28] Fixing tests --- tests/datasets/api.fixture.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/datasets/api.fixture.php b/tests/datasets/api.fixture.php index c9c16a33f..54401152a 100644 --- a/tests/datasets/api.fixture.php +++ b/tests/datasets/api.fixture.php @@ -27,7 +27,7 @@ return [ 'photo', 'workerqueue', 'mail', - 'item-delivery-data', + 'post-delivery-data', // Base test config to avoid notice messages 'config' => [ [ @@ -103,6 +103,7 @@ return [ 'item' => [ [ 'id' => 1, + 'uri-id' => 1, 'visible' => 1, 'contact-id' => 42, 'author-id' => 42, @@ -123,6 +124,7 @@ return [ ], [ 'id' => 2, + 'uri-id' => 2, 'visible' => 1, 'contact-id' => 42, 'author-id' => 42, @@ -140,6 +142,7 @@ return [ [ 'id' => 3, + 'uri-id' => 3, 'visible' => 1, 'contact-id' => 43, 'author-id' => 43, @@ -156,6 +159,7 @@ return [ ], [ 'id' => 4, + 'uri-id' => 4, 'visible' => 1, 'contact-id' => 44, 'author-id' => 44, @@ -173,6 +177,7 @@ return [ [ 'id' => 5, + 'uri-id' => 5, 'visible' => 1, 'contact-id' => 42, 'author-id' => 42, @@ -193,6 +198,7 @@ return [ ], [ 'id' => 6, + 'uri-id' => 6, 'visible' => 1, 'contact-id' => 44, 'author-id' => 44, @@ -220,6 +226,7 @@ return [ 'uid' => 42, 'link' => 'http://localhost/notification/1', 'iid' => 4, + 'uri-id' => 4, 'seen' => 0, 'verb' => '', 'otype' => 'item', From 1ec3cc2fdc795aa8c30f585b1e7c34982c0dcacd Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 2 May 2020 23:16:42 +0000 Subject: [PATCH 10/28] Fixing test again? --- tests/datasets/api.fixture.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/datasets/api.fixture.php b/tests/datasets/api.fixture.php index 54401152a..fb11ae347 100644 --- a/tests/datasets/api.fixture.php +++ b/tests/datasets/api.fixture.php @@ -226,7 +226,6 @@ return [ 'uid' => 42, 'link' => 'http://localhost/notification/1', 'iid' => 4, - 'uri-id' => 4, 'seen' => 0, 'verb' => '', 'otype' => 'item', From 6b1b052308ca1c9deecaa52223640d37a9ba2646 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 4 May 2020 14:42:24 +0000 Subject: [PATCH 11/28] Updated database version in sql file --- database.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database.sql b/database.sql index 320ef38e7..5faea07b9 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2020.06-dev (Red Hot Poker) --- DB_UPDATE_VERSION 1343 +-- DB_UPDATE_VERSION 1345 -- ------------------------------------------ From 48f323431801d09e95f891b6722f482243c167fc Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 4 May 2020 14:50:22 +0000 Subject: [PATCH 12/28] Useless space and useless "null" check --- include/enotify.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/enotify.php b/include/enotify.php index 81b491ade..ae2e2e7fe 100644 --- a/include/enotify.php +++ b/include/enotify.php @@ -470,10 +470,10 @@ function notification($params) 'photo' => $params['source_photo'] ?? '', 'link' => $itemlink ?? '', 'uid' => $params['uid'] ?? 0, - 'iid' => $item_id ?? 0, - 'uri-id' => $uri_id ?? 0, - 'parent' => $parent_id ?? 0, - 'parent-uri-id' => $parent_uri_id ?? 0, + 'iid' => $item_id, + 'uri-id' => $uri_id, + 'parent' => $parent_id, + 'parent-uri-id' => $parent_uri_id, 'type' => $params['type'] ?? '', 'verb' => $params['verb'] ?? '', 'otype' => $params['otype'] ?? '', From b261df14af642fc1225df0153dbc9967688e5108 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 4 May 2020 15:23:45 +0000 Subject: [PATCH 13/28] Updated database versions --- database.sql | 55 +++++++++++++++++++++++++++++++++-- static/dbstructure.config.php | 2 +- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/database.sql b/database.sql index a68f3d4e5..3ae2374fd 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2020.06-dev (Red Hot Poker) --- DB_UPDATE_VERSION 1341 +-- DB_UPDATE_VERSION 1346 -- ------------------------------------------ @@ -666,7 +666,8 @@ CREATE TABLE IF NOT EXISTS `item` ( INDEX `uid_eventid` (`uid`,`event-id`), INDEX `icid` (`icid`), INDEX `iaid` (`iaid`), - INDEX `psid_wall` (`psid`,`wall`) + INDEX `psid_wall` (`psid`,`wall`), + INDEX `uri-id` (`uri-id`) ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Structure for all posts'; -- @@ -1187,6 +1188,18 @@ CREATE TABLE IF NOT EXISTS `tag` ( INDEX `url` (`url`) ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='tags and mentions'; +-- +-- TABLE post-category +-- +CREATE TABLE IF NOT EXISTS `post-category` ( + `uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the item uri', + `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', + `type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '', + `tid` int unsigned NOT NULL DEFAULT 0 COMMENT '', + PRIMARY KEY(`uri-id`,`uid`,`type`,`tid`), + INDEX `uri-id` (`tid`) +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='post relation to categories'; + -- -- TABLE post-tag -- @@ -1386,6 +1399,23 @@ CREATE TABLE IF NOT EXISTS `storage` ( PRIMARY KEY(`id`) ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Data stored by Database storage backend'; +-- +-- VIEW category-view +-- +DROP VIEW IF EXISTS `category-view`; +CREATE VIEW `category-view` AS SELECT + `post-category`.`uri-id` AS `uri-id`, + `post-category`.`uid` AS `uid`, + `item-uri`.`uri` AS `uri`, + `item-uri`.`guid` AS `guid`, + `post-category`.`type` AS `type`, + `post-category`.`tid` AS `tid`, + `tag`.`name` AS `name`, + `tag`.`url` AS `url` + FROM `post-category` + INNER JOIN `item-uri` ON `item-uri`.id = `post-category`.`uri-id` + LEFT JOIN `tag` ON `post-category`.`tid` = `tag`.`id`; + -- -- VIEW tag-view -- @@ -1577,6 +1607,27 @@ CREATE VIEW `pending-view` AS SELECT INNER JOIN `contact` ON `register`.`uid` = `contact`.`uid` INNER JOIN `user` ON `register`.`uid` = `user`.`uid`; +-- +-- VIEW tag-search-view +-- +DROP VIEW IF EXISTS `tag-search-view`; +CREATE VIEW `tag-search-view` AS SELECT + `post-tag`.`uri-id` AS `uri-id`, + `item`.`id` AS `iid`, + `item`.`uri` AS `uri`, + `item`.`guid` AS `guid`, + `item`.`uid` AS `uid`, + `item`.`private` AS `private`, + `item`.`wall` AS `wall`, + `item`.`origin` AS `origin`, + `item`.`gravity` AS `gravity`, + `item`.`received` AS `received`, + `tag`.`name` AS `name` + FROM `post-tag` + INNER JOIN `tag` ON `tag`.`id` = `post-tag`.`tid` + INNER JOIN `item` ON `item`.`uri-id` = `post-tag`.`uri-id` + WHERE `post-tag`.`type` = 1; + -- -- VIEW workerqueue-view -- diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 788ff7d13..a9b0682ef 100755 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -51,7 +51,7 @@ use Friendica\Database\DBA; if (!defined('DB_UPDATE_VERSION')) { - define('DB_UPDATE_VERSION', 1345); + define('DB_UPDATE_VERSION', 1346); } return [ From 340bc57952ca10a3c454576a7914f5f6ad41056d Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Mon, 4 May 2020 18:51:03 -0400 Subject: [PATCH 14/28] Ensure Tag::getByURIId parameter value in OStatus;;entryFooter - Address https://github.com/friendica/friendica/issues/8473#issuecomment-623701081 - $item['uri-id'] isn't present for follow entry pseudo-items --- src/Protocol/OStatus.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index 8ab14c078..cff50f08e 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -2081,11 +2081,9 @@ class OStatus XML::addElement($doc, $entry, "ostatus:conversation", $conversation_uri, $attributes); } - $tags = Tag::getByURIId($item['uri-id']); - if (count($tags)) { - foreach ($tags as $tag) { - $mentioned[$tag['url']] = $tag['url']; - } + // uri-id isn't present for follow entry pseudo-items + foreach (Tag::getByURIId($item['uri-id'] ?? 0) as $tag) { + $mentioned[$tag['url']] = $tag['url']; } // Make sure that mentions are accepted (GNU Social has problems with mixing HTTP and HTTPS) From b7e25d16e7a617b94d7a348f395e61eb3f244bad Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 5 May 2020 05:11:59 +0000 Subject: [PATCH 15/28] Removed usage of "tag" field --- mod/photos.php | 18 ++--------- src/Model/Contact.php | 4 +-- src/Protocol/ActivityPub/Processor.php | 42 +------------------------- src/Protocol/DFRN.php | 2 +- src/Protocol/OStatus.php | 11 +------ src/Worker/Notifier.php | 16 ++++------ 6 files changed, 13 insertions(+), 80 deletions(-) diff --git a/mod/photos.php b/mod/photos.php index 50210d686..8f968d0c0 100644 --- a/mod/photos.php +++ b/mod/photos.php @@ -425,13 +425,11 @@ function photos_post(App $a) $item = Item::selectFirst(['tag', 'inform', 'uri-id'], ['id' => $item_id, 'uid' => $page_owner_uid]); if (DBA::isResult($item)) { - $old_tag = $item['tag']; $old_inform = $item['inform']; } } if (strlen($rawtags)) { - $str_tags = ''; $inform = ''; // if the new tag doesn't have a namespace specifier (@foo or #foo) give it a hashtag @@ -513,15 +511,10 @@ function photos_post(App $a) if (!empty($contact)) { $taginfo[] = [$newname, $profile, $notify, $contact, '@[url=' . str_replace(',', '%2c', $profile) . ']' . $newname . '[/url]']; } else { - $taginfo[] = [$newname, $profile, $notify, null, $str_tags .= '@[url=' . $profile . ']' . $newname . '[/url]']; - } - - if (strlen($str_tags)) { - $str_tags .= ','; + $taginfo[] = [$newname, $profile, $notify, null, '@[url=' . $profile . ']' . $newname . '[/url]']; } $profile = str_replace(',', '%2c', $profile); - $str_tags .= '@[url=' . $profile . ']' . $newname . '[/url]'; if (!empty($item['uri-id'])) { Tag::store($item['uri-id'], Tag::MENTION, $newname, $profile); @@ -529,7 +522,6 @@ function photos_post(App $a) } } elseif (strpos($tag, '#') === 0) { $tagname = substr($tag, 1); - $str_tags .= '#[url=' . DI::baseUrl() . "/search?tag=" . $tagname . ']' . $tagname . '[/url],'; if (!empty($item['uri-id'])) { Tag::store($item['uri-id'], Tag::HASHTAG, $tagname); } @@ -537,19 +529,13 @@ function photos_post(App $a) } } - $newtag = $old_tag ?? ''; - if (strlen($newtag) && strlen($str_tags)) { - $newtag .= ','; - } - $newtag .= $str_tags; - $newinform = $old_inform ?? ''; if (strlen($newinform) && strlen($inform)) { $newinform .= ','; } $newinform .= $inform; - $fields = ['tag' => $newtag, 'inform' => $newinform, 'edited' => DateTimeFormat::utcNow(), 'changed' => DateTimeFormat::utcNow()]; + $fields = ['inform' => $newinform, 'edited' => DateTimeFormat::utcNow(), 'changed' => DateTimeFormat::utcNow()]; $condition = ['id' => $item_id]; Item::update($fields, $condition); diff --git a/src/Model/Contact.php b/src/Model/Contact.php index becc80c0c..0d321189f 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -847,7 +847,7 @@ class Contact $item['body'] = ''; $item['title'] = ''; $item['guid'] = ''; - $item['tag'] = ''; + $item['uri-id'] = 0; $item['attach'] = ''; $slap = OStatus::salmon($item, $user); @@ -2457,7 +2457,7 @@ class Contact $item['body'] = ''; $item['title'] = ''; $item['guid'] = ''; - $item['tag'] = ''; + $item['uri-id'] = 0; $item['attach'] = ''; $slap = OStatus::salmon($item, $owner); diff --git a/src/Protocol/ActivityPub/Processor.php b/src/Protocol/ActivityPub/Processor.php index 479d8d55c..197fb2baa 100644 --- a/src/Protocol/ActivityPub/Processor.php +++ b/src/Protocol/ActivityPub/Processor.php @@ -79,35 +79,6 @@ class Processor return $body; } - /** - * Constructs a string with tags for a given tag array - * - * @param array $tags - * @param boolean $sensitive - * @return string with tags - */ - private static function constructTagString(array $tags = null, $sensitive = false) - { - if (empty($tags)) { - return ''; - } - - $tag_text = ''; - foreach ($tags as $tag) { - if (in_array($tag['type'] ?? '', ['Mention', 'Hashtag'])) { - if (!empty($tag_text)) { - $tag_text .= ','; - } - - $tag_text .= substr($tag['name'], 0, 1) . '[url=' . $tag['href'] . ']' . substr($tag['name'], 1) . '[/url]'; - } - } - - /// @todo add nsfw for $sensitive - - return $tag_text; - } - /** * Add attachment data to the item array * @@ -263,16 +234,7 @@ class Processor } Tag::store($item['uri-id'], Tag::HASHTAG, $activity['object_content'], $activity['object_id']); - - // To-Do: - // - Check if "blocktag" is set - // - Check if actor is a contact - - if (!stristr($item['tag'], trim($activity['object_content']))) { - $tag = $item['tag'] . (strlen($item['tag']) ? ',' : '') . '#[url=' . $activity['object_id'] . ']'. $activity['object_content'] . '[/url]'; - Item::update(['tag' => $tag], ['id' => $item['id']]); - Logger::info('Tagged item', ['id' => $item['id'], 'tag' => $activity['object_content'], 'uri' => $activity['target_id'], 'actor' => $activity['actor']]); - } + Logger::info('Tagged item', ['id' => $item['id'], 'tag' => $activity['object_content'], 'uri' => $activity['target_id'], 'actor' => $activity['actor']]); } } @@ -405,8 +367,6 @@ class Processor $item['body'] = $content; } - $item['tag'] = self::constructTagString($activity['tags'], $activity['sensitive']); - self::storeFromBody($item); self::storeTags($item['uri-id'], $activity['tags']); diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php index 5060a7d56..74352906d 100644 --- a/src/Protocol/DFRN.php +++ b/src/Protocol/DFRN.php @@ -2001,7 +2001,7 @@ class DFRN } $fields = ['title' => $item['title'] ?? '', 'body' => $item['body'] ?? '', - 'tag' => $item['tag'] ?? '', 'changed' => DateTimeFormat::utcNow(), + 'changed' => DateTimeFormat::utcNow(), 'edited' => DateTimeFormat::utc($item["edited"])]; $condition = ["`uri` = ? AND `uid` IN (0, ?)", $item["uri"], $importer["importer_uid"]]; diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index 8ab14c078..6eeadb46c 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -655,17 +655,8 @@ class OStatus foreach ($categories as $category) { foreach ($category->attributes as $attributes) { if ($attributes->name == 'term') { - $term = $attributes->textContent; - if (!empty($item['tag'])) { - $item['tag'] .= ','; - } else { - $item['tag'] = ''; - } - - $item['tag'] .= '#[url=' . DI::baseUrl() . '/search?tag=' . $term . ']' . $term . '[/url]'; - // Store the hashtag - Tag::store($item['uri-id'], Tag::HASHTAG, $term); + Tag::store($item['uri-id'], Tag::HASHTAG, $attributes->textContent); } } } diff --git a/src/Worker/Notifier.php b/src/Worker/Notifier.php index 35a228fce..09470c318 100644 --- a/src/Worker/Notifier.php +++ b/src/Worker/Notifier.php @@ -34,6 +34,7 @@ use Friendica\Model\Group; use Friendica\Model\Item; use Friendica\Model\ItemDeliveryData; use Friendica\Model\PushSubscriber; +use Friendica\Model\Tag; use Friendica\Model\User; use Friendica\Network\Probe; use Friendica\Protocol\ActivityPub; @@ -367,16 +368,11 @@ class Notifier } // Send a salmon notification to every person we mentioned in the post - $arr = explode(',',$target_item['tag']); - foreach ($arr as $x) { - //Logger::log('Checking tag '.$x, Logger::DEBUG); - $matches = null; - if (preg_match('/@\[url=([^\]]*)\]/',$x,$matches)) { - $probed_contact = Probe::uri($matches[1]); - if ($probed_contact["notify"] != "") { - Logger::log('Notify mentioned user '.$probed_contact["url"].': '.$probed_contact["notify"]); - $url_recipients[$probed_contact["notify"]] = $probed_contact["notify"]; - } + foreach (Tag::getByURIId($target_item['uri-id'], [Tag::MENTION, Tag::EXCLUSIVE_MENTION, Tag::IMPLICIT_MENTION]) as $tag) { + $probed_contact = Probe::uri($tag['url']); + if ($probed_contact["notify"] != "") { + Logger::log('Notify mentioned user '.$probed_contact["url"].': '.$probed_contact["notify"]); + $url_recipients[$probed_contact["notify"]] = $probed_contact["notify"]; } } From 6d47324342782064df4716e5bd46b1489b0d6e08 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 5 May 2020 05:22:14 +0000 Subject: [PATCH 16/28] Integrate perfect-scrollbar to make tests happy --- composer.json | 1 - composer.lock | 31 - library/perfect-scrollbar/LICENSE | 22 + library/perfect-scrollbar/README.md | 5 + library/perfect-scrollbar/bower.json | 18 + .../css/perfect-scrollbar.css | 113 ++ .../css/perfect-scrollbar.min.css | 2 + .../js/perfect-scrollbar.jquery.js | 1577 +++++++++++++++++ .../js/perfect-scrollbar.jquery.min.js | 2 + .../perfect-scrollbar/js/perfect-scrollbar.js | 1550 ++++++++++++++++ .../js/perfect-scrollbar.min.js | 2 + library/perfect-scrollbar/src/css/main.scss | 3 + library/perfect-scrollbar/src/css/mixins.scss | 128 ++ library/perfect-scrollbar/src/css/themes.scss | 25 + .../perfect-scrollbar/src/css/variables.scss | 24 + .../src/js/adaptor/global.js | 14 + .../src/js/adaptor/jquery.js | 41 + library/perfect-scrollbar/src/js/lib/class.js | 42 + library/perfect-scrollbar/src/js/lib/dom.js | 84 + .../src/js/lib/event-manager.js | 71 + library/perfect-scrollbar/src/js/lib/guid.js | 13 + .../perfect-scrollbar/src/js/lib/helper.js | 83 + library/perfect-scrollbar/src/js/main.js | 11 + .../src/js/plugin/default-setting.js | 16 + .../src/js/plugin/destroy.js | 22 + .../src/js/plugin/handler/click-rail.js | 39 + .../src/js/plugin/handler/drag-scrollbar.js | 103 ++ .../src/js/plugin/handler/keyboard.js | 154 ++ .../src/js/plugin/handler/mouse-wheel.js | 141 ++ .../src/js/plugin/handler/native-scroll.js | 15 + .../src/js/plugin/handler/selection.js | 115 ++ .../src/js/plugin/handler/touch.js | 179 ++ .../src/js/plugin/initialize.js | 37 + .../src/js/plugin/instances.js | 107 ++ .../src/js/plugin/update-geometry.js | 126 ++ .../src/js/plugin/update-scroll.js | 97 + .../perfect-scrollbar/src/js/plugin/update.js | 37 + view/templates/head.tpl | 4 +- view/theme/frio/templates/head.tpl | 4 +- 39 files changed, 5022 insertions(+), 36 deletions(-) create mode 100644 library/perfect-scrollbar/LICENSE create mode 100644 library/perfect-scrollbar/README.md create mode 100644 library/perfect-scrollbar/bower.json create mode 100644 library/perfect-scrollbar/css/perfect-scrollbar.css create mode 100644 library/perfect-scrollbar/css/perfect-scrollbar.min.css create mode 100644 library/perfect-scrollbar/js/perfect-scrollbar.jquery.js create mode 100644 library/perfect-scrollbar/js/perfect-scrollbar.jquery.min.js create mode 100644 library/perfect-scrollbar/js/perfect-scrollbar.js create mode 100644 library/perfect-scrollbar/js/perfect-scrollbar.min.js create mode 100644 library/perfect-scrollbar/src/css/main.scss create mode 100644 library/perfect-scrollbar/src/css/mixins.scss create mode 100644 library/perfect-scrollbar/src/css/themes.scss create mode 100644 library/perfect-scrollbar/src/css/variables.scss create mode 100644 library/perfect-scrollbar/src/js/adaptor/global.js create mode 100644 library/perfect-scrollbar/src/js/adaptor/jquery.js create mode 100644 library/perfect-scrollbar/src/js/lib/class.js create mode 100644 library/perfect-scrollbar/src/js/lib/dom.js create mode 100644 library/perfect-scrollbar/src/js/lib/event-manager.js create mode 100644 library/perfect-scrollbar/src/js/lib/guid.js create mode 100644 library/perfect-scrollbar/src/js/lib/helper.js create mode 100644 library/perfect-scrollbar/src/js/main.js create mode 100644 library/perfect-scrollbar/src/js/plugin/default-setting.js create mode 100644 library/perfect-scrollbar/src/js/plugin/destroy.js create mode 100644 library/perfect-scrollbar/src/js/plugin/handler/click-rail.js create mode 100644 library/perfect-scrollbar/src/js/plugin/handler/drag-scrollbar.js create mode 100644 library/perfect-scrollbar/src/js/plugin/handler/keyboard.js create mode 100644 library/perfect-scrollbar/src/js/plugin/handler/mouse-wheel.js create mode 100644 library/perfect-scrollbar/src/js/plugin/handler/native-scroll.js create mode 100644 library/perfect-scrollbar/src/js/plugin/handler/selection.js create mode 100644 library/perfect-scrollbar/src/js/plugin/handler/touch.js create mode 100644 library/perfect-scrollbar/src/js/plugin/initialize.js create mode 100644 library/perfect-scrollbar/src/js/plugin/instances.js create mode 100644 library/perfect-scrollbar/src/js/plugin/update-geometry.js create mode 100644 library/perfect-scrollbar/src/js/plugin/update-scroll.js create mode 100644 library/perfect-scrollbar/src/js/plugin/update.js diff --git a/composer.json b/composer.json index 108ea8473..20cbe46a7 100644 --- a/composer.json +++ b/composer.json @@ -50,7 +50,6 @@ "bower-asset/base64": "^1.0", "bower-asset/chart-js": "^2.8", "bower-asset/dompurify": "^1.0", - "bower-asset/perfect-scrollbar": "^0.6", "bower-asset/vue": "^2.6", "npm-asset/es-jquery-sortable": "^0.9.13", "npm-asset/jquery": "^2.0", diff --git a/composer.lock b/composer.lock index 8834f3f70..c04c2e9b1 100644 --- a/composer.lock +++ b/composer.lock @@ -237,37 +237,6 @@ ], "time": "2019-02-28T15:21:34+00:00" }, - { - "name": "bower-asset/perfect-scrollbar", - "version": "0.6.16", - "source": { - "type": "git", - "url": "https://github.com/utatti/perfect-scrollbar-bower.git", - "reference": "3049129e5dbb403295ce8507a461cdd0f200938c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utatti/perfect-scrollbar-bower/zipball/3049129e5dbb403295ce8507a461cdd0f200938c", - "reference": "3049129e5dbb403295ce8507a461cdd0f200938c", - "shasum": "" - }, - "type": "bower-asset-library", - "extra": { - "bower-asset-main": [ - "css/perfect-scrollbar.css", - "js/perfect-scrollbar.js" - ], - "bower-asset-ignore": [ - "**/.*", - "bower_components" - ] - }, - "license": [ - "MIT" - ], - "description": "Minimalistic but perfect custom scrollbar plugin", - "time": "2017-01-10T01:04:09+00:00" - }, { "name": "bower-asset/vue", "version": "v2.6.10", diff --git a/library/perfect-scrollbar/LICENSE b/library/perfect-scrollbar/LICENSE new file mode 100644 index 000000000..8df97031a --- /dev/null +++ b/library/perfect-scrollbar/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2016 Hyunje Alex Jun + +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. + diff --git a/library/perfect-scrollbar/README.md b/library/perfect-scrollbar/README.md new file mode 100644 index 000000000..9b445dbf8 --- /dev/null +++ b/library/perfect-scrollbar/README.md @@ -0,0 +1,5 @@ +# Bower Package of perfect-scrollbar + +This is the [Bower](https://bower.io/) package of perfect-scrollbar. + +For details and usage please read more on [https://github.com/noraesae/perfect-scrollbar](https://github.com/noraesae/perfect-scrollbar). diff --git a/library/perfect-scrollbar/bower.json b/library/perfect-scrollbar/bower.json new file mode 100644 index 000000000..423477446 --- /dev/null +++ b/library/perfect-scrollbar/bower.json @@ -0,0 +1,18 @@ +{ + "name": "perfect-scrollbar", + "version": "0.6.16", + "homepage": "http://noraesae.github.io/perfect-scrollbar/", + "authors": [ + "Hyunje Jun " + ], + "description": "Minimalistic but perfect custom scrollbar plugin", + "main": [ + "css/perfect-scrollbar.css", + "js/perfect-scrollbar.js" + ], + "license": "MIT", + "ignore": [ + "**/.*", + "bower_components" + ] +} diff --git a/library/perfect-scrollbar/css/perfect-scrollbar.css b/library/perfect-scrollbar/css/perfect-scrollbar.css new file mode 100644 index 000000000..6f03551ad --- /dev/null +++ b/library/perfect-scrollbar/css/perfect-scrollbar.css @@ -0,0 +1,113 @@ +/* perfect-scrollbar v0.6.16 */ +.ps-container { + -ms-touch-action: auto; + touch-action: auto; + overflow: hidden !important; + -ms-overflow-style: none; } + @supports (-ms-overflow-style: none) { + .ps-container { + overflow: auto !important; } } + @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { + .ps-container { + overflow: auto !important; } } + .ps-container.ps-active-x > .ps-scrollbar-x-rail, + .ps-container.ps-active-y > .ps-scrollbar-y-rail { + display: block; + background-color: transparent; } + .ps-container.ps-in-scrolling.ps-x > .ps-scrollbar-x-rail { + background-color: #eee; + opacity: 0.9; } + .ps-container.ps-in-scrolling.ps-x > .ps-scrollbar-x-rail > .ps-scrollbar-x { + background-color: #999; + height: 11px; } + .ps-container.ps-in-scrolling.ps-y > .ps-scrollbar-y-rail { + background-color: #eee; + opacity: 0.9; } + .ps-container.ps-in-scrolling.ps-y > .ps-scrollbar-y-rail > .ps-scrollbar-y { + background-color: #999; + width: 11px; } + .ps-container > .ps-scrollbar-x-rail { + display: none; + position: absolute; + /* please don't change 'position' */ + opacity: 0; + -webkit-transition: background-color .2s linear, opacity .2s linear; + -o-transition: background-color .2s linear, opacity .2s linear; + -moz-transition: background-color .2s linear, opacity .2s linear; + transition: background-color .2s linear, opacity .2s linear; + bottom: 0px; + /* there must be 'bottom' for ps-scrollbar-x-rail */ + height: 15px; } + .ps-container > .ps-scrollbar-x-rail > .ps-scrollbar-x { + position: absolute; + /* please don't change 'position' */ + background-color: #aaa; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out; + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out; + -o-transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out; + -moz-transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out; + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out; + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -webkit-border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out; + bottom: 2px; + /* there must be 'bottom' for ps-scrollbar-x */ + height: 6px; } + .ps-container > .ps-scrollbar-x-rail:hover > .ps-scrollbar-x, .ps-container > .ps-scrollbar-x-rail:active > .ps-scrollbar-x { + height: 11px; } + .ps-container > .ps-scrollbar-y-rail { + display: none; + position: absolute; + /* please don't change 'position' */ + opacity: 0; + -webkit-transition: background-color .2s linear, opacity .2s linear; + -o-transition: background-color .2s linear, opacity .2s linear; + -moz-transition: background-color .2s linear, opacity .2s linear; + transition: background-color .2s linear, opacity .2s linear; + right: 0; + /* there must be 'right' for ps-scrollbar-y-rail */ + width: 15px; } + .ps-container > .ps-scrollbar-y-rail > .ps-scrollbar-y { + position: absolute; + /* please don't change 'position' */ + background-color: #aaa; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out; + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out; + -o-transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out; + -moz-transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out; + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out; + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -webkit-border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out; + right: 2px; + /* there must be 'right' for ps-scrollbar-y */ + width: 6px; } + .ps-container > .ps-scrollbar-y-rail:hover > .ps-scrollbar-y, .ps-container > .ps-scrollbar-y-rail:active > .ps-scrollbar-y { + width: 11px; } + .ps-container:hover.ps-in-scrolling.ps-x > .ps-scrollbar-x-rail { + background-color: #eee; + opacity: 0.9; } + .ps-container:hover.ps-in-scrolling.ps-x > .ps-scrollbar-x-rail > .ps-scrollbar-x { + background-color: #999; + height: 11px; } + .ps-container:hover.ps-in-scrolling.ps-y > .ps-scrollbar-y-rail { + background-color: #eee; + opacity: 0.9; } + .ps-container:hover.ps-in-scrolling.ps-y > .ps-scrollbar-y-rail > .ps-scrollbar-y { + background-color: #999; + width: 11px; } + .ps-container:hover > .ps-scrollbar-x-rail, + .ps-container:hover > .ps-scrollbar-y-rail { + opacity: 0.6; } + .ps-container:hover > .ps-scrollbar-x-rail:hover { + background-color: #eee; + opacity: 0.9; } + .ps-container:hover > .ps-scrollbar-x-rail:hover > .ps-scrollbar-x { + background-color: #999; } + .ps-container:hover > .ps-scrollbar-y-rail:hover { + background-color: #eee; + opacity: 0.9; } + .ps-container:hover > .ps-scrollbar-y-rail:hover > .ps-scrollbar-y { + background-color: #999; } diff --git a/library/perfect-scrollbar/css/perfect-scrollbar.min.css b/library/perfect-scrollbar/css/perfect-scrollbar.min.css new file mode 100644 index 000000000..a0ad63ca5 --- /dev/null +++ b/library/perfect-scrollbar/css/perfect-scrollbar.min.css @@ -0,0 +1,2 @@ +/* perfect-scrollbar v0.6.16 */ +.ps-container{-ms-touch-action:auto;touch-action:auto;overflow:hidden !important;-ms-overflow-style:none}@supports (-ms-overflow-style: none){.ps-container{overflow:auto !important}}@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none){.ps-container{overflow:auto !important}}.ps-container.ps-active-x>.ps-scrollbar-x-rail,.ps-container.ps-active-y>.ps-scrollbar-y-rail{display:block;background-color:transparent}.ps-container.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail{background-color:#eee;opacity:.9}.ps-container.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail>.ps-scrollbar-x{background-color:#999;height:11px}.ps-container.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail{background-color:#eee;opacity:.9}.ps-container.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail>.ps-scrollbar-y{background-color:#999;width:11px}.ps-container>.ps-scrollbar-x-rail{display:none;position:absolute;opacity:0;-webkit-transition:background-color .2s linear, opacity .2s linear;-o-transition:background-color .2s linear, opacity .2s linear;-moz-transition:background-color .2s linear, opacity .2s linear;transition:background-color .2s linear, opacity .2s linear;bottom:0px;height:15px}.ps-container>.ps-scrollbar-x-rail>.ps-scrollbar-x{position:absolute;background-color:#aaa;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out;transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out;-o-transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out;-moz-transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out;transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out;transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -webkit-border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out;bottom:2px;height:6px}.ps-container>.ps-scrollbar-x-rail:hover>.ps-scrollbar-x,.ps-container>.ps-scrollbar-x-rail:active>.ps-scrollbar-x{height:11px}.ps-container>.ps-scrollbar-y-rail{display:none;position:absolute;opacity:0;-webkit-transition:background-color .2s linear, opacity .2s linear;-o-transition:background-color .2s linear, opacity .2s linear;-moz-transition:background-color .2s linear, opacity .2s linear;transition:background-color .2s linear, opacity .2s linear;right:0;width:15px}.ps-container>.ps-scrollbar-y-rail>.ps-scrollbar-y{position:absolute;background-color:#aaa;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out;transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out;-o-transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out;-moz-transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out;transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out;transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -webkit-border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out;right:2px;width:6px}.ps-container>.ps-scrollbar-y-rail:hover>.ps-scrollbar-y,.ps-container>.ps-scrollbar-y-rail:active>.ps-scrollbar-y{width:11px}.ps-container:hover.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail{background-color:#eee;opacity:.9}.ps-container:hover.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail>.ps-scrollbar-x{background-color:#999;height:11px}.ps-container:hover.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail{background-color:#eee;opacity:.9}.ps-container:hover.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail>.ps-scrollbar-y{background-color:#999;width:11px}.ps-container:hover>.ps-scrollbar-x-rail,.ps-container:hover>.ps-scrollbar-y-rail{opacity:.6}.ps-container:hover>.ps-scrollbar-x-rail:hover{background-color:#eee;opacity:.9}.ps-container:hover>.ps-scrollbar-x-rail:hover>.ps-scrollbar-x{background-color:#999}.ps-container:hover>.ps-scrollbar-y-rail:hover{background-color:#eee;opacity:.9}.ps-container:hover>.ps-scrollbar-y-rail:hover>.ps-scrollbar-y{background-color:#999} diff --git a/library/perfect-scrollbar/js/perfect-scrollbar.jquery.js b/library/perfect-scrollbar/js/perfect-scrollbar.jquery.js new file mode 100644 index 000000000..5ae830f12 --- /dev/null +++ b/library/perfect-scrollbar/js/perfect-scrollbar.jquery.js @@ -0,0 +1,1577 @@ +/* perfect-scrollbar v0.6.16 */ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 0) { + classes.splice(idx, 1); + } + element.className = classes.join(' '); +} + +exports.add = function (element, className) { + if (element.classList) { + element.classList.add(className); + } else { + oldAdd(element, className); + } +}; + +exports.remove = function (element, className) { + if (element.classList) { + element.classList.remove(className); + } else { + oldRemove(element, className); + } +}; + +exports.list = function (element) { + if (element.classList) { + return Array.prototype.slice.apply(element.classList); + } else { + return element.className.split(' '); + } +}; + +},{}],3:[function(require,module,exports){ +'use strict'; + +var DOM = {}; + +DOM.e = function (tagName, className) { + var element = document.createElement(tagName); + element.className = className; + return element; +}; + +DOM.appendTo = function (child, parent) { + parent.appendChild(child); + return child; +}; + +function cssGet(element, styleName) { + return window.getComputedStyle(element)[styleName]; +} + +function cssSet(element, styleName, styleValue) { + if (typeof styleValue === 'number') { + styleValue = styleValue.toString() + 'px'; + } + element.style[styleName] = styleValue; + return element; +} + +function cssMultiSet(element, obj) { + for (var key in obj) { + var val = obj[key]; + if (typeof val === 'number') { + val = val.toString() + 'px'; + } + element.style[key] = val; + } + return element; +} + +DOM.css = function (element, styleNameOrObject, styleValue) { + if (typeof styleNameOrObject === 'object') { + // multiple set with object + return cssMultiSet(element, styleNameOrObject); + } else { + if (typeof styleValue === 'undefined') { + return cssGet(element, styleNameOrObject); + } else { + return cssSet(element, styleNameOrObject, styleValue); + } + } +}; + +DOM.matches = function (element, query) { + if (typeof element.matches !== 'undefined') { + return element.matches(query); + } else { + if (typeof element.matchesSelector !== 'undefined') { + return element.matchesSelector(query); + } else if (typeof element.webkitMatchesSelector !== 'undefined') { + return element.webkitMatchesSelector(query); + } else if (typeof element.mozMatchesSelector !== 'undefined') { + return element.mozMatchesSelector(query); + } else if (typeof element.msMatchesSelector !== 'undefined') { + return element.msMatchesSelector(query); + } + } +}; + +DOM.remove = function (element) { + if (typeof element.remove !== 'undefined') { + element.remove(); + } else { + if (element.parentNode) { + element.parentNode.removeChild(element); + } + } +}; + +DOM.queryChildren = function (element, selector) { + return Array.prototype.filter.call(element.childNodes, function (child) { + return DOM.matches(child, selector); + }); +}; + +module.exports = DOM; + +},{}],4:[function(require,module,exports){ +'use strict'; + +var EventElement = function (element) { + this.element = element; + this.events = {}; +}; + +EventElement.prototype.bind = function (eventName, handler) { + if (typeof this.events[eventName] === 'undefined') { + this.events[eventName] = []; + } + this.events[eventName].push(handler); + this.element.addEventListener(eventName, handler, false); +}; + +EventElement.prototype.unbind = function (eventName, handler) { + var isHandlerProvided = (typeof handler !== 'undefined'); + this.events[eventName] = this.events[eventName].filter(function (hdlr) { + if (isHandlerProvided && hdlr !== handler) { + return true; + } + this.element.removeEventListener(eventName, hdlr, false); + return false; + }, this); +}; + +EventElement.prototype.unbindAll = function () { + for (var name in this.events) { + this.unbind(name); + } +}; + +var EventManager = function () { + this.eventElements = []; +}; + +EventManager.prototype.eventElement = function (element) { + var ee = this.eventElements.filter(function (eventElement) { + return eventElement.element === element; + })[0]; + if (typeof ee === 'undefined') { + ee = new EventElement(element); + this.eventElements.push(ee); + } + return ee; +}; + +EventManager.prototype.bind = function (element, eventName, handler) { + this.eventElement(element).bind(eventName, handler); +}; + +EventManager.prototype.unbind = function (element, eventName, handler) { + this.eventElement(element).unbind(eventName, handler); +}; + +EventManager.prototype.unbindAll = function () { + for (var i = 0; i < this.eventElements.length; i++) { + this.eventElements[i].unbindAll(); + } +}; + +EventManager.prototype.once = function (element, eventName, handler) { + var ee = this.eventElement(element); + var onceHandler = function (e) { + ee.unbind(eventName, onceHandler); + handler(e); + }; + ee.bind(eventName, onceHandler); +}; + +module.exports = EventManager; + +},{}],5:[function(require,module,exports){ +'use strict'; + +module.exports = (function () { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return function () { + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + + s4() + '-' + s4() + s4() + s4(); + }; +})(); + +},{}],6:[function(require,module,exports){ +'use strict'; + +var cls = require('./class'); +var dom = require('./dom'); + +var toInt = exports.toInt = function (x) { + return parseInt(x, 10) || 0; +}; + +var clone = exports.clone = function (obj) { + if (!obj) { + return null; + } else if (obj.constructor === Array) { + return obj.map(clone); + } else if (typeof obj === 'object') { + var result = {}; + for (var key in obj) { + result[key] = clone(obj[key]); + } + return result; + } else { + return obj; + } +}; + +exports.extend = function (original, source) { + var result = clone(original); + for (var key in source) { + result[key] = clone(source[key]); + } + return result; +}; + +exports.isEditable = function (el) { + return dom.matches(el, "input,[contenteditable]") || + dom.matches(el, "select,[contenteditable]") || + dom.matches(el, "textarea,[contenteditable]") || + dom.matches(el, "button,[contenteditable]"); +}; + +exports.removePsClasses = function (element) { + var clsList = cls.list(element); + for (var i = 0; i < clsList.length; i++) { + var className = clsList[i]; + if (className.indexOf('ps-') === 0) { + cls.remove(element, className); + } + } +}; + +exports.outerWidth = function (element) { + return toInt(dom.css(element, 'width')) + + toInt(dom.css(element, 'paddingLeft')) + + toInt(dom.css(element, 'paddingRight')) + + toInt(dom.css(element, 'borderLeftWidth')) + + toInt(dom.css(element, 'borderRightWidth')); +}; + +exports.startScrolling = function (element, axis) { + cls.add(element, 'ps-in-scrolling'); + if (typeof axis !== 'undefined') { + cls.add(element, 'ps-' + axis); + } else { + cls.add(element, 'ps-x'); + cls.add(element, 'ps-y'); + } +}; + +exports.stopScrolling = function (element, axis) { + cls.remove(element, 'ps-in-scrolling'); + if (typeof axis !== 'undefined') { + cls.remove(element, 'ps-' + axis); + } else { + cls.remove(element, 'ps-x'); + cls.remove(element, 'ps-y'); + } +}; + +exports.env = { + isWebKit: 'WebkitAppearance' in document.documentElement.style, + supportsTouch: (('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch), + supportsIePointer: window.navigator.msMaxTouchPoints !== null +}; + +},{"./class":2,"./dom":3}],7:[function(require,module,exports){ +'use strict'; + +var destroy = require('./plugin/destroy'); +var initialize = require('./plugin/initialize'); +var update = require('./plugin/update'); + +module.exports = { + initialize: initialize, + update: update, + destroy: destroy +}; + +},{"./plugin/destroy":9,"./plugin/initialize":17,"./plugin/update":21}],8:[function(require,module,exports){ +'use strict'; + +module.exports = { + handlers: ['click-rail', 'drag-scrollbar', 'keyboard', 'wheel', 'touch'], + maxScrollbarLength: null, + minScrollbarLength: null, + scrollXMarginOffset: 0, + scrollYMarginOffset: 0, + suppressScrollX: false, + suppressScrollY: false, + swipePropagation: true, + useBothWheelAxes: false, + wheelPropagation: false, + wheelSpeed: 1, + theme: 'default' +}; + +},{}],9:[function(require,module,exports){ +'use strict'; + +var _ = require('../lib/helper'); +var dom = require('../lib/dom'); +var instances = require('./instances'); + +module.exports = function (element) { + var i = instances.get(element); + + if (!i) { + return; + } + + i.event.unbindAll(); + dom.remove(i.scrollbarX); + dom.remove(i.scrollbarY); + dom.remove(i.scrollbarXRail); + dom.remove(i.scrollbarYRail); + _.removePsClasses(element); + + instances.remove(element); +}; + +},{"../lib/dom":3,"../lib/helper":6,"./instances":18}],10:[function(require,module,exports){ +'use strict'; + +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindClickRailHandler(element, i) { + function pageOffset(el) { + return el.getBoundingClientRect(); + } + var stopPropagation = function (e) { e.stopPropagation(); }; + + i.event.bind(i.scrollbarY, 'click', stopPropagation); + i.event.bind(i.scrollbarYRail, 'click', function (e) { + var positionTop = e.pageY - window.pageYOffset - pageOffset(i.scrollbarYRail).top; + var direction = positionTop > i.scrollbarYTop ? 1 : -1; + + updateScroll(element, 'top', element.scrollTop + direction * i.containerHeight); + updateGeometry(element); + + e.stopPropagation(); + }); + + i.event.bind(i.scrollbarX, 'click', stopPropagation); + i.event.bind(i.scrollbarXRail, 'click', function (e) { + var positionLeft = e.pageX - window.pageXOffset - pageOffset(i.scrollbarXRail).left; + var direction = positionLeft > i.scrollbarXLeft ? 1 : -1; + + updateScroll(element, 'left', element.scrollLeft + direction * i.containerWidth); + updateGeometry(element); + + e.stopPropagation(); + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindClickRailHandler(element, i); +}; + +},{"../instances":18,"../update-geometry":19,"../update-scroll":20}],11:[function(require,module,exports){ +'use strict'; + +var _ = require('../../lib/helper'); +var dom = require('../../lib/dom'); +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindMouseScrollXHandler(element, i) { + var currentLeft = null; + var currentPageX = null; + + function updateScrollLeft(deltaX) { + var newLeft = currentLeft + (deltaX * i.railXRatio); + var maxLeft = Math.max(0, i.scrollbarXRail.getBoundingClientRect().left) + (i.railXRatio * (i.railXWidth - i.scrollbarXWidth)); + + if (newLeft < 0) { + i.scrollbarXLeft = 0; + } else if (newLeft > maxLeft) { + i.scrollbarXLeft = maxLeft; + } else { + i.scrollbarXLeft = newLeft; + } + + var scrollLeft = _.toInt(i.scrollbarXLeft * (i.contentWidth - i.containerWidth) / (i.containerWidth - (i.railXRatio * i.scrollbarXWidth))) - i.negativeScrollAdjustment; + updateScroll(element, 'left', scrollLeft); + } + + var mouseMoveHandler = function (e) { + updateScrollLeft(e.pageX - currentPageX); + updateGeometry(element); + e.stopPropagation(); + e.preventDefault(); + }; + + var mouseUpHandler = function () { + _.stopScrolling(element, 'x'); + i.event.unbind(i.ownerDocument, 'mousemove', mouseMoveHandler); + }; + + i.event.bind(i.scrollbarX, 'mousedown', function (e) { + currentPageX = e.pageX; + currentLeft = _.toInt(dom.css(i.scrollbarX, 'left')) * i.railXRatio; + _.startScrolling(element, 'x'); + + i.event.bind(i.ownerDocument, 'mousemove', mouseMoveHandler); + i.event.once(i.ownerDocument, 'mouseup', mouseUpHandler); + + e.stopPropagation(); + e.preventDefault(); + }); +} + +function bindMouseScrollYHandler(element, i) { + var currentTop = null; + var currentPageY = null; + + function updateScrollTop(deltaY) { + var newTop = currentTop + (deltaY * i.railYRatio); + var maxTop = Math.max(0, i.scrollbarYRail.getBoundingClientRect().top) + (i.railYRatio * (i.railYHeight - i.scrollbarYHeight)); + + if (newTop < 0) { + i.scrollbarYTop = 0; + } else if (newTop > maxTop) { + i.scrollbarYTop = maxTop; + } else { + i.scrollbarYTop = newTop; + } + + var scrollTop = _.toInt(i.scrollbarYTop * (i.contentHeight - i.containerHeight) / (i.containerHeight - (i.railYRatio * i.scrollbarYHeight))); + updateScroll(element, 'top', scrollTop); + } + + var mouseMoveHandler = function (e) { + updateScrollTop(e.pageY - currentPageY); + updateGeometry(element); + e.stopPropagation(); + e.preventDefault(); + }; + + var mouseUpHandler = function () { + _.stopScrolling(element, 'y'); + i.event.unbind(i.ownerDocument, 'mousemove', mouseMoveHandler); + }; + + i.event.bind(i.scrollbarY, 'mousedown', function (e) { + currentPageY = e.pageY; + currentTop = _.toInt(dom.css(i.scrollbarY, 'top')) * i.railYRatio; + _.startScrolling(element, 'y'); + + i.event.bind(i.ownerDocument, 'mousemove', mouseMoveHandler); + i.event.once(i.ownerDocument, 'mouseup', mouseUpHandler); + + e.stopPropagation(); + e.preventDefault(); + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindMouseScrollXHandler(element, i); + bindMouseScrollYHandler(element, i); +}; + +},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],12:[function(require,module,exports){ +'use strict'; + +var _ = require('../../lib/helper'); +var dom = require('../../lib/dom'); +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindKeyboardHandler(element, i) { + var hovered = false; + i.event.bind(element, 'mouseenter', function () { + hovered = true; + }); + i.event.bind(element, 'mouseleave', function () { + hovered = false; + }); + + var shouldPrevent = false; + function shouldPreventDefault(deltaX, deltaY) { + var scrollTop = element.scrollTop; + if (deltaX === 0) { + if (!i.scrollbarYActive) { + return false; + } + if ((scrollTop === 0 && deltaY > 0) || (scrollTop >= i.contentHeight - i.containerHeight && deltaY < 0)) { + return !i.settings.wheelPropagation; + } + } + + var scrollLeft = element.scrollLeft; + if (deltaY === 0) { + if (!i.scrollbarXActive) { + return false; + } + if ((scrollLeft === 0 && deltaX < 0) || (scrollLeft >= i.contentWidth - i.containerWidth && deltaX > 0)) { + return !i.settings.wheelPropagation; + } + } + return true; + } + + i.event.bind(i.ownerDocument, 'keydown', function (e) { + if ((e.isDefaultPrevented && e.isDefaultPrevented()) || e.defaultPrevented) { + return; + } + + var focused = dom.matches(i.scrollbarX, ':focus') || + dom.matches(i.scrollbarY, ':focus'); + + if (!hovered && !focused) { + return; + } + + var activeElement = document.activeElement ? document.activeElement : i.ownerDocument.activeElement; + if (activeElement) { + if (activeElement.tagName === 'IFRAME') { + activeElement = activeElement.contentDocument.activeElement; + } else { + // go deeper if element is a webcomponent + while (activeElement.shadowRoot) { + activeElement = activeElement.shadowRoot.activeElement; + } + } + if (_.isEditable(activeElement)) { + return; + } + } + + var deltaX = 0; + var deltaY = 0; + + switch (e.which) { + case 37: // left + if (e.metaKey) { + deltaX = -i.contentWidth; + } else if (e.altKey) { + deltaX = -i.containerWidth; + } else { + deltaX = -30; + } + break; + case 38: // up + if (e.metaKey) { + deltaY = i.contentHeight; + } else if (e.altKey) { + deltaY = i.containerHeight; + } else { + deltaY = 30; + } + break; + case 39: // right + if (e.metaKey) { + deltaX = i.contentWidth; + } else if (e.altKey) { + deltaX = i.containerWidth; + } else { + deltaX = 30; + } + break; + case 40: // down + if (e.metaKey) { + deltaY = -i.contentHeight; + } else if (e.altKey) { + deltaY = -i.containerHeight; + } else { + deltaY = -30; + } + break; + case 33: // page up + deltaY = 90; + break; + case 32: // space bar + if (e.shiftKey) { + deltaY = 90; + } else { + deltaY = -90; + } + break; + case 34: // page down + deltaY = -90; + break; + case 35: // end + if (e.ctrlKey) { + deltaY = -i.contentHeight; + } else { + deltaY = -i.containerHeight; + } + break; + case 36: // home + if (e.ctrlKey) { + deltaY = element.scrollTop; + } else { + deltaY = i.containerHeight; + } + break; + default: + return; + } + + updateScroll(element, 'top', element.scrollTop - deltaY); + updateScroll(element, 'left', element.scrollLeft + deltaX); + updateGeometry(element); + + shouldPrevent = shouldPreventDefault(deltaX, deltaY); + if (shouldPrevent) { + e.preventDefault(); + } + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindKeyboardHandler(element, i); +}; + +},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],13:[function(require,module,exports){ +'use strict'; + +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindMouseWheelHandler(element, i) { + var shouldPrevent = false; + + function shouldPreventDefault(deltaX, deltaY) { + var scrollTop = element.scrollTop; + if (deltaX === 0) { + if (!i.scrollbarYActive) { + return false; + } + if ((scrollTop === 0 && deltaY > 0) || (scrollTop >= i.contentHeight - i.containerHeight && deltaY < 0)) { + return !i.settings.wheelPropagation; + } + } + + var scrollLeft = element.scrollLeft; + if (deltaY === 0) { + if (!i.scrollbarXActive) { + return false; + } + if ((scrollLeft === 0 && deltaX < 0) || (scrollLeft >= i.contentWidth - i.containerWidth && deltaX > 0)) { + return !i.settings.wheelPropagation; + } + } + return true; + } + + function getDeltaFromEvent(e) { + var deltaX = e.deltaX; + var deltaY = -1 * e.deltaY; + + if (typeof deltaX === "undefined" || typeof deltaY === "undefined") { + // OS X Safari + deltaX = -1 * e.wheelDeltaX / 6; + deltaY = e.wheelDeltaY / 6; + } + + if (e.deltaMode && e.deltaMode === 1) { + // Firefox in deltaMode 1: Line scrolling + deltaX *= 10; + deltaY *= 10; + } + + if (deltaX !== deltaX && deltaY !== deltaY/* NaN checks */) { + // IE in some mouse drivers + deltaX = 0; + deltaY = e.wheelDelta; + } + + if (e.shiftKey) { + // reverse axis with shift key + return [-deltaY, -deltaX]; + } + return [deltaX, deltaY]; + } + + function shouldBeConsumedByChild(deltaX, deltaY) { + var child = element.querySelector('textarea:hover, select[multiple]:hover, .ps-child:hover'); + if (child) { + if (!window.getComputedStyle(child).overflow.match(/(scroll|auto)/)) { + // if not scrollable + return false; + } + + var maxScrollTop = child.scrollHeight - child.clientHeight; + if (maxScrollTop > 0) { + if (!(child.scrollTop === 0 && deltaY > 0) && !(child.scrollTop === maxScrollTop && deltaY < 0)) { + return true; + } + } + var maxScrollLeft = child.scrollLeft - child.clientWidth; + if (maxScrollLeft > 0) { + if (!(child.scrollLeft === 0 && deltaX < 0) && !(child.scrollLeft === maxScrollLeft && deltaX > 0)) { + return true; + } + } + } + return false; + } + + function mousewheelHandler(e) { + var delta = getDeltaFromEvent(e); + + var deltaX = delta[0]; + var deltaY = delta[1]; + + if (shouldBeConsumedByChild(deltaX, deltaY)) { + return; + } + + shouldPrevent = false; + if (!i.settings.useBothWheelAxes) { + // deltaX will only be used for horizontal scrolling and deltaY will + // only be used for vertical scrolling - this is the default + updateScroll(element, 'top', element.scrollTop - (deltaY * i.settings.wheelSpeed)); + updateScroll(element, 'left', element.scrollLeft + (deltaX * i.settings.wheelSpeed)); + } else if (i.scrollbarYActive && !i.scrollbarXActive) { + // only vertical scrollbar is active and useBothWheelAxes option is + // active, so let's scroll vertical bar using both mouse wheel axes + if (deltaY) { + updateScroll(element, 'top', element.scrollTop - (deltaY * i.settings.wheelSpeed)); + } else { + updateScroll(element, 'top', element.scrollTop + (deltaX * i.settings.wheelSpeed)); + } + shouldPrevent = true; + } else if (i.scrollbarXActive && !i.scrollbarYActive) { + // useBothWheelAxes and only horizontal bar is active, so use both + // wheel axes for horizontal bar + if (deltaX) { + updateScroll(element, 'left', element.scrollLeft + (deltaX * i.settings.wheelSpeed)); + } else { + updateScroll(element, 'left', element.scrollLeft - (deltaY * i.settings.wheelSpeed)); + } + shouldPrevent = true; + } + + updateGeometry(element); + + shouldPrevent = (shouldPrevent || shouldPreventDefault(deltaX, deltaY)); + if (shouldPrevent) { + e.stopPropagation(); + e.preventDefault(); + } + } + + if (typeof window.onwheel !== "undefined") { + i.event.bind(element, 'wheel', mousewheelHandler); + } else if (typeof window.onmousewheel !== "undefined") { + i.event.bind(element, 'mousewheel', mousewheelHandler); + } +} + +module.exports = function (element) { + var i = instances.get(element); + bindMouseWheelHandler(element, i); +}; + +},{"../instances":18,"../update-geometry":19,"../update-scroll":20}],14:[function(require,module,exports){ +'use strict'; + +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); + +function bindNativeScrollHandler(element, i) { + i.event.bind(element, 'scroll', function () { + updateGeometry(element); + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindNativeScrollHandler(element, i); +}; + +},{"../instances":18,"../update-geometry":19}],15:[function(require,module,exports){ +'use strict'; + +var _ = require('../../lib/helper'); +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindSelectionHandler(element, i) { + function getRangeNode() { + var selection = window.getSelection ? window.getSelection() : + document.getSelection ? document.getSelection() : ''; + if (selection.toString().length === 0) { + return null; + } else { + return selection.getRangeAt(0).commonAncestorContainer; + } + } + + var scrollingLoop = null; + var scrollDiff = {top: 0, left: 0}; + function startScrolling() { + if (!scrollingLoop) { + scrollingLoop = setInterval(function () { + if (!instances.get(element)) { + clearInterval(scrollingLoop); + return; + } + + updateScroll(element, 'top', element.scrollTop + scrollDiff.top); + updateScroll(element, 'left', element.scrollLeft + scrollDiff.left); + updateGeometry(element); + }, 50); // every .1 sec + } + } + function stopScrolling() { + if (scrollingLoop) { + clearInterval(scrollingLoop); + scrollingLoop = null; + } + _.stopScrolling(element); + } + + var isSelected = false; + i.event.bind(i.ownerDocument, 'selectionchange', function () { + if (element.contains(getRangeNode())) { + isSelected = true; + } else { + isSelected = false; + stopScrolling(); + } + }); + i.event.bind(window, 'mouseup', function () { + if (isSelected) { + isSelected = false; + stopScrolling(); + } + }); + i.event.bind(window, 'keyup', function () { + if (isSelected) { + isSelected = false; + stopScrolling(); + } + }); + + i.event.bind(window, 'mousemove', function (e) { + if (isSelected) { + var mousePosition = {x: e.pageX, y: e.pageY}; + var containerGeometry = { + left: element.offsetLeft, + right: element.offsetLeft + element.offsetWidth, + top: element.offsetTop, + bottom: element.offsetTop + element.offsetHeight + }; + + if (mousePosition.x < containerGeometry.left + 3) { + scrollDiff.left = -5; + _.startScrolling(element, 'x'); + } else if (mousePosition.x > containerGeometry.right - 3) { + scrollDiff.left = 5; + _.startScrolling(element, 'x'); + } else { + scrollDiff.left = 0; + } + + if (mousePosition.y < containerGeometry.top + 3) { + if (containerGeometry.top + 3 - mousePosition.y < 5) { + scrollDiff.top = -5; + } else { + scrollDiff.top = -20; + } + _.startScrolling(element, 'y'); + } else if (mousePosition.y > containerGeometry.bottom - 3) { + if (mousePosition.y - containerGeometry.bottom + 3 < 5) { + scrollDiff.top = 5; + } else { + scrollDiff.top = 20; + } + _.startScrolling(element, 'y'); + } else { + scrollDiff.top = 0; + } + + if (scrollDiff.top === 0 && scrollDiff.left === 0) { + stopScrolling(); + } else { + startScrolling(); + } + } + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindSelectionHandler(element, i); +}; + +},{"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],16:[function(require,module,exports){ +'use strict'; + +var _ = require('../../lib/helper'); +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindTouchHandler(element, i, supportsTouch, supportsIePointer) { + function shouldPreventDefault(deltaX, deltaY) { + var scrollTop = element.scrollTop; + var scrollLeft = element.scrollLeft; + var magnitudeX = Math.abs(deltaX); + var magnitudeY = Math.abs(deltaY); + + if (magnitudeY > magnitudeX) { + // user is perhaps trying to swipe up/down the page + + if (((deltaY < 0) && (scrollTop === i.contentHeight - i.containerHeight)) || + ((deltaY > 0) && (scrollTop === 0))) { + return !i.settings.swipePropagation; + } + } else if (magnitudeX > magnitudeY) { + // user is perhaps trying to swipe left/right across the page + + if (((deltaX < 0) && (scrollLeft === i.contentWidth - i.containerWidth)) || + ((deltaX > 0) && (scrollLeft === 0))) { + return !i.settings.swipePropagation; + } + } + + return true; + } + + function applyTouchMove(differenceX, differenceY) { + updateScroll(element, 'top', element.scrollTop - differenceY); + updateScroll(element, 'left', element.scrollLeft - differenceX); + + updateGeometry(element); + } + + var startOffset = {}; + var startTime = 0; + var speed = {}; + var easingLoop = null; + var inGlobalTouch = false; + var inLocalTouch = false; + + function globalTouchStart() { + inGlobalTouch = true; + } + function globalTouchEnd() { + inGlobalTouch = false; + } + + function getTouch(e) { + if (e.targetTouches) { + return e.targetTouches[0]; + } else { + // Maybe IE pointer + return e; + } + } + function shouldHandle(e) { + if (e.targetTouches && e.targetTouches.length === 1) { + return true; + } + if (e.pointerType && e.pointerType !== 'mouse' && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) { + return true; + } + return false; + } + function touchStart(e) { + if (shouldHandle(e)) { + inLocalTouch = true; + + var touch = getTouch(e); + + startOffset.pageX = touch.pageX; + startOffset.pageY = touch.pageY; + + startTime = (new Date()).getTime(); + + if (easingLoop !== null) { + clearInterval(easingLoop); + } + + e.stopPropagation(); + } + } + function touchMove(e) { + if (!inLocalTouch && i.settings.swipePropagation) { + touchStart(e); + } + if (!inGlobalTouch && inLocalTouch && shouldHandle(e)) { + var touch = getTouch(e); + + var currentOffset = {pageX: touch.pageX, pageY: touch.pageY}; + + var differenceX = currentOffset.pageX - startOffset.pageX; + var differenceY = currentOffset.pageY - startOffset.pageY; + + applyTouchMove(differenceX, differenceY); + startOffset = currentOffset; + + var currentTime = (new Date()).getTime(); + + var timeGap = currentTime - startTime; + if (timeGap > 0) { + speed.x = differenceX / timeGap; + speed.y = differenceY / timeGap; + startTime = currentTime; + } + + if (shouldPreventDefault(differenceX, differenceY)) { + e.stopPropagation(); + e.preventDefault(); + } + } + } + function touchEnd() { + if (!inGlobalTouch && inLocalTouch) { + inLocalTouch = false; + + clearInterval(easingLoop); + easingLoop = setInterval(function () { + if (!instances.get(element)) { + clearInterval(easingLoop); + return; + } + + if (!speed.x && !speed.y) { + clearInterval(easingLoop); + return; + } + + if (Math.abs(speed.x) < 0.01 && Math.abs(speed.y) < 0.01) { + clearInterval(easingLoop); + return; + } + + applyTouchMove(speed.x * 30, speed.y * 30); + + speed.x *= 0.8; + speed.y *= 0.8; + }, 10); + } + } + + if (supportsTouch) { + i.event.bind(window, 'touchstart', globalTouchStart); + i.event.bind(window, 'touchend', globalTouchEnd); + i.event.bind(element, 'touchstart', touchStart); + i.event.bind(element, 'touchmove', touchMove); + i.event.bind(element, 'touchend', touchEnd); + } else if (supportsIePointer) { + if (window.PointerEvent) { + i.event.bind(window, 'pointerdown', globalTouchStart); + i.event.bind(window, 'pointerup', globalTouchEnd); + i.event.bind(element, 'pointerdown', touchStart); + i.event.bind(element, 'pointermove', touchMove); + i.event.bind(element, 'pointerup', touchEnd); + } else if (window.MSPointerEvent) { + i.event.bind(window, 'MSPointerDown', globalTouchStart); + i.event.bind(window, 'MSPointerUp', globalTouchEnd); + i.event.bind(element, 'MSPointerDown', touchStart); + i.event.bind(element, 'MSPointerMove', touchMove); + i.event.bind(element, 'MSPointerUp', touchEnd); + } + } +} + +module.exports = function (element) { + if (!_.env.supportsTouch && !_.env.supportsIePointer) { + return; + } + + var i = instances.get(element); + bindTouchHandler(element, i, _.env.supportsTouch, _.env.supportsIePointer); +}; + +},{"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],17:[function(require,module,exports){ +'use strict'; + +var _ = require('../lib/helper'); +var cls = require('../lib/class'); +var instances = require('./instances'); +var updateGeometry = require('./update-geometry'); + +// Handlers +var handlers = { + 'click-rail': require('./handler/click-rail'), + 'drag-scrollbar': require('./handler/drag-scrollbar'), + 'keyboard': require('./handler/keyboard'), + 'wheel': require('./handler/mouse-wheel'), + 'touch': require('./handler/touch'), + 'selection': require('./handler/selection') +}; +var nativeScrollHandler = require('./handler/native-scroll'); + +module.exports = function (element, userSettings) { + userSettings = typeof userSettings === 'object' ? userSettings : {}; + + cls.add(element, 'ps-container'); + + // Create a plugin instance. + var i = instances.add(element); + + i.settings = _.extend(i.settings, userSettings); + cls.add(element, 'ps-theme-' + i.settings.theme); + + i.settings.handlers.forEach(function (handlerName) { + handlers[handlerName](element); + }); + + nativeScrollHandler(element); + + updateGeometry(element); +}; + +},{"../lib/class":2,"../lib/helper":6,"./handler/click-rail":10,"./handler/drag-scrollbar":11,"./handler/keyboard":12,"./handler/mouse-wheel":13,"./handler/native-scroll":14,"./handler/selection":15,"./handler/touch":16,"./instances":18,"./update-geometry":19}],18:[function(require,module,exports){ +'use strict'; + +var _ = require('../lib/helper'); +var cls = require('../lib/class'); +var defaultSettings = require('./default-setting'); +var dom = require('../lib/dom'); +var EventManager = require('../lib/event-manager'); +var guid = require('../lib/guid'); + +var instances = {}; + +function Instance(element) { + var i = this; + + i.settings = _.clone(defaultSettings); + i.containerWidth = null; + i.containerHeight = null; + i.contentWidth = null; + i.contentHeight = null; + + i.isRtl = dom.css(element, 'direction') === "rtl"; + i.isNegativeScroll = (function () { + var originalScrollLeft = element.scrollLeft; + var result = null; + element.scrollLeft = -1; + result = element.scrollLeft < 0; + element.scrollLeft = originalScrollLeft; + return result; + })(); + i.negativeScrollAdjustment = i.isNegativeScroll ? element.scrollWidth - element.clientWidth : 0; + i.event = new EventManager(); + i.ownerDocument = element.ownerDocument || document; + + function focus() { + cls.add(element, 'ps-focus'); + } + + function blur() { + cls.remove(element, 'ps-focus'); + } + + i.scrollbarXRail = dom.appendTo(dom.e('div', 'ps-scrollbar-x-rail'), element); + i.scrollbarX = dom.appendTo(dom.e('div', 'ps-scrollbar-x'), i.scrollbarXRail); + i.scrollbarX.setAttribute('tabindex', 0); + i.event.bind(i.scrollbarX, 'focus', focus); + i.event.bind(i.scrollbarX, 'blur', blur); + i.scrollbarXActive = null; + i.scrollbarXWidth = null; + i.scrollbarXLeft = null; + i.scrollbarXBottom = _.toInt(dom.css(i.scrollbarXRail, 'bottom')); + i.isScrollbarXUsingBottom = i.scrollbarXBottom === i.scrollbarXBottom; // !isNaN + i.scrollbarXTop = i.isScrollbarXUsingBottom ? null : _.toInt(dom.css(i.scrollbarXRail, 'top')); + i.railBorderXWidth = _.toInt(dom.css(i.scrollbarXRail, 'borderLeftWidth')) + _.toInt(dom.css(i.scrollbarXRail, 'borderRightWidth')); + // Set rail to display:block to calculate margins + dom.css(i.scrollbarXRail, 'display', 'block'); + i.railXMarginWidth = _.toInt(dom.css(i.scrollbarXRail, 'marginLeft')) + _.toInt(dom.css(i.scrollbarXRail, 'marginRight')); + dom.css(i.scrollbarXRail, 'display', ''); + i.railXWidth = null; + i.railXRatio = null; + + i.scrollbarYRail = dom.appendTo(dom.e('div', 'ps-scrollbar-y-rail'), element); + i.scrollbarY = dom.appendTo(dom.e('div', 'ps-scrollbar-y'), i.scrollbarYRail); + i.scrollbarY.setAttribute('tabindex', 0); + i.event.bind(i.scrollbarY, 'focus', focus); + i.event.bind(i.scrollbarY, 'blur', blur); + i.scrollbarYActive = null; + i.scrollbarYHeight = null; + i.scrollbarYTop = null; + i.scrollbarYRight = _.toInt(dom.css(i.scrollbarYRail, 'right')); + i.isScrollbarYUsingRight = i.scrollbarYRight === i.scrollbarYRight; // !isNaN + i.scrollbarYLeft = i.isScrollbarYUsingRight ? null : _.toInt(dom.css(i.scrollbarYRail, 'left')); + i.scrollbarYOuterWidth = i.isRtl ? _.outerWidth(i.scrollbarY) : null; + i.railBorderYWidth = _.toInt(dom.css(i.scrollbarYRail, 'borderTopWidth')) + _.toInt(dom.css(i.scrollbarYRail, 'borderBottomWidth')); + dom.css(i.scrollbarYRail, 'display', 'block'); + i.railYMarginHeight = _.toInt(dom.css(i.scrollbarYRail, 'marginTop')) + _.toInt(dom.css(i.scrollbarYRail, 'marginBottom')); + dom.css(i.scrollbarYRail, 'display', ''); + i.railYHeight = null; + i.railYRatio = null; +} + +function getId(element) { + return element.getAttribute('data-ps-id'); +} + +function setId(element, id) { + element.setAttribute('data-ps-id', id); +} + +function removeId(element) { + element.removeAttribute('data-ps-id'); +} + +exports.add = function (element) { + var newId = guid(); + setId(element, newId); + instances[newId] = new Instance(element); + return instances[newId]; +}; + +exports.remove = function (element) { + delete instances[getId(element)]; + removeId(element); +}; + +exports.get = function (element) { + return instances[getId(element)]; +}; + +},{"../lib/class":2,"../lib/dom":3,"../lib/event-manager":4,"../lib/guid":5,"../lib/helper":6,"./default-setting":8}],19:[function(require,module,exports){ +'use strict'; + +var _ = require('../lib/helper'); +var cls = require('../lib/class'); +var dom = require('../lib/dom'); +var instances = require('./instances'); +var updateScroll = require('./update-scroll'); + +function getThumbSize(i, thumbSize) { + if (i.settings.minScrollbarLength) { + thumbSize = Math.max(thumbSize, i.settings.minScrollbarLength); + } + if (i.settings.maxScrollbarLength) { + thumbSize = Math.min(thumbSize, i.settings.maxScrollbarLength); + } + return thumbSize; +} + +function updateCss(element, i) { + var xRailOffset = {width: i.railXWidth}; + if (i.isRtl) { + xRailOffset.left = i.negativeScrollAdjustment + element.scrollLeft + i.containerWidth - i.contentWidth; + } else { + xRailOffset.left = element.scrollLeft; + } + if (i.isScrollbarXUsingBottom) { + xRailOffset.bottom = i.scrollbarXBottom - element.scrollTop; + } else { + xRailOffset.top = i.scrollbarXTop + element.scrollTop; + } + dom.css(i.scrollbarXRail, xRailOffset); + + var yRailOffset = {top: element.scrollTop, height: i.railYHeight}; + if (i.isScrollbarYUsingRight) { + if (i.isRtl) { + yRailOffset.right = i.contentWidth - (i.negativeScrollAdjustment + element.scrollLeft) - i.scrollbarYRight - i.scrollbarYOuterWidth; + } else { + yRailOffset.right = i.scrollbarYRight - element.scrollLeft; + } + } else { + if (i.isRtl) { + yRailOffset.left = i.negativeScrollAdjustment + element.scrollLeft + i.containerWidth * 2 - i.contentWidth - i.scrollbarYLeft - i.scrollbarYOuterWidth; + } else { + yRailOffset.left = i.scrollbarYLeft + element.scrollLeft; + } + } + dom.css(i.scrollbarYRail, yRailOffset); + + dom.css(i.scrollbarX, {left: i.scrollbarXLeft, width: i.scrollbarXWidth - i.railBorderXWidth}); + dom.css(i.scrollbarY, {top: i.scrollbarYTop, height: i.scrollbarYHeight - i.railBorderYWidth}); +} + +module.exports = function (element) { + var i = instances.get(element); + + i.containerWidth = element.clientWidth; + i.containerHeight = element.clientHeight; + i.contentWidth = element.scrollWidth; + i.contentHeight = element.scrollHeight; + + var existingRails; + if (!element.contains(i.scrollbarXRail)) { + existingRails = dom.queryChildren(element, '.ps-scrollbar-x-rail'); + if (existingRails.length > 0) { + existingRails.forEach(function (rail) { + dom.remove(rail); + }); + } + dom.appendTo(i.scrollbarXRail, element); + } + if (!element.contains(i.scrollbarYRail)) { + existingRails = dom.queryChildren(element, '.ps-scrollbar-y-rail'); + if (existingRails.length > 0) { + existingRails.forEach(function (rail) { + dom.remove(rail); + }); + } + dom.appendTo(i.scrollbarYRail, element); + } + + if (!i.settings.suppressScrollX && i.containerWidth + i.settings.scrollXMarginOffset < i.contentWidth) { + i.scrollbarXActive = true; + i.railXWidth = i.containerWidth - i.railXMarginWidth; + i.railXRatio = i.containerWidth / i.railXWidth; + i.scrollbarXWidth = getThumbSize(i, _.toInt(i.railXWidth * i.containerWidth / i.contentWidth)); + i.scrollbarXLeft = _.toInt((i.negativeScrollAdjustment + element.scrollLeft) * (i.railXWidth - i.scrollbarXWidth) / (i.contentWidth - i.containerWidth)); + } else { + i.scrollbarXActive = false; + } + + if (!i.settings.suppressScrollY && i.containerHeight + i.settings.scrollYMarginOffset < i.contentHeight) { + i.scrollbarYActive = true; + i.railYHeight = i.containerHeight - i.railYMarginHeight; + i.railYRatio = i.containerHeight / i.railYHeight; + i.scrollbarYHeight = getThumbSize(i, _.toInt(i.railYHeight * i.containerHeight / i.contentHeight)); + i.scrollbarYTop = _.toInt(element.scrollTop * (i.railYHeight - i.scrollbarYHeight) / (i.contentHeight - i.containerHeight)); + } else { + i.scrollbarYActive = false; + } + + if (i.scrollbarXLeft >= i.railXWidth - i.scrollbarXWidth) { + i.scrollbarXLeft = i.railXWidth - i.scrollbarXWidth; + } + if (i.scrollbarYTop >= i.railYHeight - i.scrollbarYHeight) { + i.scrollbarYTop = i.railYHeight - i.scrollbarYHeight; + } + + updateCss(element, i); + + if (i.scrollbarXActive) { + cls.add(element, 'ps-active-x'); + } else { + cls.remove(element, 'ps-active-x'); + i.scrollbarXWidth = 0; + i.scrollbarXLeft = 0; + updateScroll(element, 'left', 0); + } + if (i.scrollbarYActive) { + cls.add(element, 'ps-active-y'); + } else { + cls.remove(element, 'ps-active-y'); + i.scrollbarYHeight = 0; + i.scrollbarYTop = 0; + updateScroll(element, 'top', 0); + } +}; + +},{"../lib/class":2,"../lib/dom":3,"../lib/helper":6,"./instances":18,"./update-scroll":20}],20:[function(require,module,exports){ +'use strict'; + +var instances = require('./instances'); + +var lastTop; +var lastLeft; + +var createDOMEvent = function (name) { + var event = document.createEvent("Event"); + event.initEvent(name, true, true); + return event; +}; + +module.exports = function (element, axis, value) { + if (typeof element === 'undefined') { + throw 'You must provide an element to the update-scroll function'; + } + + if (typeof axis === 'undefined') { + throw 'You must provide an axis to the update-scroll function'; + } + + if (typeof value === 'undefined') { + throw 'You must provide a value to the update-scroll function'; + } + + if (axis === 'top' && value <= 0) { + element.scrollTop = value = 0; // don't allow negative scroll + element.dispatchEvent(createDOMEvent('ps-y-reach-start')); + } + + if (axis === 'left' && value <= 0) { + element.scrollLeft = value = 0; // don't allow negative scroll + element.dispatchEvent(createDOMEvent('ps-x-reach-start')); + } + + var i = instances.get(element); + + if (axis === 'top' && value >= i.contentHeight - i.containerHeight) { + // don't allow scroll past container + value = i.contentHeight - i.containerHeight; + if (value - element.scrollTop <= 1) { + // mitigates rounding errors on non-subpixel scroll values + value = element.scrollTop; + } else { + element.scrollTop = value; + } + element.dispatchEvent(createDOMEvent('ps-y-reach-end')); + } + + if (axis === 'left' && value >= i.contentWidth - i.containerWidth) { + // don't allow scroll past container + value = i.contentWidth - i.containerWidth; + if (value - element.scrollLeft <= 1) { + // mitigates rounding errors on non-subpixel scroll values + value = element.scrollLeft; + } else { + element.scrollLeft = value; + } + element.dispatchEvent(createDOMEvent('ps-x-reach-end')); + } + + if (!lastTop) { + lastTop = element.scrollTop; + } + + if (!lastLeft) { + lastLeft = element.scrollLeft; + } + + if (axis === 'top' && value < lastTop) { + element.dispatchEvent(createDOMEvent('ps-scroll-up')); + } + + if (axis === 'top' && value > lastTop) { + element.dispatchEvent(createDOMEvent('ps-scroll-down')); + } + + if (axis === 'left' && value < lastLeft) { + element.dispatchEvent(createDOMEvent('ps-scroll-left')); + } + + if (axis === 'left' && value > lastLeft) { + element.dispatchEvent(createDOMEvent('ps-scroll-right')); + } + + if (axis === 'top') { + element.scrollTop = lastTop = value; + element.dispatchEvent(createDOMEvent('ps-scroll-y')); + } + + if (axis === 'left') { + element.scrollLeft = lastLeft = value; + element.dispatchEvent(createDOMEvent('ps-scroll-x')); + } + +}; + +},{"./instances":18}],21:[function(require,module,exports){ +'use strict'; + +var _ = require('../lib/helper'); +var dom = require('../lib/dom'); +var instances = require('./instances'); +var updateGeometry = require('./update-geometry'); +var updateScroll = require('./update-scroll'); + +module.exports = function (element) { + var i = instances.get(element); + + if (!i) { + return; + } + + // Recalcuate negative scrollLeft adjustment + i.negativeScrollAdjustment = i.isNegativeScroll ? element.scrollWidth - element.clientWidth : 0; + + // Recalculate rail margins + dom.css(i.scrollbarXRail, 'display', 'block'); + dom.css(i.scrollbarYRail, 'display', 'block'); + i.railXMarginWidth = _.toInt(dom.css(i.scrollbarXRail, 'marginLeft')) + _.toInt(dom.css(i.scrollbarXRail, 'marginRight')); + i.railYMarginHeight = _.toInt(dom.css(i.scrollbarYRail, 'marginTop')) + _.toInt(dom.css(i.scrollbarYRail, 'marginBottom')); + + // Hide scrollbars not to affect scrollWidth and scrollHeight + dom.css(i.scrollbarXRail, 'display', 'none'); + dom.css(i.scrollbarYRail, 'display', 'none'); + + updateGeometry(element); + + // Update top/left scroll to trigger events + updateScroll(element, 'top', element.scrollTop); + updateScroll(element, 'left', element.scrollLeft); + + dom.css(i.scrollbarXRail, 'display', ''); + dom.css(i.scrollbarYRail, 'display', ''); +}; + +},{"../lib/dom":3,"../lib/helper":6,"./instances":18,"./update-geometry":19,"./update-scroll":20}]},{},[1]); diff --git a/library/perfect-scrollbar/js/perfect-scrollbar.jquery.min.js b/library/perfect-scrollbar/js/perfect-scrollbar.jquery.min.js new file mode 100644 index 000000000..01bc76888 --- /dev/null +++ b/library/perfect-scrollbar/js/perfect-scrollbar.jquery.min.js @@ -0,0 +1,2 @@ +/* perfect-scrollbar v0.6.16 */ +!function t(e,n,r){function o(i,s){if(!n[i]){if(!e[i]){var a="function"==typeof require&&require;if(!s&&a)return a(i,!0);if(l)return l(i,!0);var c=new Error("Cannot find module '"+i+"'");throw c.code="MODULE_NOT_FOUND",c}var u=n[i]={exports:{}};e[i][0].call(u.exports,function(t){var n=e[i][1][t];return o(n?n:t)},u,u.exports,t,e,n,r)}return n[i].exports}for(var l="function"==typeof require&&require,i=0;i=0&&n.splice(r,1),t.className=n.join(" ")}n.add=function(t,e){t.classList?t.classList.add(e):r(t,e)},n.remove=function(t,e){t.classList?t.classList.remove(e):o(t,e)},n.list=function(t){return t.classList?Array.prototype.slice.apply(t.classList):t.className.split(" ")}},{}],3:[function(t,e,n){"use strict";function r(t,e){return window.getComputedStyle(t)[e]}function o(t,e,n){return"number"==typeof n&&(n=n.toString()+"px"),t.style[e]=n,t}function l(t,e){for(var n in e){var r=e[n];"number"==typeof r&&(r=r.toString()+"px"),t.style[n]=r}return t}var i={};i.e=function(t,e){var n=document.createElement(t);return n.className=e,n},i.appendTo=function(t,e){return e.appendChild(t),t},i.css=function(t,e,n){return"object"==typeof e?l(t,e):"undefined"==typeof n?r(t,e):o(t,e,n)},i.matches=function(t,e){return"undefined"!=typeof t.matches?t.matches(e):"undefined"!=typeof t.matchesSelector?t.matchesSelector(e):"undefined"!=typeof t.webkitMatchesSelector?t.webkitMatchesSelector(e):"undefined"!=typeof t.mozMatchesSelector?t.mozMatchesSelector(e):"undefined"!=typeof t.msMatchesSelector?t.msMatchesSelector(e):void 0},i.remove=function(t){"undefined"!=typeof t.remove?t.remove():t.parentNode&&t.parentNode.removeChild(t)},i.queryChildren=function(t,e){return Array.prototype.filter.call(t.childNodes,function(t){return i.matches(t,e)})},e.exports=i},{}],4:[function(t,e,n){"use strict";var r=function(t){this.element=t,this.events={}};r.prototype.bind=function(t,e){"undefined"==typeof this.events[t]&&(this.events[t]=[]),this.events[t].push(e),this.element.addEventListener(t,e,!1)},r.prototype.unbind=function(t,e){var n="undefined"!=typeof e;this.events[t]=this.events[t].filter(function(r){return!(!n||r===e)||(this.element.removeEventListener(t,r,!1),!1)},this)},r.prototype.unbindAll=function(){for(var t in this.events)this.unbind(t)};var o=function(){this.eventElements=[]};o.prototype.eventElement=function(t){var e=this.eventElements.filter(function(e){return e.element===t})[0];return"undefined"==typeof e&&(e=new r(t),this.eventElements.push(e)),e},o.prototype.bind=function(t,e,n){this.eventElement(t).bind(e,n)},o.prototype.unbind=function(t,e,n){this.eventElement(t).unbind(e,n)},o.prototype.unbindAll=function(){for(var t=0;te.scrollbarYTop?1:-1;i(t,"top",t.scrollTop+s*e.containerHeight),l(t),r.stopPropagation()}),e.event.bind(e.scrollbarX,"click",r),e.event.bind(e.scrollbarXRail,"click",function(r){var o=r.pageX-window.pageXOffset-n(e.scrollbarXRail).left,s=o>e.scrollbarXLeft?1:-1;i(t,"left",t.scrollLeft+s*e.containerWidth),l(t),r.stopPropagation()})}var o=t("../instances"),l=t("../update-geometry"),i=t("../update-scroll");e.exports=function(t){var e=o.get(t);r(t,e)}},{"../instances":18,"../update-geometry":19,"../update-scroll":20}],11:[function(t,e,n){"use strict";function r(t,e){function n(n){var o=r+n*e.railXRatio,i=Math.max(0,e.scrollbarXRail.getBoundingClientRect().left)+e.railXRatio*(e.railXWidth-e.scrollbarXWidth);o<0?e.scrollbarXLeft=0:o>i?e.scrollbarXLeft=i:e.scrollbarXLeft=o;var s=l.toInt(e.scrollbarXLeft*(e.contentWidth-e.containerWidth)/(e.containerWidth-e.railXRatio*e.scrollbarXWidth))-e.negativeScrollAdjustment;c(t,"left",s)}var r=null,o=null,s=function(e){n(e.pageX-o),a(t),e.stopPropagation(),e.preventDefault()},u=function(){l.stopScrolling(t,"x"),e.event.unbind(e.ownerDocument,"mousemove",s)};e.event.bind(e.scrollbarX,"mousedown",function(n){o=n.pageX,r=l.toInt(i.css(e.scrollbarX,"left"))*e.railXRatio,l.startScrolling(t,"x"),e.event.bind(e.ownerDocument,"mousemove",s),e.event.once(e.ownerDocument,"mouseup",u),n.stopPropagation(),n.preventDefault()})}function o(t,e){function n(n){var o=r+n*e.railYRatio,i=Math.max(0,e.scrollbarYRail.getBoundingClientRect().top)+e.railYRatio*(e.railYHeight-e.scrollbarYHeight);o<0?e.scrollbarYTop=0:o>i?e.scrollbarYTop=i:e.scrollbarYTop=o;var s=l.toInt(e.scrollbarYTop*(e.contentHeight-e.containerHeight)/(e.containerHeight-e.railYRatio*e.scrollbarYHeight));c(t,"top",s)}var r=null,o=null,s=function(e){n(e.pageY-o),a(t),e.stopPropagation(),e.preventDefault()},u=function(){l.stopScrolling(t,"y"),e.event.unbind(e.ownerDocument,"mousemove",s)};e.event.bind(e.scrollbarY,"mousedown",function(n){o=n.pageY,r=l.toInt(i.css(e.scrollbarY,"top"))*e.railYRatio,l.startScrolling(t,"y"),e.event.bind(e.ownerDocument,"mousemove",s),e.event.once(e.ownerDocument,"mouseup",u),n.stopPropagation(),n.preventDefault()})}var l=t("../../lib/helper"),i=t("../../lib/dom"),s=t("../instances"),a=t("../update-geometry"),c=t("../update-scroll");e.exports=function(t){var e=s.get(t);r(t,e),o(t,e)}},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],12:[function(t,e,n){"use strict";function r(t,e){function n(n,r){var o=t.scrollTop;if(0===n){if(!e.scrollbarYActive)return!1;if(0===o&&r>0||o>=e.contentHeight-e.containerHeight&&r<0)return!e.settings.wheelPropagation}var l=t.scrollLeft;if(0===r){if(!e.scrollbarXActive)return!1;if(0===l&&n<0||l>=e.contentWidth-e.containerWidth&&n>0)return!e.settings.wheelPropagation}return!0}var r=!1;e.event.bind(t,"mouseenter",function(){r=!0}),e.event.bind(t,"mouseleave",function(){r=!1});var i=!1;e.event.bind(e.ownerDocument,"keydown",function(c){if(!(c.isDefaultPrevented&&c.isDefaultPrevented()||c.defaultPrevented)){var u=l.matches(e.scrollbarX,":focus")||l.matches(e.scrollbarY,":focus");if(r||u){var d=document.activeElement?document.activeElement:e.ownerDocument.activeElement;if(d){if("IFRAME"===d.tagName)d=d.contentDocument.activeElement;else for(;d.shadowRoot;)d=d.shadowRoot.activeElement;if(o.isEditable(d))return}var p=0,f=0;switch(c.which){case 37:p=c.metaKey?-e.contentWidth:c.altKey?-e.containerWidth:-30;break;case 38:f=c.metaKey?e.contentHeight:c.altKey?e.containerHeight:30;break;case 39:p=c.metaKey?e.contentWidth:c.altKey?e.containerWidth:30;break;case 40:f=c.metaKey?-e.contentHeight:c.altKey?-e.containerHeight:-30;break;case 33:f=90;break;case 32:f=c.shiftKey?90:-90;break;case 34:f=-90;break;case 35:f=c.ctrlKey?-e.contentHeight:-e.containerHeight;break;case 36:f=c.ctrlKey?t.scrollTop:e.containerHeight;break;default:return}a(t,"top",t.scrollTop-f),a(t,"left",t.scrollLeft+p),s(t),i=n(p,f),i&&c.preventDefault()}}})}var o=t("../../lib/helper"),l=t("../../lib/dom"),i=t("../instances"),s=t("../update-geometry"),a=t("../update-scroll");e.exports=function(t){var e=i.get(t);r(t,e)}},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],13:[function(t,e,n){"use strict";function r(t,e){function n(n,r){var o=t.scrollTop;if(0===n){if(!e.scrollbarYActive)return!1;if(0===o&&r>0||o>=e.contentHeight-e.containerHeight&&r<0)return!e.settings.wheelPropagation}var l=t.scrollLeft;if(0===r){if(!e.scrollbarXActive)return!1;if(0===l&&n<0||l>=e.contentWidth-e.containerWidth&&n>0)return!e.settings.wheelPropagation}return!0}function r(t){var e=t.deltaX,n=-1*t.deltaY;return"undefined"!=typeof e&&"undefined"!=typeof n||(e=-1*t.wheelDeltaX/6,n=t.wheelDeltaY/6),t.deltaMode&&1===t.deltaMode&&(e*=10,n*=10),e!==e&&n!==n&&(e=0,n=t.wheelDelta),t.shiftKey?[-n,-e]:[e,n]}function o(e,n){var r=t.querySelector("textarea:hover, select[multiple]:hover, .ps-child:hover");if(r){if(!window.getComputedStyle(r).overflow.match(/(scroll|auto)/))return!1;var o=r.scrollHeight-r.clientHeight;if(o>0&&!(0===r.scrollTop&&n>0||r.scrollTop===o&&n<0))return!0;var l=r.scrollLeft-r.clientWidth;if(l>0&&!(0===r.scrollLeft&&e<0||r.scrollLeft===l&&e>0))return!0}return!1}function s(s){var c=r(s),u=c[0],d=c[1];o(u,d)||(a=!1,e.settings.useBothWheelAxes?e.scrollbarYActive&&!e.scrollbarXActive?(d?i(t,"top",t.scrollTop-d*e.settings.wheelSpeed):i(t,"top",t.scrollTop+u*e.settings.wheelSpeed),a=!0):e.scrollbarXActive&&!e.scrollbarYActive&&(u?i(t,"left",t.scrollLeft+u*e.settings.wheelSpeed):i(t,"left",t.scrollLeft-d*e.settings.wheelSpeed),a=!0):(i(t,"top",t.scrollTop-d*e.settings.wheelSpeed),i(t,"left",t.scrollLeft+u*e.settings.wheelSpeed)),l(t),a=a||n(u,d),a&&(s.stopPropagation(),s.preventDefault()))}var a=!1;"undefined"!=typeof window.onwheel?e.event.bind(t,"wheel",s):"undefined"!=typeof window.onmousewheel&&e.event.bind(t,"mousewheel",s)}var o=t("../instances"),l=t("../update-geometry"),i=t("../update-scroll");e.exports=function(t){var e=o.get(t);r(t,e)}},{"../instances":18,"../update-geometry":19,"../update-scroll":20}],14:[function(t,e,n){"use strict";function r(t,e){e.event.bind(t,"scroll",function(){l(t)})}var o=t("../instances"),l=t("../update-geometry");e.exports=function(t){var e=o.get(t);r(t,e)}},{"../instances":18,"../update-geometry":19}],15:[function(t,e,n){"use strict";function r(t,e){function n(){var t=window.getSelection?window.getSelection():document.getSelection?document.getSelection():"";return 0===t.toString().length?null:t.getRangeAt(0).commonAncestorContainer}function r(){c||(c=setInterval(function(){return l.get(t)?(s(t,"top",t.scrollTop+u.top),s(t,"left",t.scrollLeft+u.left),void i(t)):void clearInterval(c)},50))}function a(){c&&(clearInterval(c),c=null),o.stopScrolling(t)}var c=null,u={top:0,left:0},d=!1;e.event.bind(e.ownerDocument,"selectionchange",function(){t.contains(n())?d=!0:(d=!1,a())}),e.event.bind(window,"mouseup",function(){d&&(d=!1,a())}),e.event.bind(window,"keyup",function(){d&&(d=!1,a())}),e.event.bind(window,"mousemove",function(e){if(d){var n={x:e.pageX,y:e.pageY},l={left:t.offsetLeft,right:t.offsetLeft+t.offsetWidth,top:t.offsetTop,bottom:t.offsetTop+t.offsetHeight};n.xl.right-3?(u.left=5,o.startScrolling(t,"x")):u.left=0,n.yl.bottom-3?(n.y-l.bottom+3<5?u.top=5:u.top=20,o.startScrolling(t,"y")):u.top=0,0===u.top&&0===u.left?a():r()}})}var o=t("../../lib/helper"),l=t("../instances"),i=t("../update-geometry"),s=t("../update-scroll");e.exports=function(t){var e=l.get(t);r(t,e)}},{"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],16:[function(t,e,n){"use strict";function r(t,e,n,r){function o(n,r){var o=t.scrollTop,l=t.scrollLeft,i=Math.abs(n),s=Math.abs(r);if(s>i){if(r<0&&o===e.contentHeight-e.containerHeight||r>0&&0===o)return!e.settings.swipePropagation}else if(i>s&&(n<0&&l===e.contentWidth-e.containerWidth||n>0&&0===l))return!e.settings.swipePropagation;return!0}function a(e,n){s(t,"top",t.scrollTop-n),s(t,"left",t.scrollLeft-e),i(t)}function c(){w=!0}function u(){w=!1}function d(t){return t.targetTouches?t.targetTouches[0]:t}function p(t){return!(!t.targetTouches||1!==t.targetTouches.length)||!(!t.pointerType||"mouse"===t.pointerType||t.pointerType===t.MSPOINTER_TYPE_MOUSE)}function f(t){if(p(t)){Y=!0;var e=d(t);g.pageX=e.pageX,g.pageY=e.pageY,v=(new Date).getTime(),null!==y&&clearInterval(y),t.stopPropagation()}}function h(t){if(!Y&&e.settings.swipePropagation&&f(t),!w&&Y&&p(t)){var n=d(t),r={pageX:n.pageX,pageY:n.pageY},l=r.pageX-g.pageX,i=r.pageY-g.pageY;a(l,i),g=r;var s=(new Date).getTime(),c=s-v;c>0&&(m.x=l/c,m.y=i/c,v=s),o(l,i)&&(t.stopPropagation(),t.preventDefault())}}function b(){!w&&Y&&(Y=!1,clearInterval(y),y=setInterval(function(){return l.get(t)&&(m.x||m.y)?Math.abs(m.x)<.01&&Math.abs(m.y)<.01?void clearInterval(y):(a(30*m.x,30*m.y),m.x*=.8,void(m.y*=.8)):void clearInterval(y)},10))}var g={},v=0,m={},y=null,w=!1,Y=!1;n?(e.event.bind(window,"touchstart",c),e.event.bind(window,"touchend",u),e.event.bind(t,"touchstart",f),e.event.bind(t,"touchmove",h),e.event.bind(t,"touchend",b)):r&&(window.PointerEvent?(e.event.bind(window,"pointerdown",c),e.event.bind(window,"pointerup",u),e.event.bind(t,"pointerdown",f),e.event.bind(t,"pointermove",h),e.event.bind(t,"pointerup",b)):window.MSPointerEvent&&(e.event.bind(window,"MSPointerDown",c),e.event.bind(window,"MSPointerUp",u),e.event.bind(t,"MSPointerDown",f),e.event.bind(t,"MSPointerMove",h),e.event.bind(t,"MSPointerUp",b)))}var o=t("../../lib/helper"),l=t("../instances"),i=t("../update-geometry"),s=t("../update-scroll");e.exports=function(t){if(o.env.supportsTouch||o.env.supportsIePointer){var e=l.get(t);r(t,e,o.env.supportsTouch,o.env.supportsIePointer)}}},{"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],17:[function(t,e,n){"use strict";var r=t("../lib/helper"),o=t("../lib/class"),l=t("./instances"),i=t("./update-geometry"),s={"click-rail":t("./handler/click-rail"),"drag-scrollbar":t("./handler/drag-scrollbar"),keyboard:t("./handler/keyboard"),wheel:t("./handler/mouse-wheel"),touch:t("./handler/touch"),selection:t("./handler/selection")},a=t("./handler/native-scroll");e.exports=function(t,e){e="object"==typeof e?e:{},o.add(t,"ps-container");var n=l.add(t);n.settings=r.extend(n.settings,e),o.add(t,"ps-theme-"+n.settings.theme),n.settings.handlers.forEach(function(e){s[e](t)}),a(t),i(t)}},{"../lib/class":2,"../lib/helper":6,"./handler/click-rail":10,"./handler/drag-scrollbar":11,"./handler/keyboard":12,"./handler/mouse-wheel":13,"./handler/native-scroll":14,"./handler/selection":15,"./handler/touch":16,"./instances":18,"./update-geometry":19}],18:[function(t,e,n){"use strict";function r(t){function e(){a.add(t,"ps-focus")}function n(){a.remove(t,"ps-focus")}var r=this;r.settings=s.clone(c),r.containerWidth=null,r.containerHeight=null,r.contentWidth=null,r.contentHeight=null,r.isRtl="rtl"===u.css(t,"direction"),r.isNegativeScroll=function(){var e=t.scrollLeft,n=null;return t.scrollLeft=-1,n=t.scrollLeft<0,t.scrollLeft=e,n}(),r.negativeScrollAdjustment=r.isNegativeScroll?t.scrollWidth-t.clientWidth:0,r.event=new d,r.ownerDocument=t.ownerDocument||document,r.scrollbarXRail=u.appendTo(u.e("div","ps-scrollbar-x-rail"),t),r.scrollbarX=u.appendTo(u.e("div","ps-scrollbar-x"),r.scrollbarXRail),r.scrollbarX.setAttribute("tabindex",0),r.event.bind(r.scrollbarX,"focus",e),r.event.bind(r.scrollbarX,"blur",n),r.scrollbarXActive=null,r.scrollbarXWidth=null,r.scrollbarXLeft=null,r.scrollbarXBottom=s.toInt(u.css(r.scrollbarXRail,"bottom")),r.isScrollbarXUsingBottom=r.scrollbarXBottom===r.scrollbarXBottom,r.scrollbarXTop=r.isScrollbarXUsingBottom?null:s.toInt(u.css(r.scrollbarXRail,"top")),r.railBorderXWidth=s.toInt(u.css(r.scrollbarXRail,"borderLeftWidth"))+s.toInt(u.css(r.scrollbarXRail,"borderRightWidth")),u.css(r.scrollbarXRail,"display","block"),r.railXMarginWidth=s.toInt(u.css(r.scrollbarXRail,"marginLeft"))+s.toInt(u.css(r.scrollbarXRail,"marginRight")),u.css(r.scrollbarXRail,"display",""),r.railXWidth=null,r.railXRatio=null,r.scrollbarYRail=u.appendTo(u.e("div","ps-scrollbar-y-rail"),t),r.scrollbarY=u.appendTo(u.e("div","ps-scrollbar-y"),r.scrollbarYRail),r.scrollbarY.setAttribute("tabindex",0),r.event.bind(r.scrollbarY,"focus",e),r.event.bind(r.scrollbarY,"blur",n),r.scrollbarYActive=null,r.scrollbarYHeight=null,r.scrollbarYTop=null,r.scrollbarYRight=s.toInt(u.css(r.scrollbarYRail,"right")),r.isScrollbarYUsingRight=r.scrollbarYRight===r.scrollbarYRight,r.scrollbarYLeft=r.isScrollbarYUsingRight?null:s.toInt(u.css(r.scrollbarYRail,"left")),r.scrollbarYOuterWidth=r.isRtl?s.outerWidth(r.scrollbarY):null,r.railBorderYWidth=s.toInt(u.css(r.scrollbarYRail,"borderTopWidth"))+s.toInt(u.css(r.scrollbarYRail,"borderBottomWidth")),u.css(r.scrollbarYRail,"display","block"),r.railYMarginHeight=s.toInt(u.css(r.scrollbarYRail,"marginTop"))+s.toInt(u.css(r.scrollbarYRail,"marginBottom")),u.css(r.scrollbarYRail,"display",""),r.railYHeight=null,r.railYRatio=null}function o(t){return t.getAttribute("data-ps-id")}function l(t,e){t.setAttribute("data-ps-id",e)}function i(t){t.removeAttribute("data-ps-id")}var s=t("../lib/helper"),a=t("../lib/class"),c=t("./default-setting"),u=t("../lib/dom"),d=t("../lib/event-manager"),p=t("../lib/guid"),f={};n.add=function(t){var e=p();return l(t,e),f[e]=new r(t),f[e]},n.remove=function(t){delete f[o(t)],i(t)},n.get=function(t){return f[o(t)]}},{"../lib/class":2,"../lib/dom":3,"../lib/event-manager":4,"../lib/guid":5,"../lib/helper":6,"./default-setting":8}],19:[function(t,e,n){"use strict";function r(t,e){return t.settings.minScrollbarLength&&(e=Math.max(e,t.settings.minScrollbarLength)),t.settings.maxScrollbarLength&&(e=Math.min(e,t.settings.maxScrollbarLength)),e}function o(t,e){var n={width:e.railXWidth};e.isRtl?n.left=e.negativeScrollAdjustment+t.scrollLeft+e.containerWidth-e.contentWidth:n.left=t.scrollLeft,e.isScrollbarXUsingBottom?n.bottom=e.scrollbarXBottom-t.scrollTop:n.top=e.scrollbarXTop+t.scrollTop,s.css(e.scrollbarXRail,n);var r={top:t.scrollTop,height:e.railYHeight};e.isScrollbarYUsingRight?e.isRtl?r.right=e.contentWidth-(e.negativeScrollAdjustment+t.scrollLeft)-e.scrollbarYRight-e.scrollbarYOuterWidth:r.right=e.scrollbarYRight-t.scrollLeft:e.isRtl?r.left=e.negativeScrollAdjustment+t.scrollLeft+2*e.containerWidth-e.contentWidth-e.scrollbarYLeft-e.scrollbarYOuterWidth:r.left=e.scrollbarYLeft+t.scrollLeft,s.css(e.scrollbarYRail,r),s.css(e.scrollbarX,{left:e.scrollbarXLeft,width:e.scrollbarXWidth-e.railBorderXWidth}),s.css(e.scrollbarY,{top:e.scrollbarYTop,height:e.scrollbarYHeight-e.railBorderYWidth})}var l=t("../lib/helper"),i=t("../lib/class"),s=t("../lib/dom"),a=t("./instances"),c=t("./update-scroll");e.exports=function(t){var e=a.get(t);e.containerWidth=t.clientWidth,e.containerHeight=t.clientHeight,e.contentWidth=t.scrollWidth,e.contentHeight=t.scrollHeight;var n;t.contains(e.scrollbarXRail)||(n=s.queryChildren(t,".ps-scrollbar-x-rail"),n.length>0&&n.forEach(function(t){s.remove(t)}),s.appendTo(e.scrollbarXRail,t)),t.contains(e.scrollbarYRail)||(n=s.queryChildren(t,".ps-scrollbar-y-rail"),n.length>0&&n.forEach(function(t){s.remove(t)}),s.appendTo(e.scrollbarYRail,t)),!e.settings.suppressScrollX&&e.containerWidth+e.settings.scrollXMarginOffset=e.railXWidth-e.scrollbarXWidth&&(e.scrollbarXLeft=e.railXWidth-e.scrollbarXWidth),e.scrollbarYTop>=e.railYHeight-e.scrollbarYHeight&&(e.scrollbarYTop=e.railYHeight-e.scrollbarYHeight),o(t,e),e.scrollbarXActive?i.add(t,"ps-active-x"):(i.remove(t,"ps-active-x"),e.scrollbarXWidth=0,e.scrollbarXLeft=0,c(t,"left",0)),e.scrollbarYActive?i.add(t,"ps-active-y"):(i.remove(t,"ps-active-y"),e.scrollbarYHeight=0,e.scrollbarYTop=0,c(t,"top",0))}},{"../lib/class":2,"../lib/dom":3,"../lib/helper":6,"./instances":18,"./update-scroll":20}],20:[function(t,e,n){"use strict";var r,o,l=t("./instances"),i=function(t){var e=document.createEvent("Event");return e.initEvent(t,!0,!0),e};e.exports=function(t,e,n){if("undefined"==typeof t)throw"You must provide an element to the update-scroll function";if("undefined"==typeof e)throw"You must provide an axis to the update-scroll function";if("undefined"==typeof n)throw"You must provide a value to the update-scroll function";"top"===e&&n<=0&&(t.scrollTop=n=0,t.dispatchEvent(i("ps-y-reach-start"))),"left"===e&&n<=0&&(t.scrollLeft=n=0,t.dispatchEvent(i("ps-x-reach-start")));var s=l.get(t);"top"===e&&n>=s.contentHeight-s.containerHeight&&(n=s.contentHeight-s.containerHeight,n-t.scrollTop<=1?n=t.scrollTop:t.scrollTop=n,t.dispatchEvent(i("ps-y-reach-end"))),"left"===e&&n>=s.contentWidth-s.containerWidth&&(n=s.contentWidth-s.containerWidth,n-t.scrollLeft<=1?n=t.scrollLeft:t.scrollLeft=n,t.dispatchEvent(i("ps-x-reach-end"))),r||(r=t.scrollTop),o||(o=t.scrollLeft),"top"===e&&nr&&t.dispatchEvent(i("ps-scroll-down")),"left"===e&&no&&t.dispatchEvent(i("ps-scroll-right")),"top"===e&&(t.scrollTop=r=n,t.dispatchEvent(i("ps-scroll-y"))),"left"===e&&(t.scrollLeft=o=n,t.dispatchEvent(i("ps-scroll-x")))}},{"./instances":18}],21:[function(t,e,n){"use strict";var r=t("../lib/helper"),o=t("../lib/dom"),l=t("./instances"),i=t("./update-geometry"),s=t("./update-scroll");e.exports=function(t){var e=l.get(t);e&&(e.negativeScrollAdjustment=e.isNegativeScroll?t.scrollWidth-t.clientWidth:0,o.css(e.scrollbarXRail,"display","block"),o.css(e.scrollbarYRail,"display","block"),e.railXMarginWidth=r.toInt(o.css(e.scrollbarXRail,"marginLeft"))+r.toInt(o.css(e.scrollbarXRail,"marginRight")),e.railYMarginHeight=r.toInt(o.css(e.scrollbarYRail,"marginTop"))+r.toInt(o.css(e.scrollbarYRail,"marginBottom")),o.css(e.scrollbarXRail,"display","none"),o.css(e.scrollbarYRail,"display","none"),i(t),s(t,"top",t.scrollTop),s(t,"left",t.scrollLeft),o.css(e.scrollbarXRail,"display",""),o.css(e.scrollbarYRail,"display",""))}},{"../lib/dom":3,"../lib/helper":6,"./instances":18,"./update-geometry":19,"./update-scroll":20}]},{},[1]); \ No newline at end of file diff --git a/library/perfect-scrollbar/js/perfect-scrollbar.js b/library/perfect-scrollbar/js/perfect-scrollbar.js new file mode 100644 index 000000000..ad0e71f3d --- /dev/null +++ b/library/perfect-scrollbar/js/perfect-scrollbar.js @@ -0,0 +1,1550 @@ +/* perfect-scrollbar v0.6.16 */ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 0) { + classes.splice(idx, 1); + } + element.className = classes.join(' '); +} + +exports.add = function (element, className) { + if (element.classList) { + element.classList.add(className); + } else { + oldAdd(element, className); + } +}; + +exports.remove = function (element, className) { + if (element.classList) { + element.classList.remove(className); + } else { + oldRemove(element, className); + } +}; + +exports.list = function (element) { + if (element.classList) { + return Array.prototype.slice.apply(element.classList); + } else { + return element.className.split(' '); + } +}; + +},{}],3:[function(require,module,exports){ +'use strict'; + +var DOM = {}; + +DOM.e = function (tagName, className) { + var element = document.createElement(tagName); + element.className = className; + return element; +}; + +DOM.appendTo = function (child, parent) { + parent.appendChild(child); + return child; +}; + +function cssGet(element, styleName) { + return window.getComputedStyle(element)[styleName]; +} + +function cssSet(element, styleName, styleValue) { + if (typeof styleValue === 'number') { + styleValue = styleValue.toString() + 'px'; + } + element.style[styleName] = styleValue; + return element; +} + +function cssMultiSet(element, obj) { + for (var key in obj) { + var val = obj[key]; + if (typeof val === 'number') { + val = val.toString() + 'px'; + } + element.style[key] = val; + } + return element; +} + +DOM.css = function (element, styleNameOrObject, styleValue) { + if (typeof styleNameOrObject === 'object') { + // multiple set with object + return cssMultiSet(element, styleNameOrObject); + } else { + if (typeof styleValue === 'undefined') { + return cssGet(element, styleNameOrObject); + } else { + return cssSet(element, styleNameOrObject, styleValue); + } + } +}; + +DOM.matches = function (element, query) { + if (typeof element.matches !== 'undefined') { + return element.matches(query); + } else { + if (typeof element.matchesSelector !== 'undefined') { + return element.matchesSelector(query); + } else if (typeof element.webkitMatchesSelector !== 'undefined') { + return element.webkitMatchesSelector(query); + } else if (typeof element.mozMatchesSelector !== 'undefined') { + return element.mozMatchesSelector(query); + } else if (typeof element.msMatchesSelector !== 'undefined') { + return element.msMatchesSelector(query); + } + } +}; + +DOM.remove = function (element) { + if (typeof element.remove !== 'undefined') { + element.remove(); + } else { + if (element.parentNode) { + element.parentNode.removeChild(element); + } + } +}; + +DOM.queryChildren = function (element, selector) { + return Array.prototype.filter.call(element.childNodes, function (child) { + return DOM.matches(child, selector); + }); +}; + +module.exports = DOM; + +},{}],4:[function(require,module,exports){ +'use strict'; + +var EventElement = function (element) { + this.element = element; + this.events = {}; +}; + +EventElement.prototype.bind = function (eventName, handler) { + if (typeof this.events[eventName] === 'undefined') { + this.events[eventName] = []; + } + this.events[eventName].push(handler); + this.element.addEventListener(eventName, handler, false); +}; + +EventElement.prototype.unbind = function (eventName, handler) { + var isHandlerProvided = (typeof handler !== 'undefined'); + this.events[eventName] = this.events[eventName].filter(function (hdlr) { + if (isHandlerProvided && hdlr !== handler) { + return true; + } + this.element.removeEventListener(eventName, hdlr, false); + return false; + }, this); +}; + +EventElement.prototype.unbindAll = function () { + for (var name in this.events) { + this.unbind(name); + } +}; + +var EventManager = function () { + this.eventElements = []; +}; + +EventManager.prototype.eventElement = function (element) { + var ee = this.eventElements.filter(function (eventElement) { + return eventElement.element === element; + })[0]; + if (typeof ee === 'undefined') { + ee = new EventElement(element); + this.eventElements.push(ee); + } + return ee; +}; + +EventManager.prototype.bind = function (element, eventName, handler) { + this.eventElement(element).bind(eventName, handler); +}; + +EventManager.prototype.unbind = function (element, eventName, handler) { + this.eventElement(element).unbind(eventName, handler); +}; + +EventManager.prototype.unbindAll = function () { + for (var i = 0; i < this.eventElements.length; i++) { + this.eventElements[i].unbindAll(); + } +}; + +EventManager.prototype.once = function (element, eventName, handler) { + var ee = this.eventElement(element); + var onceHandler = function (e) { + ee.unbind(eventName, onceHandler); + handler(e); + }; + ee.bind(eventName, onceHandler); +}; + +module.exports = EventManager; + +},{}],5:[function(require,module,exports){ +'use strict'; + +module.exports = (function () { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return function () { + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + + s4() + '-' + s4() + s4() + s4(); + }; +})(); + +},{}],6:[function(require,module,exports){ +'use strict'; + +var cls = require('./class'); +var dom = require('./dom'); + +var toInt = exports.toInt = function (x) { + return parseInt(x, 10) || 0; +}; + +var clone = exports.clone = function (obj) { + if (!obj) { + return null; + } else if (obj.constructor === Array) { + return obj.map(clone); + } else if (typeof obj === 'object') { + var result = {}; + for (var key in obj) { + result[key] = clone(obj[key]); + } + return result; + } else { + return obj; + } +}; + +exports.extend = function (original, source) { + var result = clone(original); + for (var key in source) { + result[key] = clone(source[key]); + } + return result; +}; + +exports.isEditable = function (el) { + return dom.matches(el, "input,[contenteditable]") || + dom.matches(el, "select,[contenteditable]") || + dom.matches(el, "textarea,[contenteditable]") || + dom.matches(el, "button,[contenteditable]"); +}; + +exports.removePsClasses = function (element) { + var clsList = cls.list(element); + for (var i = 0; i < clsList.length; i++) { + var className = clsList[i]; + if (className.indexOf('ps-') === 0) { + cls.remove(element, className); + } + } +}; + +exports.outerWidth = function (element) { + return toInt(dom.css(element, 'width')) + + toInt(dom.css(element, 'paddingLeft')) + + toInt(dom.css(element, 'paddingRight')) + + toInt(dom.css(element, 'borderLeftWidth')) + + toInt(dom.css(element, 'borderRightWidth')); +}; + +exports.startScrolling = function (element, axis) { + cls.add(element, 'ps-in-scrolling'); + if (typeof axis !== 'undefined') { + cls.add(element, 'ps-' + axis); + } else { + cls.add(element, 'ps-x'); + cls.add(element, 'ps-y'); + } +}; + +exports.stopScrolling = function (element, axis) { + cls.remove(element, 'ps-in-scrolling'); + if (typeof axis !== 'undefined') { + cls.remove(element, 'ps-' + axis); + } else { + cls.remove(element, 'ps-x'); + cls.remove(element, 'ps-y'); + } +}; + +exports.env = { + isWebKit: 'WebkitAppearance' in document.documentElement.style, + supportsTouch: (('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch), + supportsIePointer: window.navigator.msMaxTouchPoints !== null +}; + +},{"./class":2,"./dom":3}],7:[function(require,module,exports){ +'use strict'; + +var destroy = require('./plugin/destroy'); +var initialize = require('./plugin/initialize'); +var update = require('./plugin/update'); + +module.exports = { + initialize: initialize, + update: update, + destroy: destroy +}; + +},{"./plugin/destroy":9,"./plugin/initialize":17,"./plugin/update":21}],8:[function(require,module,exports){ +'use strict'; + +module.exports = { + handlers: ['click-rail', 'drag-scrollbar', 'keyboard', 'wheel', 'touch'], + maxScrollbarLength: null, + minScrollbarLength: null, + scrollXMarginOffset: 0, + scrollYMarginOffset: 0, + suppressScrollX: false, + suppressScrollY: false, + swipePropagation: true, + useBothWheelAxes: false, + wheelPropagation: false, + wheelSpeed: 1, + theme: 'default' +}; + +},{}],9:[function(require,module,exports){ +'use strict'; + +var _ = require('../lib/helper'); +var dom = require('../lib/dom'); +var instances = require('./instances'); + +module.exports = function (element) { + var i = instances.get(element); + + if (!i) { + return; + } + + i.event.unbindAll(); + dom.remove(i.scrollbarX); + dom.remove(i.scrollbarY); + dom.remove(i.scrollbarXRail); + dom.remove(i.scrollbarYRail); + _.removePsClasses(element); + + instances.remove(element); +}; + +},{"../lib/dom":3,"../lib/helper":6,"./instances":18}],10:[function(require,module,exports){ +'use strict'; + +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindClickRailHandler(element, i) { + function pageOffset(el) { + return el.getBoundingClientRect(); + } + var stopPropagation = function (e) { e.stopPropagation(); }; + + i.event.bind(i.scrollbarY, 'click', stopPropagation); + i.event.bind(i.scrollbarYRail, 'click', function (e) { + var positionTop = e.pageY - window.pageYOffset - pageOffset(i.scrollbarYRail).top; + var direction = positionTop > i.scrollbarYTop ? 1 : -1; + + updateScroll(element, 'top', element.scrollTop + direction * i.containerHeight); + updateGeometry(element); + + e.stopPropagation(); + }); + + i.event.bind(i.scrollbarX, 'click', stopPropagation); + i.event.bind(i.scrollbarXRail, 'click', function (e) { + var positionLeft = e.pageX - window.pageXOffset - pageOffset(i.scrollbarXRail).left; + var direction = positionLeft > i.scrollbarXLeft ? 1 : -1; + + updateScroll(element, 'left', element.scrollLeft + direction * i.containerWidth); + updateGeometry(element); + + e.stopPropagation(); + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindClickRailHandler(element, i); +}; + +},{"../instances":18,"../update-geometry":19,"../update-scroll":20}],11:[function(require,module,exports){ +'use strict'; + +var _ = require('../../lib/helper'); +var dom = require('../../lib/dom'); +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindMouseScrollXHandler(element, i) { + var currentLeft = null; + var currentPageX = null; + + function updateScrollLeft(deltaX) { + var newLeft = currentLeft + (deltaX * i.railXRatio); + var maxLeft = Math.max(0, i.scrollbarXRail.getBoundingClientRect().left) + (i.railXRatio * (i.railXWidth - i.scrollbarXWidth)); + + if (newLeft < 0) { + i.scrollbarXLeft = 0; + } else if (newLeft > maxLeft) { + i.scrollbarXLeft = maxLeft; + } else { + i.scrollbarXLeft = newLeft; + } + + var scrollLeft = _.toInt(i.scrollbarXLeft * (i.contentWidth - i.containerWidth) / (i.containerWidth - (i.railXRatio * i.scrollbarXWidth))) - i.negativeScrollAdjustment; + updateScroll(element, 'left', scrollLeft); + } + + var mouseMoveHandler = function (e) { + updateScrollLeft(e.pageX - currentPageX); + updateGeometry(element); + e.stopPropagation(); + e.preventDefault(); + }; + + var mouseUpHandler = function () { + _.stopScrolling(element, 'x'); + i.event.unbind(i.ownerDocument, 'mousemove', mouseMoveHandler); + }; + + i.event.bind(i.scrollbarX, 'mousedown', function (e) { + currentPageX = e.pageX; + currentLeft = _.toInt(dom.css(i.scrollbarX, 'left')) * i.railXRatio; + _.startScrolling(element, 'x'); + + i.event.bind(i.ownerDocument, 'mousemove', mouseMoveHandler); + i.event.once(i.ownerDocument, 'mouseup', mouseUpHandler); + + e.stopPropagation(); + e.preventDefault(); + }); +} + +function bindMouseScrollYHandler(element, i) { + var currentTop = null; + var currentPageY = null; + + function updateScrollTop(deltaY) { + var newTop = currentTop + (deltaY * i.railYRatio); + var maxTop = Math.max(0, i.scrollbarYRail.getBoundingClientRect().top) + (i.railYRatio * (i.railYHeight - i.scrollbarYHeight)); + + if (newTop < 0) { + i.scrollbarYTop = 0; + } else if (newTop > maxTop) { + i.scrollbarYTop = maxTop; + } else { + i.scrollbarYTop = newTop; + } + + var scrollTop = _.toInt(i.scrollbarYTop * (i.contentHeight - i.containerHeight) / (i.containerHeight - (i.railYRatio * i.scrollbarYHeight))); + updateScroll(element, 'top', scrollTop); + } + + var mouseMoveHandler = function (e) { + updateScrollTop(e.pageY - currentPageY); + updateGeometry(element); + e.stopPropagation(); + e.preventDefault(); + }; + + var mouseUpHandler = function () { + _.stopScrolling(element, 'y'); + i.event.unbind(i.ownerDocument, 'mousemove', mouseMoveHandler); + }; + + i.event.bind(i.scrollbarY, 'mousedown', function (e) { + currentPageY = e.pageY; + currentTop = _.toInt(dom.css(i.scrollbarY, 'top')) * i.railYRatio; + _.startScrolling(element, 'y'); + + i.event.bind(i.ownerDocument, 'mousemove', mouseMoveHandler); + i.event.once(i.ownerDocument, 'mouseup', mouseUpHandler); + + e.stopPropagation(); + e.preventDefault(); + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindMouseScrollXHandler(element, i); + bindMouseScrollYHandler(element, i); +}; + +},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],12:[function(require,module,exports){ +'use strict'; + +var _ = require('../../lib/helper'); +var dom = require('../../lib/dom'); +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindKeyboardHandler(element, i) { + var hovered = false; + i.event.bind(element, 'mouseenter', function () { + hovered = true; + }); + i.event.bind(element, 'mouseleave', function () { + hovered = false; + }); + + var shouldPrevent = false; + function shouldPreventDefault(deltaX, deltaY) { + var scrollTop = element.scrollTop; + if (deltaX === 0) { + if (!i.scrollbarYActive) { + return false; + } + if ((scrollTop === 0 && deltaY > 0) || (scrollTop >= i.contentHeight - i.containerHeight && deltaY < 0)) { + return !i.settings.wheelPropagation; + } + } + + var scrollLeft = element.scrollLeft; + if (deltaY === 0) { + if (!i.scrollbarXActive) { + return false; + } + if ((scrollLeft === 0 && deltaX < 0) || (scrollLeft >= i.contentWidth - i.containerWidth && deltaX > 0)) { + return !i.settings.wheelPropagation; + } + } + return true; + } + + i.event.bind(i.ownerDocument, 'keydown', function (e) { + if ((e.isDefaultPrevented && e.isDefaultPrevented()) || e.defaultPrevented) { + return; + } + + var focused = dom.matches(i.scrollbarX, ':focus') || + dom.matches(i.scrollbarY, ':focus'); + + if (!hovered && !focused) { + return; + } + + var activeElement = document.activeElement ? document.activeElement : i.ownerDocument.activeElement; + if (activeElement) { + if (activeElement.tagName === 'IFRAME') { + activeElement = activeElement.contentDocument.activeElement; + } else { + // go deeper if element is a webcomponent + while (activeElement.shadowRoot) { + activeElement = activeElement.shadowRoot.activeElement; + } + } + if (_.isEditable(activeElement)) { + return; + } + } + + var deltaX = 0; + var deltaY = 0; + + switch (e.which) { + case 37: // left + if (e.metaKey) { + deltaX = -i.contentWidth; + } else if (e.altKey) { + deltaX = -i.containerWidth; + } else { + deltaX = -30; + } + break; + case 38: // up + if (e.metaKey) { + deltaY = i.contentHeight; + } else if (e.altKey) { + deltaY = i.containerHeight; + } else { + deltaY = 30; + } + break; + case 39: // right + if (e.metaKey) { + deltaX = i.contentWidth; + } else if (e.altKey) { + deltaX = i.containerWidth; + } else { + deltaX = 30; + } + break; + case 40: // down + if (e.metaKey) { + deltaY = -i.contentHeight; + } else if (e.altKey) { + deltaY = -i.containerHeight; + } else { + deltaY = -30; + } + break; + case 33: // page up + deltaY = 90; + break; + case 32: // space bar + if (e.shiftKey) { + deltaY = 90; + } else { + deltaY = -90; + } + break; + case 34: // page down + deltaY = -90; + break; + case 35: // end + if (e.ctrlKey) { + deltaY = -i.contentHeight; + } else { + deltaY = -i.containerHeight; + } + break; + case 36: // home + if (e.ctrlKey) { + deltaY = element.scrollTop; + } else { + deltaY = i.containerHeight; + } + break; + default: + return; + } + + updateScroll(element, 'top', element.scrollTop - deltaY); + updateScroll(element, 'left', element.scrollLeft + deltaX); + updateGeometry(element); + + shouldPrevent = shouldPreventDefault(deltaX, deltaY); + if (shouldPrevent) { + e.preventDefault(); + } + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindKeyboardHandler(element, i); +}; + +},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],13:[function(require,module,exports){ +'use strict'; + +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindMouseWheelHandler(element, i) { + var shouldPrevent = false; + + function shouldPreventDefault(deltaX, deltaY) { + var scrollTop = element.scrollTop; + if (deltaX === 0) { + if (!i.scrollbarYActive) { + return false; + } + if ((scrollTop === 0 && deltaY > 0) || (scrollTop >= i.contentHeight - i.containerHeight && deltaY < 0)) { + return !i.settings.wheelPropagation; + } + } + + var scrollLeft = element.scrollLeft; + if (deltaY === 0) { + if (!i.scrollbarXActive) { + return false; + } + if ((scrollLeft === 0 && deltaX < 0) || (scrollLeft >= i.contentWidth - i.containerWidth && deltaX > 0)) { + return !i.settings.wheelPropagation; + } + } + return true; + } + + function getDeltaFromEvent(e) { + var deltaX = e.deltaX; + var deltaY = -1 * e.deltaY; + + if (typeof deltaX === "undefined" || typeof deltaY === "undefined") { + // OS X Safari + deltaX = -1 * e.wheelDeltaX / 6; + deltaY = e.wheelDeltaY / 6; + } + + if (e.deltaMode && e.deltaMode === 1) { + // Firefox in deltaMode 1: Line scrolling + deltaX *= 10; + deltaY *= 10; + } + + if (deltaX !== deltaX && deltaY !== deltaY/* NaN checks */) { + // IE in some mouse drivers + deltaX = 0; + deltaY = e.wheelDelta; + } + + if (e.shiftKey) { + // reverse axis with shift key + return [-deltaY, -deltaX]; + } + return [deltaX, deltaY]; + } + + function shouldBeConsumedByChild(deltaX, deltaY) { + var child = element.querySelector('textarea:hover, select[multiple]:hover, .ps-child:hover'); + if (child) { + if (!window.getComputedStyle(child).overflow.match(/(scroll|auto)/)) { + // if not scrollable + return false; + } + + var maxScrollTop = child.scrollHeight - child.clientHeight; + if (maxScrollTop > 0) { + if (!(child.scrollTop === 0 && deltaY > 0) && !(child.scrollTop === maxScrollTop && deltaY < 0)) { + return true; + } + } + var maxScrollLeft = child.scrollLeft - child.clientWidth; + if (maxScrollLeft > 0) { + if (!(child.scrollLeft === 0 && deltaX < 0) && !(child.scrollLeft === maxScrollLeft && deltaX > 0)) { + return true; + } + } + } + return false; + } + + function mousewheelHandler(e) { + var delta = getDeltaFromEvent(e); + + var deltaX = delta[0]; + var deltaY = delta[1]; + + if (shouldBeConsumedByChild(deltaX, deltaY)) { + return; + } + + shouldPrevent = false; + if (!i.settings.useBothWheelAxes) { + // deltaX will only be used for horizontal scrolling and deltaY will + // only be used for vertical scrolling - this is the default + updateScroll(element, 'top', element.scrollTop - (deltaY * i.settings.wheelSpeed)); + updateScroll(element, 'left', element.scrollLeft + (deltaX * i.settings.wheelSpeed)); + } else if (i.scrollbarYActive && !i.scrollbarXActive) { + // only vertical scrollbar is active and useBothWheelAxes option is + // active, so let's scroll vertical bar using both mouse wheel axes + if (deltaY) { + updateScroll(element, 'top', element.scrollTop - (deltaY * i.settings.wheelSpeed)); + } else { + updateScroll(element, 'top', element.scrollTop + (deltaX * i.settings.wheelSpeed)); + } + shouldPrevent = true; + } else if (i.scrollbarXActive && !i.scrollbarYActive) { + // useBothWheelAxes and only horizontal bar is active, so use both + // wheel axes for horizontal bar + if (deltaX) { + updateScroll(element, 'left', element.scrollLeft + (deltaX * i.settings.wheelSpeed)); + } else { + updateScroll(element, 'left', element.scrollLeft - (deltaY * i.settings.wheelSpeed)); + } + shouldPrevent = true; + } + + updateGeometry(element); + + shouldPrevent = (shouldPrevent || shouldPreventDefault(deltaX, deltaY)); + if (shouldPrevent) { + e.stopPropagation(); + e.preventDefault(); + } + } + + if (typeof window.onwheel !== "undefined") { + i.event.bind(element, 'wheel', mousewheelHandler); + } else if (typeof window.onmousewheel !== "undefined") { + i.event.bind(element, 'mousewheel', mousewheelHandler); + } +} + +module.exports = function (element) { + var i = instances.get(element); + bindMouseWheelHandler(element, i); +}; + +},{"../instances":18,"../update-geometry":19,"../update-scroll":20}],14:[function(require,module,exports){ +'use strict'; + +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); + +function bindNativeScrollHandler(element, i) { + i.event.bind(element, 'scroll', function () { + updateGeometry(element); + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindNativeScrollHandler(element, i); +}; + +},{"../instances":18,"../update-geometry":19}],15:[function(require,module,exports){ +'use strict'; + +var _ = require('../../lib/helper'); +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindSelectionHandler(element, i) { + function getRangeNode() { + var selection = window.getSelection ? window.getSelection() : + document.getSelection ? document.getSelection() : ''; + if (selection.toString().length === 0) { + return null; + } else { + return selection.getRangeAt(0).commonAncestorContainer; + } + } + + var scrollingLoop = null; + var scrollDiff = {top: 0, left: 0}; + function startScrolling() { + if (!scrollingLoop) { + scrollingLoop = setInterval(function () { + if (!instances.get(element)) { + clearInterval(scrollingLoop); + return; + } + + updateScroll(element, 'top', element.scrollTop + scrollDiff.top); + updateScroll(element, 'left', element.scrollLeft + scrollDiff.left); + updateGeometry(element); + }, 50); // every .1 sec + } + } + function stopScrolling() { + if (scrollingLoop) { + clearInterval(scrollingLoop); + scrollingLoop = null; + } + _.stopScrolling(element); + } + + var isSelected = false; + i.event.bind(i.ownerDocument, 'selectionchange', function () { + if (element.contains(getRangeNode())) { + isSelected = true; + } else { + isSelected = false; + stopScrolling(); + } + }); + i.event.bind(window, 'mouseup', function () { + if (isSelected) { + isSelected = false; + stopScrolling(); + } + }); + i.event.bind(window, 'keyup', function () { + if (isSelected) { + isSelected = false; + stopScrolling(); + } + }); + + i.event.bind(window, 'mousemove', function (e) { + if (isSelected) { + var mousePosition = {x: e.pageX, y: e.pageY}; + var containerGeometry = { + left: element.offsetLeft, + right: element.offsetLeft + element.offsetWidth, + top: element.offsetTop, + bottom: element.offsetTop + element.offsetHeight + }; + + if (mousePosition.x < containerGeometry.left + 3) { + scrollDiff.left = -5; + _.startScrolling(element, 'x'); + } else if (mousePosition.x > containerGeometry.right - 3) { + scrollDiff.left = 5; + _.startScrolling(element, 'x'); + } else { + scrollDiff.left = 0; + } + + if (mousePosition.y < containerGeometry.top + 3) { + if (containerGeometry.top + 3 - mousePosition.y < 5) { + scrollDiff.top = -5; + } else { + scrollDiff.top = -20; + } + _.startScrolling(element, 'y'); + } else if (mousePosition.y > containerGeometry.bottom - 3) { + if (mousePosition.y - containerGeometry.bottom + 3 < 5) { + scrollDiff.top = 5; + } else { + scrollDiff.top = 20; + } + _.startScrolling(element, 'y'); + } else { + scrollDiff.top = 0; + } + + if (scrollDiff.top === 0 && scrollDiff.left === 0) { + stopScrolling(); + } else { + startScrolling(); + } + } + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindSelectionHandler(element, i); +}; + +},{"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],16:[function(require,module,exports){ +'use strict'; + +var _ = require('../../lib/helper'); +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindTouchHandler(element, i, supportsTouch, supportsIePointer) { + function shouldPreventDefault(deltaX, deltaY) { + var scrollTop = element.scrollTop; + var scrollLeft = element.scrollLeft; + var magnitudeX = Math.abs(deltaX); + var magnitudeY = Math.abs(deltaY); + + if (magnitudeY > magnitudeX) { + // user is perhaps trying to swipe up/down the page + + if (((deltaY < 0) && (scrollTop === i.contentHeight - i.containerHeight)) || + ((deltaY > 0) && (scrollTop === 0))) { + return !i.settings.swipePropagation; + } + } else if (magnitudeX > magnitudeY) { + // user is perhaps trying to swipe left/right across the page + + if (((deltaX < 0) && (scrollLeft === i.contentWidth - i.containerWidth)) || + ((deltaX > 0) && (scrollLeft === 0))) { + return !i.settings.swipePropagation; + } + } + + return true; + } + + function applyTouchMove(differenceX, differenceY) { + updateScroll(element, 'top', element.scrollTop - differenceY); + updateScroll(element, 'left', element.scrollLeft - differenceX); + + updateGeometry(element); + } + + var startOffset = {}; + var startTime = 0; + var speed = {}; + var easingLoop = null; + var inGlobalTouch = false; + var inLocalTouch = false; + + function globalTouchStart() { + inGlobalTouch = true; + } + function globalTouchEnd() { + inGlobalTouch = false; + } + + function getTouch(e) { + if (e.targetTouches) { + return e.targetTouches[0]; + } else { + // Maybe IE pointer + return e; + } + } + function shouldHandle(e) { + if (e.targetTouches && e.targetTouches.length === 1) { + return true; + } + if (e.pointerType && e.pointerType !== 'mouse' && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) { + return true; + } + return false; + } + function touchStart(e) { + if (shouldHandle(e)) { + inLocalTouch = true; + + var touch = getTouch(e); + + startOffset.pageX = touch.pageX; + startOffset.pageY = touch.pageY; + + startTime = (new Date()).getTime(); + + if (easingLoop !== null) { + clearInterval(easingLoop); + } + + e.stopPropagation(); + } + } + function touchMove(e) { + if (!inLocalTouch && i.settings.swipePropagation) { + touchStart(e); + } + if (!inGlobalTouch && inLocalTouch && shouldHandle(e)) { + var touch = getTouch(e); + + var currentOffset = {pageX: touch.pageX, pageY: touch.pageY}; + + var differenceX = currentOffset.pageX - startOffset.pageX; + var differenceY = currentOffset.pageY - startOffset.pageY; + + applyTouchMove(differenceX, differenceY); + startOffset = currentOffset; + + var currentTime = (new Date()).getTime(); + + var timeGap = currentTime - startTime; + if (timeGap > 0) { + speed.x = differenceX / timeGap; + speed.y = differenceY / timeGap; + startTime = currentTime; + } + + if (shouldPreventDefault(differenceX, differenceY)) { + e.stopPropagation(); + e.preventDefault(); + } + } + } + function touchEnd() { + if (!inGlobalTouch && inLocalTouch) { + inLocalTouch = false; + + clearInterval(easingLoop); + easingLoop = setInterval(function () { + if (!instances.get(element)) { + clearInterval(easingLoop); + return; + } + + if (!speed.x && !speed.y) { + clearInterval(easingLoop); + return; + } + + if (Math.abs(speed.x) < 0.01 && Math.abs(speed.y) < 0.01) { + clearInterval(easingLoop); + return; + } + + applyTouchMove(speed.x * 30, speed.y * 30); + + speed.x *= 0.8; + speed.y *= 0.8; + }, 10); + } + } + + if (supportsTouch) { + i.event.bind(window, 'touchstart', globalTouchStart); + i.event.bind(window, 'touchend', globalTouchEnd); + i.event.bind(element, 'touchstart', touchStart); + i.event.bind(element, 'touchmove', touchMove); + i.event.bind(element, 'touchend', touchEnd); + } else if (supportsIePointer) { + if (window.PointerEvent) { + i.event.bind(window, 'pointerdown', globalTouchStart); + i.event.bind(window, 'pointerup', globalTouchEnd); + i.event.bind(element, 'pointerdown', touchStart); + i.event.bind(element, 'pointermove', touchMove); + i.event.bind(element, 'pointerup', touchEnd); + } else if (window.MSPointerEvent) { + i.event.bind(window, 'MSPointerDown', globalTouchStart); + i.event.bind(window, 'MSPointerUp', globalTouchEnd); + i.event.bind(element, 'MSPointerDown', touchStart); + i.event.bind(element, 'MSPointerMove', touchMove); + i.event.bind(element, 'MSPointerUp', touchEnd); + } + } +} + +module.exports = function (element) { + if (!_.env.supportsTouch && !_.env.supportsIePointer) { + return; + } + + var i = instances.get(element); + bindTouchHandler(element, i, _.env.supportsTouch, _.env.supportsIePointer); +}; + +},{"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],17:[function(require,module,exports){ +'use strict'; + +var _ = require('../lib/helper'); +var cls = require('../lib/class'); +var instances = require('./instances'); +var updateGeometry = require('./update-geometry'); + +// Handlers +var handlers = { + 'click-rail': require('./handler/click-rail'), + 'drag-scrollbar': require('./handler/drag-scrollbar'), + 'keyboard': require('./handler/keyboard'), + 'wheel': require('./handler/mouse-wheel'), + 'touch': require('./handler/touch'), + 'selection': require('./handler/selection') +}; +var nativeScrollHandler = require('./handler/native-scroll'); + +module.exports = function (element, userSettings) { + userSettings = typeof userSettings === 'object' ? userSettings : {}; + + cls.add(element, 'ps-container'); + + // Create a plugin instance. + var i = instances.add(element); + + i.settings = _.extend(i.settings, userSettings); + cls.add(element, 'ps-theme-' + i.settings.theme); + + i.settings.handlers.forEach(function (handlerName) { + handlers[handlerName](element); + }); + + nativeScrollHandler(element); + + updateGeometry(element); +}; + +},{"../lib/class":2,"../lib/helper":6,"./handler/click-rail":10,"./handler/drag-scrollbar":11,"./handler/keyboard":12,"./handler/mouse-wheel":13,"./handler/native-scroll":14,"./handler/selection":15,"./handler/touch":16,"./instances":18,"./update-geometry":19}],18:[function(require,module,exports){ +'use strict'; + +var _ = require('../lib/helper'); +var cls = require('../lib/class'); +var defaultSettings = require('./default-setting'); +var dom = require('../lib/dom'); +var EventManager = require('../lib/event-manager'); +var guid = require('../lib/guid'); + +var instances = {}; + +function Instance(element) { + var i = this; + + i.settings = _.clone(defaultSettings); + i.containerWidth = null; + i.containerHeight = null; + i.contentWidth = null; + i.contentHeight = null; + + i.isRtl = dom.css(element, 'direction') === "rtl"; + i.isNegativeScroll = (function () { + var originalScrollLeft = element.scrollLeft; + var result = null; + element.scrollLeft = -1; + result = element.scrollLeft < 0; + element.scrollLeft = originalScrollLeft; + return result; + })(); + i.negativeScrollAdjustment = i.isNegativeScroll ? element.scrollWidth - element.clientWidth : 0; + i.event = new EventManager(); + i.ownerDocument = element.ownerDocument || document; + + function focus() { + cls.add(element, 'ps-focus'); + } + + function blur() { + cls.remove(element, 'ps-focus'); + } + + i.scrollbarXRail = dom.appendTo(dom.e('div', 'ps-scrollbar-x-rail'), element); + i.scrollbarX = dom.appendTo(dom.e('div', 'ps-scrollbar-x'), i.scrollbarXRail); + i.scrollbarX.setAttribute('tabindex', 0); + i.event.bind(i.scrollbarX, 'focus', focus); + i.event.bind(i.scrollbarX, 'blur', blur); + i.scrollbarXActive = null; + i.scrollbarXWidth = null; + i.scrollbarXLeft = null; + i.scrollbarXBottom = _.toInt(dom.css(i.scrollbarXRail, 'bottom')); + i.isScrollbarXUsingBottom = i.scrollbarXBottom === i.scrollbarXBottom; // !isNaN + i.scrollbarXTop = i.isScrollbarXUsingBottom ? null : _.toInt(dom.css(i.scrollbarXRail, 'top')); + i.railBorderXWidth = _.toInt(dom.css(i.scrollbarXRail, 'borderLeftWidth')) + _.toInt(dom.css(i.scrollbarXRail, 'borderRightWidth')); + // Set rail to display:block to calculate margins + dom.css(i.scrollbarXRail, 'display', 'block'); + i.railXMarginWidth = _.toInt(dom.css(i.scrollbarXRail, 'marginLeft')) + _.toInt(dom.css(i.scrollbarXRail, 'marginRight')); + dom.css(i.scrollbarXRail, 'display', ''); + i.railXWidth = null; + i.railXRatio = null; + + i.scrollbarYRail = dom.appendTo(dom.e('div', 'ps-scrollbar-y-rail'), element); + i.scrollbarY = dom.appendTo(dom.e('div', 'ps-scrollbar-y'), i.scrollbarYRail); + i.scrollbarY.setAttribute('tabindex', 0); + i.event.bind(i.scrollbarY, 'focus', focus); + i.event.bind(i.scrollbarY, 'blur', blur); + i.scrollbarYActive = null; + i.scrollbarYHeight = null; + i.scrollbarYTop = null; + i.scrollbarYRight = _.toInt(dom.css(i.scrollbarYRail, 'right')); + i.isScrollbarYUsingRight = i.scrollbarYRight === i.scrollbarYRight; // !isNaN + i.scrollbarYLeft = i.isScrollbarYUsingRight ? null : _.toInt(dom.css(i.scrollbarYRail, 'left')); + i.scrollbarYOuterWidth = i.isRtl ? _.outerWidth(i.scrollbarY) : null; + i.railBorderYWidth = _.toInt(dom.css(i.scrollbarYRail, 'borderTopWidth')) + _.toInt(dom.css(i.scrollbarYRail, 'borderBottomWidth')); + dom.css(i.scrollbarYRail, 'display', 'block'); + i.railYMarginHeight = _.toInt(dom.css(i.scrollbarYRail, 'marginTop')) + _.toInt(dom.css(i.scrollbarYRail, 'marginBottom')); + dom.css(i.scrollbarYRail, 'display', ''); + i.railYHeight = null; + i.railYRatio = null; +} + +function getId(element) { + return element.getAttribute('data-ps-id'); +} + +function setId(element, id) { + element.setAttribute('data-ps-id', id); +} + +function removeId(element) { + element.removeAttribute('data-ps-id'); +} + +exports.add = function (element) { + var newId = guid(); + setId(element, newId); + instances[newId] = new Instance(element); + return instances[newId]; +}; + +exports.remove = function (element) { + delete instances[getId(element)]; + removeId(element); +}; + +exports.get = function (element) { + return instances[getId(element)]; +}; + +},{"../lib/class":2,"../lib/dom":3,"../lib/event-manager":4,"../lib/guid":5,"../lib/helper":6,"./default-setting":8}],19:[function(require,module,exports){ +'use strict'; + +var _ = require('../lib/helper'); +var cls = require('../lib/class'); +var dom = require('../lib/dom'); +var instances = require('./instances'); +var updateScroll = require('./update-scroll'); + +function getThumbSize(i, thumbSize) { + if (i.settings.minScrollbarLength) { + thumbSize = Math.max(thumbSize, i.settings.minScrollbarLength); + } + if (i.settings.maxScrollbarLength) { + thumbSize = Math.min(thumbSize, i.settings.maxScrollbarLength); + } + return thumbSize; +} + +function updateCss(element, i) { + var xRailOffset = {width: i.railXWidth}; + if (i.isRtl) { + xRailOffset.left = i.negativeScrollAdjustment + element.scrollLeft + i.containerWidth - i.contentWidth; + } else { + xRailOffset.left = element.scrollLeft; + } + if (i.isScrollbarXUsingBottom) { + xRailOffset.bottom = i.scrollbarXBottom - element.scrollTop; + } else { + xRailOffset.top = i.scrollbarXTop + element.scrollTop; + } + dom.css(i.scrollbarXRail, xRailOffset); + + var yRailOffset = {top: element.scrollTop, height: i.railYHeight}; + if (i.isScrollbarYUsingRight) { + if (i.isRtl) { + yRailOffset.right = i.contentWidth - (i.negativeScrollAdjustment + element.scrollLeft) - i.scrollbarYRight - i.scrollbarYOuterWidth; + } else { + yRailOffset.right = i.scrollbarYRight - element.scrollLeft; + } + } else { + if (i.isRtl) { + yRailOffset.left = i.negativeScrollAdjustment + element.scrollLeft + i.containerWidth * 2 - i.contentWidth - i.scrollbarYLeft - i.scrollbarYOuterWidth; + } else { + yRailOffset.left = i.scrollbarYLeft + element.scrollLeft; + } + } + dom.css(i.scrollbarYRail, yRailOffset); + + dom.css(i.scrollbarX, {left: i.scrollbarXLeft, width: i.scrollbarXWidth - i.railBorderXWidth}); + dom.css(i.scrollbarY, {top: i.scrollbarYTop, height: i.scrollbarYHeight - i.railBorderYWidth}); +} + +module.exports = function (element) { + var i = instances.get(element); + + i.containerWidth = element.clientWidth; + i.containerHeight = element.clientHeight; + i.contentWidth = element.scrollWidth; + i.contentHeight = element.scrollHeight; + + var existingRails; + if (!element.contains(i.scrollbarXRail)) { + existingRails = dom.queryChildren(element, '.ps-scrollbar-x-rail'); + if (existingRails.length > 0) { + existingRails.forEach(function (rail) { + dom.remove(rail); + }); + } + dom.appendTo(i.scrollbarXRail, element); + } + if (!element.contains(i.scrollbarYRail)) { + existingRails = dom.queryChildren(element, '.ps-scrollbar-y-rail'); + if (existingRails.length > 0) { + existingRails.forEach(function (rail) { + dom.remove(rail); + }); + } + dom.appendTo(i.scrollbarYRail, element); + } + + if (!i.settings.suppressScrollX && i.containerWidth + i.settings.scrollXMarginOffset < i.contentWidth) { + i.scrollbarXActive = true; + i.railXWidth = i.containerWidth - i.railXMarginWidth; + i.railXRatio = i.containerWidth / i.railXWidth; + i.scrollbarXWidth = getThumbSize(i, _.toInt(i.railXWidth * i.containerWidth / i.contentWidth)); + i.scrollbarXLeft = _.toInt((i.negativeScrollAdjustment + element.scrollLeft) * (i.railXWidth - i.scrollbarXWidth) / (i.contentWidth - i.containerWidth)); + } else { + i.scrollbarXActive = false; + } + + if (!i.settings.suppressScrollY && i.containerHeight + i.settings.scrollYMarginOffset < i.contentHeight) { + i.scrollbarYActive = true; + i.railYHeight = i.containerHeight - i.railYMarginHeight; + i.railYRatio = i.containerHeight / i.railYHeight; + i.scrollbarYHeight = getThumbSize(i, _.toInt(i.railYHeight * i.containerHeight / i.contentHeight)); + i.scrollbarYTop = _.toInt(element.scrollTop * (i.railYHeight - i.scrollbarYHeight) / (i.contentHeight - i.containerHeight)); + } else { + i.scrollbarYActive = false; + } + + if (i.scrollbarXLeft >= i.railXWidth - i.scrollbarXWidth) { + i.scrollbarXLeft = i.railXWidth - i.scrollbarXWidth; + } + if (i.scrollbarYTop >= i.railYHeight - i.scrollbarYHeight) { + i.scrollbarYTop = i.railYHeight - i.scrollbarYHeight; + } + + updateCss(element, i); + + if (i.scrollbarXActive) { + cls.add(element, 'ps-active-x'); + } else { + cls.remove(element, 'ps-active-x'); + i.scrollbarXWidth = 0; + i.scrollbarXLeft = 0; + updateScroll(element, 'left', 0); + } + if (i.scrollbarYActive) { + cls.add(element, 'ps-active-y'); + } else { + cls.remove(element, 'ps-active-y'); + i.scrollbarYHeight = 0; + i.scrollbarYTop = 0; + updateScroll(element, 'top', 0); + } +}; + +},{"../lib/class":2,"../lib/dom":3,"../lib/helper":6,"./instances":18,"./update-scroll":20}],20:[function(require,module,exports){ +'use strict'; + +var instances = require('./instances'); + +var lastTop; +var lastLeft; + +var createDOMEvent = function (name) { + var event = document.createEvent("Event"); + event.initEvent(name, true, true); + return event; +}; + +module.exports = function (element, axis, value) { + if (typeof element === 'undefined') { + throw 'You must provide an element to the update-scroll function'; + } + + if (typeof axis === 'undefined') { + throw 'You must provide an axis to the update-scroll function'; + } + + if (typeof value === 'undefined') { + throw 'You must provide a value to the update-scroll function'; + } + + if (axis === 'top' && value <= 0) { + element.scrollTop = value = 0; // don't allow negative scroll + element.dispatchEvent(createDOMEvent('ps-y-reach-start')); + } + + if (axis === 'left' && value <= 0) { + element.scrollLeft = value = 0; // don't allow negative scroll + element.dispatchEvent(createDOMEvent('ps-x-reach-start')); + } + + var i = instances.get(element); + + if (axis === 'top' && value >= i.contentHeight - i.containerHeight) { + // don't allow scroll past container + value = i.contentHeight - i.containerHeight; + if (value - element.scrollTop <= 1) { + // mitigates rounding errors on non-subpixel scroll values + value = element.scrollTop; + } else { + element.scrollTop = value; + } + element.dispatchEvent(createDOMEvent('ps-y-reach-end')); + } + + if (axis === 'left' && value >= i.contentWidth - i.containerWidth) { + // don't allow scroll past container + value = i.contentWidth - i.containerWidth; + if (value - element.scrollLeft <= 1) { + // mitigates rounding errors on non-subpixel scroll values + value = element.scrollLeft; + } else { + element.scrollLeft = value; + } + element.dispatchEvent(createDOMEvent('ps-x-reach-end')); + } + + if (!lastTop) { + lastTop = element.scrollTop; + } + + if (!lastLeft) { + lastLeft = element.scrollLeft; + } + + if (axis === 'top' && value < lastTop) { + element.dispatchEvent(createDOMEvent('ps-scroll-up')); + } + + if (axis === 'top' && value > lastTop) { + element.dispatchEvent(createDOMEvent('ps-scroll-down')); + } + + if (axis === 'left' && value < lastLeft) { + element.dispatchEvent(createDOMEvent('ps-scroll-left')); + } + + if (axis === 'left' && value > lastLeft) { + element.dispatchEvent(createDOMEvent('ps-scroll-right')); + } + + if (axis === 'top') { + element.scrollTop = lastTop = value; + element.dispatchEvent(createDOMEvent('ps-scroll-y')); + } + + if (axis === 'left') { + element.scrollLeft = lastLeft = value; + element.dispatchEvent(createDOMEvent('ps-scroll-x')); + } + +}; + +},{"./instances":18}],21:[function(require,module,exports){ +'use strict'; + +var _ = require('../lib/helper'); +var dom = require('../lib/dom'); +var instances = require('./instances'); +var updateGeometry = require('./update-geometry'); +var updateScroll = require('./update-scroll'); + +module.exports = function (element) { + var i = instances.get(element); + + if (!i) { + return; + } + + // Recalcuate negative scrollLeft adjustment + i.negativeScrollAdjustment = i.isNegativeScroll ? element.scrollWidth - element.clientWidth : 0; + + // Recalculate rail margins + dom.css(i.scrollbarXRail, 'display', 'block'); + dom.css(i.scrollbarYRail, 'display', 'block'); + i.railXMarginWidth = _.toInt(dom.css(i.scrollbarXRail, 'marginLeft')) + _.toInt(dom.css(i.scrollbarXRail, 'marginRight')); + i.railYMarginHeight = _.toInt(dom.css(i.scrollbarYRail, 'marginTop')) + _.toInt(dom.css(i.scrollbarYRail, 'marginBottom')); + + // Hide scrollbars not to affect scrollWidth and scrollHeight + dom.css(i.scrollbarXRail, 'display', 'none'); + dom.css(i.scrollbarYRail, 'display', 'none'); + + updateGeometry(element); + + // Update top/left scroll to trigger events + updateScroll(element, 'top', element.scrollTop); + updateScroll(element, 'left', element.scrollLeft); + + dom.css(i.scrollbarXRail, 'display', ''); + dom.css(i.scrollbarYRail, 'display', ''); +}; + +},{"../lib/dom":3,"../lib/helper":6,"./instances":18,"./update-geometry":19,"./update-scroll":20}]},{},[1]); diff --git a/library/perfect-scrollbar/js/perfect-scrollbar.min.js b/library/perfect-scrollbar/js/perfect-scrollbar.min.js new file mode 100644 index 000000000..4d76567e8 --- /dev/null +++ b/library/perfect-scrollbar/js/perfect-scrollbar.min.js @@ -0,0 +1,2 @@ +/* perfect-scrollbar v0.6.16 */ +!function t(e,n,r){function o(i,s){if(!n[i]){if(!e[i]){var a="function"==typeof require&&require;if(!s&&a)return a(i,!0);if(l)return l(i,!0);var c=new Error("Cannot find module '"+i+"'");throw c.code="MODULE_NOT_FOUND",c}var u=n[i]={exports:{}};e[i][0].call(u.exports,function(t){var n=e[i][1][t];return o(n?n:t)},u,u.exports,t,e,n,r)}return n[i].exports}for(var l="function"==typeof require&&require,i=0;i=0&&n.splice(r,1),t.className=n.join(" ")}n.add=function(t,e){t.classList?t.classList.add(e):r(t,e)},n.remove=function(t,e){t.classList?t.classList.remove(e):o(t,e)},n.list=function(t){return t.classList?Array.prototype.slice.apply(t.classList):t.className.split(" ")}},{}],3:[function(t,e,n){"use strict";function r(t,e){return window.getComputedStyle(t)[e]}function o(t,e,n){return"number"==typeof n&&(n=n.toString()+"px"),t.style[e]=n,t}function l(t,e){for(var n in e){var r=e[n];"number"==typeof r&&(r=r.toString()+"px"),t.style[n]=r}return t}var i={};i.e=function(t,e){var n=document.createElement(t);return n.className=e,n},i.appendTo=function(t,e){return e.appendChild(t),t},i.css=function(t,e,n){return"object"==typeof e?l(t,e):"undefined"==typeof n?r(t,e):o(t,e,n)},i.matches=function(t,e){return"undefined"!=typeof t.matches?t.matches(e):"undefined"!=typeof t.matchesSelector?t.matchesSelector(e):"undefined"!=typeof t.webkitMatchesSelector?t.webkitMatchesSelector(e):"undefined"!=typeof t.mozMatchesSelector?t.mozMatchesSelector(e):"undefined"!=typeof t.msMatchesSelector?t.msMatchesSelector(e):void 0},i.remove=function(t){"undefined"!=typeof t.remove?t.remove():t.parentNode&&t.parentNode.removeChild(t)},i.queryChildren=function(t,e){return Array.prototype.filter.call(t.childNodes,function(t){return i.matches(t,e)})},e.exports=i},{}],4:[function(t,e,n){"use strict";var r=function(t){this.element=t,this.events={}};r.prototype.bind=function(t,e){"undefined"==typeof this.events[t]&&(this.events[t]=[]),this.events[t].push(e),this.element.addEventListener(t,e,!1)},r.prototype.unbind=function(t,e){var n="undefined"!=typeof e;this.events[t]=this.events[t].filter(function(r){return!(!n||r===e)||(this.element.removeEventListener(t,r,!1),!1)},this)},r.prototype.unbindAll=function(){for(var t in this.events)this.unbind(t)};var o=function(){this.eventElements=[]};o.prototype.eventElement=function(t){var e=this.eventElements.filter(function(e){return e.element===t})[0];return"undefined"==typeof e&&(e=new r(t),this.eventElements.push(e)),e},o.prototype.bind=function(t,e,n){this.eventElement(t).bind(e,n)},o.prototype.unbind=function(t,e,n){this.eventElement(t).unbind(e,n)},o.prototype.unbindAll=function(){for(var t=0;te.scrollbarYTop?1:-1;i(t,"top",t.scrollTop+s*e.containerHeight),l(t),r.stopPropagation()}),e.event.bind(e.scrollbarX,"click",r),e.event.bind(e.scrollbarXRail,"click",function(r){var o=r.pageX-window.pageXOffset-n(e.scrollbarXRail).left,s=o>e.scrollbarXLeft?1:-1;i(t,"left",t.scrollLeft+s*e.containerWidth),l(t),r.stopPropagation()})}var o=t("../instances"),l=t("../update-geometry"),i=t("../update-scroll");e.exports=function(t){var e=o.get(t);r(t,e)}},{"../instances":18,"../update-geometry":19,"../update-scroll":20}],11:[function(t,e,n){"use strict";function r(t,e){function n(n){var o=r+n*e.railXRatio,i=Math.max(0,e.scrollbarXRail.getBoundingClientRect().left)+e.railXRatio*(e.railXWidth-e.scrollbarXWidth);o<0?e.scrollbarXLeft=0:o>i?e.scrollbarXLeft=i:e.scrollbarXLeft=o;var s=l.toInt(e.scrollbarXLeft*(e.contentWidth-e.containerWidth)/(e.containerWidth-e.railXRatio*e.scrollbarXWidth))-e.negativeScrollAdjustment;c(t,"left",s)}var r=null,o=null,s=function(e){n(e.pageX-o),a(t),e.stopPropagation(),e.preventDefault()},u=function(){l.stopScrolling(t,"x"),e.event.unbind(e.ownerDocument,"mousemove",s)};e.event.bind(e.scrollbarX,"mousedown",function(n){o=n.pageX,r=l.toInt(i.css(e.scrollbarX,"left"))*e.railXRatio,l.startScrolling(t,"x"),e.event.bind(e.ownerDocument,"mousemove",s),e.event.once(e.ownerDocument,"mouseup",u),n.stopPropagation(),n.preventDefault()})}function o(t,e){function n(n){var o=r+n*e.railYRatio,i=Math.max(0,e.scrollbarYRail.getBoundingClientRect().top)+e.railYRatio*(e.railYHeight-e.scrollbarYHeight);o<0?e.scrollbarYTop=0:o>i?e.scrollbarYTop=i:e.scrollbarYTop=o;var s=l.toInt(e.scrollbarYTop*(e.contentHeight-e.containerHeight)/(e.containerHeight-e.railYRatio*e.scrollbarYHeight));c(t,"top",s)}var r=null,o=null,s=function(e){n(e.pageY-o),a(t),e.stopPropagation(),e.preventDefault()},u=function(){l.stopScrolling(t,"y"),e.event.unbind(e.ownerDocument,"mousemove",s)};e.event.bind(e.scrollbarY,"mousedown",function(n){o=n.pageY,r=l.toInt(i.css(e.scrollbarY,"top"))*e.railYRatio,l.startScrolling(t,"y"),e.event.bind(e.ownerDocument,"mousemove",s),e.event.once(e.ownerDocument,"mouseup",u),n.stopPropagation(),n.preventDefault()})}var l=t("../../lib/helper"),i=t("../../lib/dom"),s=t("../instances"),a=t("../update-geometry"),c=t("../update-scroll");e.exports=function(t){var e=s.get(t);r(t,e),o(t,e)}},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],12:[function(t,e,n){"use strict";function r(t,e){function n(n,r){var o=t.scrollTop;if(0===n){if(!e.scrollbarYActive)return!1;if(0===o&&r>0||o>=e.contentHeight-e.containerHeight&&r<0)return!e.settings.wheelPropagation}var l=t.scrollLeft;if(0===r){if(!e.scrollbarXActive)return!1;if(0===l&&n<0||l>=e.contentWidth-e.containerWidth&&n>0)return!e.settings.wheelPropagation}return!0}var r=!1;e.event.bind(t,"mouseenter",function(){r=!0}),e.event.bind(t,"mouseleave",function(){r=!1});var i=!1;e.event.bind(e.ownerDocument,"keydown",function(c){if(!(c.isDefaultPrevented&&c.isDefaultPrevented()||c.defaultPrevented)){var u=l.matches(e.scrollbarX,":focus")||l.matches(e.scrollbarY,":focus");if(r||u){var d=document.activeElement?document.activeElement:e.ownerDocument.activeElement;if(d){if("IFRAME"===d.tagName)d=d.contentDocument.activeElement;else for(;d.shadowRoot;)d=d.shadowRoot.activeElement;if(o.isEditable(d))return}var p=0,f=0;switch(c.which){case 37:p=c.metaKey?-e.contentWidth:c.altKey?-e.containerWidth:-30;break;case 38:f=c.metaKey?e.contentHeight:c.altKey?e.containerHeight:30;break;case 39:p=c.metaKey?e.contentWidth:c.altKey?e.containerWidth:30;break;case 40:f=c.metaKey?-e.contentHeight:c.altKey?-e.containerHeight:-30;break;case 33:f=90;break;case 32:f=c.shiftKey?90:-90;break;case 34:f=-90;break;case 35:f=c.ctrlKey?-e.contentHeight:-e.containerHeight;break;case 36:f=c.ctrlKey?t.scrollTop:e.containerHeight;break;default:return}a(t,"top",t.scrollTop-f),a(t,"left",t.scrollLeft+p),s(t),i=n(p,f),i&&c.preventDefault()}}})}var o=t("../../lib/helper"),l=t("../../lib/dom"),i=t("../instances"),s=t("../update-geometry"),a=t("../update-scroll");e.exports=function(t){var e=i.get(t);r(t,e)}},{"../../lib/dom":3,"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],13:[function(t,e,n){"use strict";function r(t,e){function n(n,r){var o=t.scrollTop;if(0===n){if(!e.scrollbarYActive)return!1;if(0===o&&r>0||o>=e.contentHeight-e.containerHeight&&r<0)return!e.settings.wheelPropagation}var l=t.scrollLeft;if(0===r){if(!e.scrollbarXActive)return!1;if(0===l&&n<0||l>=e.contentWidth-e.containerWidth&&n>0)return!e.settings.wheelPropagation}return!0}function r(t){var e=t.deltaX,n=-1*t.deltaY;return"undefined"!=typeof e&&"undefined"!=typeof n||(e=-1*t.wheelDeltaX/6,n=t.wheelDeltaY/6),t.deltaMode&&1===t.deltaMode&&(e*=10,n*=10),e!==e&&n!==n&&(e=0,n=t.wheelDelta),t.shiftKey?[-n,-e]:[e,n]}function o(e,n){var r=t.querySelector("textarea:hover, select[multiple]:hover, .ps-child:hover");if(r){if(!window.getComputedStyle(r).overflow.match(/(scroll|auto)/))return!1;var o=r.scrollHeight-r.clientHeight;if(o>0&&!(0===r.scrollTop&&n>0||r.scrollTop===o&&n<0))return!0;var l=r.scrollLeft-r.clientWidth;if(l>0&&!(0===r.scrollLeft&&e<0||r.scrollLeft===l&&e>0))return!0}return!1}function s(s){var c=r(s),u=c[0],d=c[1];o(u,d)||(a=!1,e.settings.useBothWheelAxes?e.scrollbarYActive&&!e.scrollbarXActive?(d?i(t,"top",t.scrollTop-d*e.settings.wheelSpeed):i(t,"top",t.scrollTop+u*e.settings.wheelSpeed),a=!0):e.scrollbarXActive&&!e.scrollbarYActive&&(u?i(t,"left",t.scrollLeft+u*e.settings.wheelSpeed):i(t,"left",t.scrollLeft-d*e.settings.wheelSpeed),a=!0):(i(t,"top",t.scrollTop-d*e.settings.wheelSpeed),i(t,"left",t.scrollLeft+u*e.settings.wheelSpeed)),l(t),a=a||n(u,d),a&&(s.stopPropagation(),s.preventDefault()))}var a=!1;"undefined"!=typeof window.onwheel?e.event.bind(t,"wheel",s):"undefined"!=typeof window.onmousewheel&&e.event.bind(t,"mousewheel",s)}var o=t("../instances"),l=t("../update-geometry"),i=t("../update-scroll");e.exports=function(t){var e=o.get(t);r(t,e)}},{"../instances":18,"../update-geometry":19,"../update-scroll":20}],14:[function(t,e,n){"use strict";function r(t,e){e.event.bind(t,"scroll",function(){l(t)})}var o=t("../instances"),l=t("../update-geometry");e.exports=function(t){var e=o.get(t);r(t,e)}},{"../instances":18,"../update-geometry":19}],15:[function(t,e,n){"use strict";function r(t,e){function n(){var t=window.getSelection?window.getSelection():document.getSelection?document.getSelection():"";return 0===t.toString().length?null:t.getRangeAt(0).commonAncestorContainer}function r(){c||(c=setInterval(function(){return l.get(t)?(s(t,"top",t.scrollTop+u.top),s(t,"left",t.scrollLeft+u.left),void i(t)):void clearInterval(c)},50))}function a(){c&&(clearInterval(c),c=null),o.stopScrolling(t)}var c=null,u={top:0,left:0},d=!1;e.event.bind(e.ownerDocument,"selectionchange",function(){t.contains(n())?d=!0:(d=!1,a())}),e.event.bind(window,"mouseup",function(){d&&(d=!1,a())}),e.event.bind(window,"keyup",function(){d&&(d=!1,a())}),e.event.bind(window,"mousemove",function(e){if(d){var n={x:e.pageX,y:e.pageY},l={left:t.offsetLeft,right:t.offsetLeft+t.offsetWidth,top:t.offsetTop,bottom:t.offsetTop+t.offsetHeight};n.xl.right-3?(u.left=5,o.startScrolling(t,"x")):u.left=0,n.yl.bottom-3?(n.y-l.bottom+3<5?u.top=5:u.top=20,o.startScrolling(t,"y")):u.top=0,0===u.top&&0===u.left?a():r()}})}var o=t("../../lib/helper"),l=t("../instances"),i=t("../update-geometry"),s=t("../update-scroll");e.exports=function(t){var e=l.get(t);r(t,e)}},{"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],16:[function(t,e,n){"use strict";function r(t,e,n,r){function o(n,r){var o=t.scrollTop,l=t.scrollLeft,i=Math.abs(n),s=Math.abs(r);if(s>i){if(r<0&&o===e.contentHeight-e.containerHeight||r>0&&0===o)return!e.settings.swipePropagation}else if(i>s&&(n<0&&l===e.contentWidth-e.containerWidth||n>0&&0===l))return!e.settings.swipePropagation;return!0}function a(e,n){s(t,"top",t.scrollTop-n),s(t,"left",t.scrollLeft-e),i(t)}function c(){w=!0}function u(){w=!1}function d(t){return t.targetTouches?t.targetTouches[0]:t}function p(t){return!(!t.targetTouches||1!==t.targetTouches.length)||!(!t.pointerType||"mouse"===t.pointerType||t.pointerType===t.MSPOINTER_TYPE_MOUSE)}function f(t){if(p(t)){Y=!0;var e=d(t);g.pageX=e.pageX,g.pageY=e.pageY,v=(new Date).getTime(),null!==y&&clearInterval(y),t.stopPropagation()}}function h(t){if(!Y&&e.settings.swipePropagation&&f(t),!w&&Y&&p(t)){var n=d(t),r={pageX:n.pageX,pageY:n.pageY},l=r.pageX-g.pageX,i=r.pageY-g.pageY;a(l,i),g=r;var s=(new Date).getTime(),c=s-v;c>0&&(m.x=l/c,m.y=i/c,v=s),o(l,i)&&(t.stopPropagation(),t.preventDefault())}}function b(){!w&&Y&&(Y=!1,clearInterval(y),y=setInterval(function(){return l.get(t)&&(m.x||m.y)?Math.abs(m.x)<.01&&Math.abs(m.y)<.01?void clearInterval(y):(a(30*m.x,30*m.y),m.x*=.8,void(m.y*=.8)):void clearInterval(y)},10))}var g={},v=0,m={},y=null,w=!1,Y=!1;n?(e.event.bind(window,"touchstart",c),e.event.bind(window,"touchend",u),e.event.bind(t,"touchstart",f),e.event.bind(t,"touchmove",h),e.event.bind(t,"touchend",b)):r&&(window.PointerEvent?(e.event.bind(window,"pointerdown",c),e.event.bind(window,"pointerup",u),e.event.bind(t,"pointerdown",f),e.event.bind(t,"pointermove",h),e.event.bind(t,"pointerup",b)):window.MSPointerEvent&&(e.event.bind(window,"MSPointerDown",c),e.event.bind(window,"MSPointerUp",u),e.event.bind(t,"MSPointerDown",f),e.event.bind(t,"MSPointerMove",h),e.event.bind(t,"MSPointerUp",b)))}var o=t("../../lib/helper"),l=t("../instances"),i=t("../update-geometry"),s=t("../update-scroll");e.exports=function(t){if(o.env.supportsTouch||o.env.supportsIePointer){var e=l.get(t);r(t,e,o.env.supportsTouch,o.env.supportsIePointer)}}},{"../../lib/helper":6,"../instances":18,"../update-geometry":19,"../update-scroll":20}],17:[function(t,e,n){"use strict";var r=t("../lib/helper"),o=t("../lib/class"),l=t("./instances"),i=t("./update-geometry"),s={"click-rail":t("./handler/click-rail"),"drag-scrollbar":t("./handler/drag-scrollbar"),keyboard:t("./handler/keyboard"),wheel:t("./handler/mouse-wheel"),touch:t("./handler/touch"),selection:t("./handler/selection")},a=t("./handler/native-scroll");e.exports=function(t,e){e="object"==typeof e?e:{},o.add(t,"ps-container");var n=l.add(t);n.settings=r.extend(n.settings,e),o.add(t,"ps-theme-"+n.settings.theme),n.settings.handlers.forEach(function(e){s[e](t)}),a(t),i(t)}},{"../lib/class":2,"../lib/helper":6,"./handler/click-rail":10,"./handler/drag-scrollbar":11,"./handler/keyboard":12,"./handler/mouse-wheel":13,"./handler/native-scroll":14,"./handler/selection":15,"./handler/touch":16,"./instances":18,"./update-geometry":19}],18:[function(t,e,n){"use strict";function r(t){function e(){a.add(t,"ps-focus")}function n(){a.remove(t,"ps-focus")}var r=this;r.settings=s.clone(c),r.containerWidth=null,r.containerHeight=null,r.contentWidth=null,r.contentHeight=null,r.isRtl="rtl"===u.css(t,"direction"),r.isNegativeScroll=function(){var e=t.scrollLeft,n=null;return t.scrollLeft=-1,n=t.scrollLeft<0,t.scrollLeft=e,n}(),r.negativeScrollAdjustment=r.isNegativeScroll?t.scrollWidth-t.clientWidth:0,r.event=new d,r.ownerDocument=t.ownerDocument||document,r.scrollbarXRail=u.appendTo(u.e("div","ps-scrollbar-x-rail"),t),r.scrollbarX=u.appendTo(u.e("div","ps-scrollbar-x"),r.scrollbarXRail),r.scrollbarX.setAttribute("tabindex",0),r.event.bind(r.scrollbarX,"focus",e),r.event.bind(r.scrollbarX,"blur",n),r.scrollbarXActive=null,r.scrollbarXWidth=null,r.scrollbarXLeft=null,r.scrollbarXBottom=s.toInt(u.css(r.scrollbarXRail,"bottom")),r.isScrollbarXUsingBottom=r.scrollbarXBottom===r.scrollbarXBottom,r.scrollbarXTop=r.isScrollbarXUsingBottom?null:s.toInt(u.css(r.scrollbarXRail,"top")),r.railBorderXWidth=s.toInt(u.css(r.scrollbarXRail,"borderLeftWidth"))+s.toInt(u.css(r.scrollbarXRail,"borderRightWidth")),u.css(r.scrollbarXRail,"display","block"),r.railXMarginWidth=s.toInt(u.css(r.scrollbarXRail,"marginLeft"))+s.toInt(u.css(r.scrollbarXRail,"marginRight")),u.css(r.scrollbarXRail,"display",""),r.railXWidth=null,r.railXRatio=null,r.scrollbarYRail=u.appendTo(u.e("div","ps-scrollbar-y-rail"),t),r.scrollbarY=u.appendTo(u.e("div","ps-scrollbar-y"),r.scrollbarYRail),r.scrollbarY.setAttribute("tabindex",0),r.event.bind(r.scrollbarY,"focus",e),r.event.bind(r.scrollbarY,"blur",n),r.scrollbarYActive=null,r.scrollbarYHeight=null,r.scrollbarYTop=null,r.scrollbarYRight=s.toInt(u.css(r.scrollbarYRail,"right")),r.isScrollbarYUsingRight=r.scrollbarYRight===r.scrollbarYRight,r.scrollbarYLeft=r.isScrollbarYUsingRight?null:s.toInt(u.css(r.scrollbarYRail,"left")),r.scrollbarYOuterWidth=r.isRtl?s.outerWidth(r.scrollbarY):null,r.railBorderYWidth=s.toInt(u.css(r.scrollbarYRail,"borderTopWidth"))+s.toInt(u.css(r.scrollbarYRail,"borderBottomWidth")),u.css(r.scrollbarYRail,"display","block"),r.railYMarginHeight=s.toInt(u.css(r.scrollbarYRail,"marginTop"))+s.toInt(u.css(r.scrollbarYRail,"marginBottom")),u.css(r.scrollbarYRail,"display",""),r.railYHeight=null,r.railYRatio=null}function o(t){return t.getAttribute("data-ps-id")}function l(t,e){t.setAttribute("data-ps-id",e)}function i(t){t.removeAttribute("data-ps-id")}var s=t("../lib/helper"),a=t("../lib/class"),c=t("./default-setting"),u=t("../lib/dom"),d=t("../lib/event-manager"),p=t("../lib/guid"),f={};n.add=function(t){var e=p();return l(t,e),f[e]=new r(t),f[e]},n.remove=function(t){delete f[o(t)],i(t)},n.get=function(t){return f[o(t)]}},{"../lib/class":2,"../lib/dom":3,"../lib/event-manager":4,"../lib/guid":5,"../lib/helper":6,"./default-setting":8}],19:[function(t,e,n){"use strict";function r(t,e){return t.settings.minScrollbarLength&&(e=Math.max(e,t.settings.minScrollbarLength)),t.settings.maxScrollbarLength&&(e=Math.min(e,t.settings.maxScrollbarLength)),e}function o(t,e){var n={width:e.railXWidth};e.isRtl?n.left=e.negativeScrollAdjustment+t.scrollLeft+e.containerWidth-e.contentWidth:n.left=t.scrollLeft,e.isScrollbarXUsingBottom?n.bottom=e.scrollbarXBottom-t.scrollTop:n.top=e.scrollbarXTop+t.scrollTop,s.css(e.scrollbarXRail,n);var r={top:t.scrollTop,height:e.railYHeight};e.isScrollbarYUsingRight?e.isRtl?r.right=e.contentWidth-(e.negativeScrollAdjustment+t.scrollLeft)-e.scrollbarYRight-e.scrollbarYOuterWidth:r.right=e.scrollbarYRight-t.scrollLeft:e.isRtl?r.left=e.negativeScrollAdjustment+t.scrollLeft+2*e.containerWidth-e.contentWidth-e.scrollbarYLeft-e.scrollbarYOuterWidth:r.left=e.scrollbarYLeft+t.scrollLeft,s.css(e.scrollbarYRail,r),s.css(e.scrollbarX,{left:e.scrollbarXLeft,width:e.scrollbarXWidth-e.railBorderXWidth}),s.css(e.scrollbarY,{top:e.scrollbarYTop,height:e.scrollbarYHeight-e.railBorderYWidth})}var l=t("../lib/helper"),i=t("../lib/class"),s=t("../lib/dom"),a=t("./instances"),c=t("./update-scroll");e.exports=function(t){var e=a.get(t);e.containerWidth=t.clientWidth,e.containerHeight=t.clientHeight,e.contentWidth=t.scrollWidth,e.contentHeight=t.scrollHeight;var n;t.contains(e.scrollbarXRail)||(n=s.queryChildren(t,".ps-scrollbar-x-rail"),n.length>0&&n.forEach(function(t){s.remove(t)}),s.appendTo(e.scrollbarXRail,t)),t.contains(e.scrollbarYRail)||(n=s.queryChildren(t,".ps-scrollbar-y-rail"),n.length>0&&n.forEach(function(t){s.remove(t)}),s.appendTo(e.scrollbarYRail,t)),!e.settings.suppressScrollX&&e.containerWidth+e.settings.scrollXMarginOffset=e.railXWidth-e.scrollbarXWidth&&(e.scrollbarXLeft=e.railXWidth-e.scrollbarXWidth),e.scrollbarYTop>=e.railYHeight-e.scrollbarYHeight&&(e.scrollbarYTop=e.railYHeight-e.scrollbarYHeight),o(t,e),e.scrollbarXActive?i.add(t,"ps-active-x"):(i.remove(t,"ps-active-x"),e.scrollbarXWidth=0,e.scrollbarXLeft=0,c(t,"left",0)),e.scrollbarYActive?i.add(t,"ps-active-y"):(i.remove(t,"ps-active-y"),e.scrollbarYHeight=0,e.scrollbarYTop=0,c(t,"top",0))}},{"../lib/class":2,"../lib/dom":3,"../lib/helper":6,"./instances":18,"./update-scroll":20}],20:[function(t,e,n){"use strict";var r,o,l=t("./instances"),i=function(t){var e=document.createEvent("Event");return e.initEvent(t,!0,!0),e};e.exports=function(t,e,n){if("undefined"==typeof t)throw"You must provide an element to the update-scroll function";if("undefined"==typeof e)throw"You must provide an axis to the update-scroll function";if("undefined"==typeof n)throw"You must provide a value to the update-scroll function";"top"===e&&n<=0&&(t.scrollTop=n=0,t.dispatchEvent(i("ps-y-reach-start"))),"left"===e&&n<=0&&(t.scrollLeft=n=0,t.dispatchEvent(i("ps-x-reach-start")));var s=l.get(t);"top"===e&&n>=s.contentHeight-s.containerHeight&&(n=s.contentHeight-s.containerHeight,n-t.scrollTop<=1?n=t.scrollTop:t.scrollTop=n,t.dispatchEvent(i("ps-y-reach-end"))),"left"===e&&n>=s.contentWidth-s.containerWidth&&(n=s.contentWidth-s.containerWidth,n-t.scrollLeft<=1?n=t.scrollLeft:t.scrollLeft=n,t.dispatchEvent(i("ps-x-reach-end"))),r||(r=t.scrollTop),o||(o=t.scrollLeft),"top"===e&&nr&&t.dispatchEvent(i("ps-scroll-down")),"left"===e&&no&&t.dispatchEvent(i("ps-scroll-right")),"top"===e&&(t.scrollTop=r=n,t.dispatchEvent(i("ps-scroll-y"))),"left"===e&&(t.scrollLeft=o=n,t.dispatchEvent(i("ps-scroll-x")))}},{"./instances":18}],21:[function(t,e,n){"use strict";var r=t("../lib/helper"),o=t("../lib/dom"),l=t("./instances"),i=t("./update-geometry"),s=t("./update-scroll");e.exports=function(t){var e=l.get(t);e&&(e.negativeScrollAdjustment=e.isNegativeScroll?t.scrollWidth-t.clientWidth:0,o.css(e.scrollbarXRail,"display","block"),o.css(e.scrollbarYRail,"display","block"),e.railXMarginWidth=r.toInt(o.css(e.scrollbarXRail,"marginLeft"))+r.toInt(o.css(e.scrollbarXRail,"marginRight")),e.railYMarginHeight=r.toInt(o.css(e.scrollbarYRail,"marginTop"))+r.toInt(o.css(e.scrollbarYRail,"marginBottom")),o.css(e.scrollbarXRail,"display","none"),o.css(e.scrollbarYRail,"display","none"),i(t),s(t,"top",t.scrollTop),s(t,"left",t.scrollLeft),o.css(e.scrollbarXRail,"display",""),o.css(e.scrollbarYRail,"display",""))}},{"../lib/dom":3,"../lib/helper":6,"./instances":18,"./update-geometry":19,"./update-scroll":20}]},{},[1]); \ No newline at end of file diff --git a/library/perfect-scrollbar/src/css/main.scss b/library/perfect-scrollbar/src/css/main.scss new file mode 100644 index 000000000..ae1f655ad --- /dev/null +++ b/library/perfect-scrollbar/src/css/main.scss @@ -0,0 +1,3 @@ +@import 'variables'; +@import 'mixins'; +@import 'themes'; diff --git a/library/perfect-scrollbar/src/css/mixins.scss b/library/perfect-scrollbar/src/css/mixins.scss new file mode 100644 index 000000000..79d3d2c9d --- /dev/null +++ b/library/perfect-scrollbar/src/css/mixins.scss @@ -0,0 +1,128 @@ +@mixin scrollbar-rail-default($theme) { + display: none; + position: absolute; /* please don't change 'position' */ + opacity: map_get($theme, rail-default-opacity); + transition: background-color .2s linear, opacity .2s linear; +} + +@mixin scrollbar-rail-hover($theme) { + background-color: map_get($theme, rail-hover-bg); + opacity: map_get($theme, rail-hover-opacity); +} + +@mixin scrollbar-default($theme) { + position: absolute; /* please don't change 'position' */ + background-color: map_get($theme, bar-container-hover-bg); + border-radius: map_get($theme, border-radius); + transition: background-color .2s linear, height .2s linear, width .2s ease-in-out, + border-radius .2s ease-in-out; +} + +@mixin scrollbar-hover($theme) { + background-color: map_get($theme, bar-hover-bg); +} + +@mixin in-scrolling($theme) { + &.ps-in-scrolling { + &.ps-x > .ps-scrollbar-x-rail { + @include scrollbar-rail-hover($theme); + > .ps-scrollbar-x { + @include scrollbar-hover($theme); + height: map_get($theme, scrollbar-x-hover-height); + } + } + &.ps-y > .ps-scrollbar-y-rail { + @include scrollbar-rail-hover($theme); + > .ps-scrollbar-y { + @include scrollbar-hover($theme); + width: map_get($theme, scrollbar-y-hover-width); + } + } + } +} + +// Layout and theme mixin +@mixin ps-container($theme) { + -ms-touch-action: auto; + touch-action: auto; + overflow: hidden !important; + -ms-overflow-style: none; + + // Edge + @supports (-ms-overflow-style: none) { + overflow: auto !important; + } + // IE10+ + @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { + overflow: auto !important; + } + + &.ps-active-x > .ps-scrollbar-x-rail, + &.ps-active-y > .ps-scrollbar-y-rail { + display: block; + background-color: map_get($theme, bar-bg); + } + + @include in-scrolling($theme); + + > .ps-scrollbar-x-rail { + @include scrollbar-rail-default($theme); + bottom: map_get($theme, scrollbar-x-rail-bottom); /* there must be 'bottom' for ps-scrollbar-x-rail */ + height: map_get($theme, scrollbar-x-rail-height); + + > .ps-scrollbar-x { + @include scrollbar-default($theme); + bottom: map_get($theme, scrollbar-x-bottom); /* there must be 'bottom' for ps-scrollbar-x */ + height: map_get($theme, scrollbar-x-height); + } + &:hover, + &:active { + > .ps-scrollbar-x { + height: map_get($theme, scrollbar-x-hover-height); + } + } + } + + > .ps-scrollbar-y-rail { + @include scrollbar-rail-default($theme); + right: map_get($theme, scrollbar-y-rail-right); /* there must be 'right' for ps-scrollbar-y-rail */ + width: map_get($theme, scrollbar-y-rail-width); + + > .ps-scrollbar-y { + @include scrollbar-default($theme); + right: map_get($theme, scrollbar-y-right); /* there must be 'right' for ps-scrollbar-y */ + width: map_get($theme, scrollbar-y-width); + } + &:hover, + &:active { + > .ps-scrollbar-y { + width: map_get($theme, scrollbar-y-hover-width); + } + } + } + + &:hover { + @include in-scrolling($theme); + + > .ps-scrollbar-x-rail, + > .ps-scrollbar-y-rail { + opacity: map_get($theme, rail-container-hover-opacity); + } + + > .ps-scrollbar-x-rail:hover { + @include scrollbar-rail-hover($theme); + + > .ps-scrollbar-x { + @include scrollbar-hover($theme); + } + } + + > .ps-scrollbar-y-rail:hover { + @include scrollbar-rail-hover($theme); + + > .ps-scrollbar-y { + @include scrollbar-hover($theme); + } + } + } +} diff --git a/library/perfect-scrollbar/src/css/themes.scss b/library/perfect-scrollbar/src/css/themes.scss new file mode 100644 index 000000000..bf7729a7d --- /dev/null +++ b/library/perfect-scrollbar/src/css/themes.scss @@ -0,0 +1,25 @@ +$ps-theme-default: ( + border-radius: $ps-border-radius, + rail-default-opacity: $ps-rail-default-opacity, + rail-container-hover-opacity: $ps-rail-container-hover-opacity, + rail-hover-opacity: $ps-rail-hover-opacity, + bar-bg: $ps-bar-bg, + bar-container-hover-bg: $ps-bar-container-hover-bg, + bar-hover-bg: $ps-bar-hover-bg, + rail-hover-bg: $ps-rail-hover-bg, + scrollbar-x-rail-bottom: $ps-scrollbar-x-rail-bottom, + scrollbar-x-rail-height: $ps-scrollbar-x-rail-height, + scrollbar-x-bottom: $ps-scrollbar-x-bottom, + scrollbar-x-height: $ps-scrollbar-x-height, + scrollbar-x-hover-height: $ps-scrollbar-x-hover-height, + scrollbar-y-rail-right: $ps-scrollbar-y-rail-right, + scrollbar-y-rail-width: $ps-scrollbar-y-rail-width, + scrollbar-y-right: $ps-scrollbar-y-right, + scrollbar-y-width: $ps-scrollbar-y-width, + scrollbar-y-hover-width: $ps-scrollbar-y-hover-width, +); + +// Default theme +.ps-container { + @include ps-container($ps-theme-default); +} diff --git a/library/perfect-scrollbar/src/css/variables.scss b/library/perfect-scrollbar/src/css/variables.scss new file mode 100644 index 000000000..7454fb0b0 --- /dev/null +++ b/library/perfect-scrollbar/src/css/variables.scss @@ -0,0 +1,24 @@ +// Colors +$ps-border-radius: 6px !default; + +$ps-rail-default-opacity: 0 !default; +$ps-rail-container-hover-opacity: 0.6 !default; +$ps-rail-hover-opacity: 0.9 !default; + +$ps-bar-bg: transparent !default; +$ps-bar-container-hover-bg: #aaa !default; +$ps-bar-hover-bg: #999 !default; +$ps-rail-hover-bg: #eee !default; + +// Sizes +$ps-scrollbar-x-rail-bottom: 0px !default; +$ps-scrollbar-x-rail-height: 15px !default; +$ps-scrollbar-x-bottom: 2px !default; +$ps-scrollbar-x-height: 6px !default; +$ps-scrollbar-x-hover-height: 11px !default; + +$ps-scrollbar-y-rail-right: 0 !default; +$ps-scrollbar-y-rail-width: 15px !default; +$ps-scrollbar-y-right: 2px !default; +$ps-scrollbar-y-width: 6px !default; +$ps-scrollbar-y-hover-width: 11px !default; diff --git a/library/perfect-scrollbar/src/js/adaptor/global.js b/library/perfect-scrollbar/src/js/adaptor/global.js new file mode 100644 index 000000000..0438e361e --- /dev/null +++ b/library/perfect-scrollbar/src/js/adaptor/global.js @@ -0,0 +1,14 @@ +'use strict'; + +var ps = require('../main'); + +if (typeof define === 'function' && define.amd) { + // AMD + define(ps); +} else { + // Add to a global object. + window.PerfectScrollbar = ps; + if (typeof window.Ps === 'undefined') { + window.Ps = ps; + } +} diff --git a/library/perfect-scrollbar/src/js/adaptor/jquery.js b/library/perfect-scrollbar/src/js/adaptor/jquery.js new file mode 100644 index 000000000..ef55e093e --- /dev/null +++ b/library/perfect-scrollbar/src/js/adaptor/jquery.js @@ -0,0 +1,41 @@ +'use strict'; + +var ps = require('../main'); +var psInstances = require('../plugin/instances'); + +function mountJQuery(jQuery) { + jQuery.fn.perfectScrollbar = function (settingOrCommand) { + return this.each(function () { + if (typeof settingOrCommand === 'object' || + typeof settingOrCommand === 'undefined') { + // If it's an object or none, initialize. + var settings = settingOrCommand; + + if (!psInstances.get(this)) { + ps.initialize(this, settings); + } + } else { + // Unless, it may be a command. + var command = settingOrCommand; + + if (command === 'update') { + ps.update(this); + } else if (command === 'destroy') { + ps.destroy(this); + } + } + }); + }; +} + +if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['jquery'], mountJQuery); +} else { + var jq = window.jQuery ? window.jQuery : window.$; + if (typeof jq !== 'undefined') { + mountJQuery(jq); + } +} + +module.exports = mountJQuery; diff --git a/library/perfect-scrollbar/src/js/lib/class.js b/library/perfect-scrollbar/src/js/lib/class.js new file mode 100644 index 000000000..951b10bb2 --- /dev/null +++ b/library/perfect-scrollbar/src/js/lib/class.js @@ -0,0 +1,42 @@ +'use strict'; + +function oldAdd(element, className) { + var classes = element.className.split(' '); + if (classes.indexOf(className) < 0) { + classes.push(className); + } + element.className = classes.join(' '); +} + +function oldRemove(element, className) { + var classes = element.className.split(' '); + var idx = classes.indexOf(className); + if (idx >= 0) { + classes.splice(idx, 1); + } + element.className = classes.join(' '); +} + +exports.add = function (element, className) { + if (element.classList) { + element.classList.add(className); + } else { + oldAdd(element, className); + } +}; + +exports.remove = function (element, className) { + if (element.classList) { + element.classList.remove(className); + } else { + oldRemove(element, className); + } +}; + +exports.list = function (element) { + if (element.classList) { + return Array.prototype.slice.apply(element.classList); + } else { + return element.className.split(' '); + } +}; diff --git a/library/perfect-scrollbar/src/js/lib/dom.js b/library/perfect-scrollbar/src/js/lib/dom.js new file mode 100644 index 000000000..b929a17ed --- /dev/null +++ b/library/perfect-scrollbar/src/js/lib/dom.js @@ -0,0 +1,84 @@ +'use strict'; + +var DOM = {}; + +DOM.e = function (tagName, className) { + var element = document.createElement(tagName); + element.className = className; + return element; +}; + +DOM.appendTo = function (child, parent) { + parent.appendChild(child); + return child; +}; + +function cssGet(element, styleName) { + return window.getComputedStyle(element)[styleName]; +} + +function cssSet(element, styleName, styleValue) { + if (typeof styleValue === 'number') { + styleValue = styleValue.toString() + 'px'; + } + element.style[styleName] = styleValue; + return element; +} + +function cssMultiSet(element, obj) { + for (var key in obj) { + var val = obj[key]; + if (typeof val === 'number') { + val = val.toString() + 'px'; + } + element.style[key] = val; + } + return element; +} + +DOM.css = function (element, styleNameOrObject, styleValue) { + if (typeof styleNameOrObject === 'object') { + // multiple set with object + return cssMultiSet(element, styleNameOrObject); + } else { + if (typeof styleValue === 'undefined') { + return cssGet(element, styleNameOrObject); + } else { + return cssSet(element, styleNameOrObject, styleValue); + } + } +}; + +DOM.matches = function (element, query) { + if (typeof element.matches !== 'undefined') { + return element.matches(query); + } else { + if (typeof element.matchesSelector !== 'undefined') { + return element.matchesSelector(query); + } else if (typeof element.webkitMatchesSelector !== 'undefined') { + return element.webkitMatchesSelector(query); + } else if (typeof element.mozMatchesSelector !== 'undefined') { + return element.mozMatchesSelector(query); + } else if (typeof element.msMatchesSelector !== 'undefined') { + return element.msMatchesSelector(query); + } + } +}; + +DOM.remove = function (element) { + if (typeof element.remove !== 'undefined') { + element.remove(); + } else { + if (element.parentNode) { + element.parentNode.removeChild(element); + } + } +}; + +DOM.queryChildren = function (element, selector) { + return Array.prototype.filter.call(element.childNodes, function (child) { + return DOM.matches(child, selector); + }); +}; + +module.exports = DOM; diff --git a/library/perfect-scrollbar/src/js/lib/event-manager.js b/library/perfect-scrollbar/src/js/lib/event-manager.js new file mode 100644 index 000000000..d148ad8fe --- /dev/null +++ b/library/perfect-scrollbar/src/js/lib/event-manager.js @@ -0,0 +1,71 @@ +'use strict'; + +var EventElement = function (element) { + this.element = element; + this.events = {}; +}; + +EventElement.prototype.bind = function (eventName, handler) { + if (typeof this.events[eventName] === 'undefined') { + this.events[eventName] = []; + } + this.events[eventName].push(handler); + this.element.addEventListener(eventName, handler, false); +}; + +EventElement.prototype.unbind = function (eventName, handler) { + var isHandlerProvided = (typeof handler !== 'undefined'); + this.events[eventName] = this.events[eventName].filter(function (hdlr) { + if (isHandlerProvided && hdlr !== handler) { + return true; + } + this.element.removeEventListener(eventName, hdlr, false); + return false; + }, this); +}; + +EventElement.prototype.unbindAll = function () { + for (var name in this.events) { + this.unbind(name); + } +}; + +var EventManager = function () { + this.eventElements = []; +}; + +EventManager.prototype.eventElement = function (element) { + var ee = this.eventElements.filter(function (eventElement) { + return eventElement.element === element; + })[0]; + if (typeof ee === 'undefined') { + ee = new EventElement(element); + this.eventElements.push(ee); + } + return ee; +}; + +EventManager.prototype.bind = function (element, eventName, handler) { + this.eventElement(element).bind(eventName, handler); +}; + +EventManager.prototype.unbind = function (element, eventName, handler) { + this.eventElement(element).unbind(eventName, handler); +}; + +EventManager.prototype.unbindAll = function () { + for (var i = 0; i < this.eventElements.length; i++) { + this.eventElements[i].unbindAll(); + } +}; + +EventManager.prototype.once = function (element, eventName, handler) { + var ee = this.eventElement(element); + var onceHandler = function (e) { + ee.unbind(eventName, onceHandler); + handler(e); + }; + ee.bind(eventName, onceHandler); +}; + +module.exports = EventManager; diff --git a/library/perfect-scrollbar/src/js/lib/guid.js b/library/perfect-scrollbar/src/js/lib/guid.js new file mode 100644 index 000000000..84c7237eb --- /dev/null +++ b/library/perfect-scrollbar/src/js/lib/guid.js @@ -0,0 +1,13 @@ +'use strict'; + +module.exports = (function () { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return function () { + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + + s4() + '-' + s4() + s4() + s4(); + }; +})(); diff --git a/library/perfect-scrollbar/src/js/lib/helper.js b/library/perfect-scrollbar/src/js/lib/helper.js new file mode 100644 index 000000000..a72f2e59f --- /dev/null +++ b/library/perfect-scrollbar/src/js/lib/helper.js @@ -0,0 +1,83 @@ +'use strict'; + +var cls = require('./class'); +var dom = require('./dom'); + +var toInt = exports.toInt = function (x) { + return parseInt(x, 10) || 0; +}; + +var clone = exports.clone = function (obj) { + if (!obj) { + return null; + } else if (obj.constructor === Array) { + return obj.map(clone); + } else if (typeof obj === 'object') { + var result = {}; + for (var key in obj) { + result[key] = clone(obj[key]); + } + return result; + } else { + return obj; + } +}; + +exports.extend = function (original, source) { + var result = clone(original); + for (var key in source) { + result[key] = clone(source[key]); + } + return result; +}; + +exports.isEditable = function (el) { + return dom.matches(el, "input,[contenteditable]") || + dom.matches(el, "select,[contenteditable]") || + dom.matches(el, "textarea,[contenteditable]") || + dom.matches(el, "button,[contenteditable]"); +}; + +exports.removePsClasses = function (element) { + var clsList = cls.list(element); + for (var i = 0; i < clsList.length; i++) { + var className = clsList[i]; + if (className.indexOf('ps-') === 0) { + cls.remove(element, className); + } + } +}; + +exports.outerWidth = function (element) { + return toInt(dom.css(element, 'width')) + + toInt(dom.css(element, 'paddingLeft')) + + toInt(dom.css(element, 'paddingRight')) + + toInt(dom.css(element, 'borderLeftWidth')) + + toInt(dom.css(element, 'borderRightWidth')); +}; + +exports.startScrolling = function (element, axis) { + cls.add(element, 'ps-in-scrolling'); + if (typeof axis !== 'undefined') { + cls.add(element, 'ps-' + axis); + } else { + cls.add(element, 'ps-x'); + cls.add(element, 'ps-y'); + } +}; + +exports.stopScrolling = function (element, axis) { + cls.remove(element, 'ps-in-scrolling'); + if (typeof axis !== 'undefined') { + cls.remove(element, 'ps-' + axis); + } else { + cls.remove(element, 'ps-x'); + cls.remove(element, 'ps-y'); + } +}; + +exports.env = { + isWebKit: 'WebkitAppearance' in document.documentElement.style, + supportsTouch: (('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch), + supportsIePointer: window.navigator.msMaxTouchPoints !== null +}; diff --git a/library/perfect-scrollbar/src/js/main.js b/library/perfect-scrollbar/src/js/main.js new file mode 100644 index 000000000..06b1c2be7 --- /dev/null +++ b/library/perfect-scrollbar/src/js/main.js @@ -0,0 +1,11 @@ +'use strict'; + +var destroy = require('./plugin/destroy'); +var initialize = require('./plugin/initialize'); +var update = require('./plugin/update'); + +module.exports = { + initialize: initialize, + update: update, + destroy: destroy +}; diff --git a/library/perfect-scrollbar/src/js/plugin/default-setting.js b/library/perfect-scrollbar/src/js/plugin/default-setting.js new file mode 100644 index 000000000..b3f2ddd8b --- /dev/null +++ b/library/perfect-scrollbar/src/js/plugin/default-setting.js @@ -0,0 +1,16 @@ +'use strict'; + +module.exports = { + handlers: ['click-rail', 'drag-scrollbar', 'keyboard', 'wheel', 'touch'], + maxScrollbarLength: null, + minScrollbarLength: null, + scrollXMarginOffset: 0, + scrollYMarginOffset: 0, + suppressScrollX: false, + suppressScrollY: false, + swipePropagation: true, + useBothWheelAxes: false, + wheelPropagation: false, + wheelSpeed: 1, + theme: 'default' +}; diff --git a/library/perfect-scrollbar/src/js/plugin/destroy.js b/library/perfect-scrollbar/src/js/plugin/destroy.js new file mode 100644 index 000000000..97a83e0bc --- /dev/null +++ b/library/perfect-scrollbar/src/js/plugin/destroy.js @@ -0,0 +1,22 @@ +'use strict'; + +var _ = require('../lib/helper'); +var dom = require('../lib/dom'); +var instances = require('./instances'); + +module.exports = function (element) { + var i = instances.get(element); + + if (!i) { + return; + } + + i.event.unbindAll(); + dom.remove(i.scrollbarX); + dom.remove(i.scrollbarY); + dom.remove(i.scrollbarXRail); + dom.remove(i.scrollbarYRail); + _.removePsClasses(element); + + instances.remove(element); +}; diff --git a/library/perfect-scrollbar/src/js/plugin/handler/click-rail.js b/library/perfect-scrollbar/src/js/plugin/handler/click-rail.js new file mode 100644 index 000000000..bbd15218e --- /dev/null +++ b/library/perfect-scrollbar/src/js/plugin/handler/click-rail.js @@ -0,0 +1,39 @@ +'use strict'; + +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindClickRailHandler(element, i) { + function pageOffset(el) { + return el.getBoundingClientRect(); + } + var stopPropagation = function (e) { e.stopPropagation(); }; + + i.event.bind(i.scrollbarY, 'click', stopPropagation); + i.event.bind(i.scrollbarYRail, 'click', function (e) { + var positionTop = e.pageY - window.pageYOffset - pageOffset(i.scrollbarYRail).top; + var direction = positionTop > i.scrollbarYTop ? 1 : -1; + + updateScroll(element, 'top', element.scrollTop + direction * i.containerHeight); + updateGeometry(element); + + e.stopPropagation(); + }); + + i.event.bind(i.scrollbarX, 'click', stopPropagation); + i.event.bind(i.scrollbarXRail, 'click', function (e) { + var positionLeft = e.pageX - window.pageXOffset - pageOffset(i.scrollbarXRail).left; + var direction = positionLeft > i.scrollbarXLeft ? 1 : -1; + + updateScroll(element, 'left', element.scrollLeft + direction * i.containerWidth); + updateGeometry(element); + + e.stopPropagation(); + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindClickRailHandler(element, i); +}; diff --git a/library/perfect-scrollbar/src/js/plugin/handler/drag-scrollbar.js b/library/perfect-scrollbar/src/js/plugin/handler/drag-scrollbar.js new file mode 100644 index 000000000..fc99d0083 --- /dev/null +++ b/library/perfect-scrollbar/src/js/plugin/handler/drag-scrollbar.js @@ -0,0 +1,103 @@ +'use strict'; + +var _ = require('../../lib/helper'); +var dom = require('../../lib/dom'); +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindMouseScrollXHandler(element, i) { + var currentLeft = null; + var currentPageX = null; + + function updateScrollLeft(deltaX) { + var newLeft = currentLeft + (deltaX * i.railXRatio); + var maxLeft = Math.max(0, i.scrollbarXRail.getBoundingClientRect().left) + (i.railXRatio * (i.railXWidth - i.scrollbarXWidth)); + + if (newLeft < 0) { + i.scrollbarXLeft = 0; + } else if (newLeft > maxLeft) { + i.scrollbarXLeft = maxLeft; + } else { + i.scrollbarXLeft = newLeft; + } + + var scrollLeft = _.toInt(i.scrollbarXLeft * (i.contentWidth - i.containerWidth) / (i.containerWidth - (i.railXRatio * i.scrollbarXWidth))) - i.negativeScrollAdjustment; + updateScroll(element, 'left', scrollLeft); + } + + var mouseMoveHandler = function (e) { + updateScrollLeft(e.pageX - currentPageX); + updateGeometry(element); + e.stopPropagation(); + e.preventDefault(); + }; + + var mouseUpHandler = function () { + _.stopScrolling(element, 'x'); + i.event.unbind(i.ownerDocument, 'mousemove', mouseMoveHandler); + }; + + i.event.bind(i.scrollbarX, 'mousedown', function (e) { + currentPageX = e.pageX; + currentLeft = _.toInt(dom.css(i.scrollbarX, 'left')) * i.railXRatio; + _.startScrolling(element, 'x'); + + i.event.bind(i.ownerDocument, 'mousemove', mouseMoveHandler); + i.event.once(i.ownerDocument, 'mouseup', mouseUpHandler); + + e.stopPropagation(); + e.preventDefault(); + }); +} + +function bindMouseScrollYHandler(element, i) { + var currentTop = null; + var currentPageY = null; + + function updateScrollTop(deltaY) { + var newTop = currentTop + (deltaY * i.railYRatio); + var maxTop = Math.max(0, i.scrollbarYRail.getBoundingClientRect().top) + (i.railYRatio * (i.railYHeight - i.scrollbarYHeight)); + + if (newTop < 0) { + i.scrollbarYTop = 0; + } else if (newTop > maxTop) { + i.scrollbarYTop = maxTop; + } else { + i.scrollbarYTop = newTop; + } + + var scrollTop = _.toInt(i.scrollbarYTop * (i.contentHeight - i.containerHeight) / (i.containerHeight - (i.railYRatio * i.scrollbarYHeight))); + updateScroll(element, 'top', scrollTop); + } + + var mouseMoveHandler = function (e) { + updateScrollTop(e.pageY - currentPageY); + updateGeometry(element); + e.stopPropagation(); + e.preventDefault(); + }; + + var mouseUpHandler = function () { + _.stopScrolling(element, 'y'); + i.event.unbind(i.ownerDocument, 'mousemove', mouseMoveHandler); + }; + + i.event.bind(i.scrollbarY, 'mousedown', function (e) { + currentPageY = e.pageY; + currentTop = _.toInt(dom.css(i.scrollbarY, 'top')) * i.railYRatio; + _.startScrolling(element, 'y'); + + i.event.bind(i.ownerDocument, 'mousemove', mouseMoveHandler); + i.event.once(i.ownerDocument, 'mouseup', mouseUpHandler); + + e.stopPropagation(); + e.preventDefault(); + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindMouseScrollXHandler(element, i); + bindMouseScrollYHandler(element, i); +}; diff --git a/library/perfect-scrollbar/src/js/plugin/handler/keyboard.js b/library/perfect-scrollbar/src/js/plugin/handler/keyboard.js new file mode 100644 index 000000000..b23a3bdb1 --- /dev/null +++ b/library/perfect-scrollbar/src/js/plugin/handler/keyboard.js @@ -0,0 +1,154 @@ +'use strict'; + +var _ = require('../../lib/helper'); +var dom = require('../../lib/dom'); +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindKeyboardHandler(element, i) { + var hovered = false; + i.event.bind(element, 'mouseenter', function () { + hovered = true; + }); + i.event.bind(element, 'mouseleave', function () { + hovered = false; + }); + + var shouldPrevent = false; + function shouldPreventDefault(deltaX, deltaY) { + var scrollTop = element.scrollTop; + if (deltaX === 0) { + if (!i.scrollbarYActive) { + return false; + } + if ((scrollTop === 0 && deltaY > 0) || (scrollTop >= i.contentHeight - i.containerHeight && deltaY < 0)) { + return !i.settings.wheelPropagation; + } + } + + var scrollLeft = element.scrollLeft; + if (deltaY === 0) { + if (!i.scrollbarXActive) { + return false; + } + if ((scrollLeft === 0 && deltaX < 0) || (scrollLeft >= i.contentWidth - i.containerWidth && deltaX > 0)) { + return !i.settings.wheelPropagation; + } + } + return true; + } + + i.event.bind(i.ownerDocument, 'keydown', function (e) { + if ((e.isDefaultPrevented && e.isDefaultPrevented()) || e.defaultPrevented) { + return; + } + + var focused = dom.matches(i.scrollbarX, ':focus') || + dom.matches(i.scrollbarY, ':focus'); + + if (!hovered && !focused) { + return; + } + + var activeElement = document.activeElement ? document.activeElement : i.ownerDocument.activeElement; + if (activeElement) { + if (activeElement.tagName === 'IFRAME') { + activeElement = activeElement.contentDocument.activeElement; + } else { + // go deeper if element is a webcomponent + while (activeElement.shadowRoot) { + activeElement = activeElement.shadowRoot.activeElement; + } + } + if (_.isEditable(activeElement)) { + return; + } + } + + var deltaX = 0; + var deltaY = 0; + + switch (e.which) { + case 37: // left + if (e.metaKey) { + deltaX = -i.contentWidth; + } else if (e.altKey) { + deltaX = -i.containerWidth; + } else { + deltaX = -30; + } + break; + case 38: // up + if (e.metaKey) { + deltaY = i.contentHeight; + } else if (e.altKey) { + deltaY = i.containerHeight; + } else { + deltaY = 30; + } + break; + case 39: // right + if (e.metaKey) { + deltaX = i.contentWidth; + } else if (e.altKey) { + deltaX = i.containerWidth; + } else { + deltaX = 30; + } + break; + case 40: // down + if (e.metaKey) { + deltaY = -i.contentHeight; + } else if (e.altKey) { + deltaY = -i.containerHeight; + } else { + deltaY = -30; + } + break; + case 33: // page up + deltaY = 90; + break; + case 32: // space bar + if (e.shiftKey) { + deltaY = 90; + } else { + deltaY = -90; + } + break; + case 34: // page down + deltaY = -90; + break; + case 35: // end + if (e.ctrlKey) { + deltaY = -i.contentHeight; + } else { + deltaY = -i.containerHeight; + } + break; + case 36: // home + if (e.ctrlKey) { + deltaY = element.scrollTop; + } else { + deltaY = i.containerHeight; + } + break; + default: + return; + } + + updateScroll(element, 'top', element.scrollTop - deltaY); + updateScroll(element, 'left', element.scrollLeft + deltaX); + updateGeometry(element); + + shouldPrevent = shouldPreventDefault(deltaX, deltaY); + if (shouldPrevent) { + e.preventDefault(); + } + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindKeyboardHandler(element, i); +}; diff --git a/library/perfect-scrollbar/src/js/plugin/handler/mouse-wheel.js b/library/perfect-scrollbar/src/js/plugin/handler/mouse-wheel.js new file mode 100644 index 000000000..9e08f303b --- /dev/null +++ b/library/perfect-scrollbar/src/js/plugin/handler/mouse-wheel.js @@ -0,0 +1,141 @@ +'use strict'; + +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindMouseWheelHandler(element, i) { + var shouldPrevent = false; + + function shouldPreventDefault(deltaX, deltaY) { + var scrollTop = element.scrollTop; + if (deltaX === 0) { + if (!i.scrollbarYActive) { + return false; + } + if ((scrollTop === 0 && deltaY > 0) || (scrollTop >= i.contentHeight - i.containerHeight && deltaY < 0)) { + return !i.settings.wheelPropagation; + } + } + + var scrollLeft = element.scrollLeft; + if (deltaY === 0) { + if (!i.scrollbarXActive) { + return false; + } + if ((scrollLeft === 0 && deltaX < 0) || (scrollLeft >= i.contentWidth - i.containerWidth && deltaX > 0)) { + return !i.settings.wheelPropagation; + } + } + return true; + } + + function getDeltaFromEvent(e) { + var deltaX = e.deltaX; + var deltaY = -1 * e.deltaY; + + if (typeof deltaX === "undefined" || typeof deltaY === "undefined") { + // OS X Safari + deltaX = -1 * e.wheelDeltaX / 6; + deltaY = e.wheelDeltaY / 6; + } + + if (e.deltaMode && e.deltaMode === 1) { + // Firefox in deltaMode 1: Line scrolling + deltaX *= 10; + deltaY *= 10; + } + + if (deltaX !== deltaX && deltaY !== deltaY/* NaN checks */) { + // IE in some mouse drivers + deltaX = 0; + deltaY = e.wheelDelta; + } + + if (e.shiftKey) { + // reverse axis with shift key + return [-deltaY, -deltaX]; + } + return [deltaX, deltaY]; + } + + function shouldBeConsumedByChild(deltaX, deltaY) { + var child = element.querySelector('textarea:hover, select[multiple]:hover, .ps-child:hover'); + if (child) { + if (!window.getComputedStyle(child).overflow.match(/(scroll|auto)/)) { + // if not scrollable + return false; + } + + var maxScrollTop = child.scrollHeight - child.clientHeight; + if (maxScrollTop > 0) { + if (!(child.scrollTop === 0 && deltaY > 0) && !(child.scrollTop === maxScrollTop && deltaY < 0)) { + return true; + } + } + var maxScrollLeft = child.scrollLeft - child.clientWidth; + if (maxScrollLeft > 0) { + if (!(child.scrollLeft === 0 && deltaX < 0) && !(child.scrollLeft === maxScrollLeft && deltaX > 0)) { + return true; + } + } + } + return false; + } + + function mousewheelHandler(e) { + var delta = getDeltaFromEvent(e); + + var deltaX = delta[0]; + var deltaY = delta[1]; + + if (shouldBeConsumedByChild(deltaX, deltaY)) { + return; + } + + shouldPrevent = false; + if (!i.settings.useBothWheelAxes) { + // deltaX will only be used for horizontal scrolling and deltaY will + // only be used for vertical scrolling - this is the default + updateScroll(element, 'top', element.scrollTop - (deltaY * i.settings.wheelSpeed)); + updateScroll(element, 'left', element.scrollLeft + (deltaX * i.settings.wheelSpeed)); + } else if (i.scrollbarYActive && !i.scrollbarXActive) { + // only vertical scrollbar is active and useBothWheelAxes option is + // active, so let's scroll vertical bar using both mouse wheel axes + if (deltaY) { + updateScroll(element, 'top', element.scrollTop - (deltaY * i.settings.wheelSpeed)); + } else { + updateScroll(element, 'top', element.scrollTop + (deltaX * i.settings.wheelSpeed)); + } + shouldPrevent = true; + } else if (i.scrollbarXActive && !i.scrollbarYActive) { + // useBothWheelAxes and only horizontal bar is active, so use both + // wheel axes for horizontal bar + if (deltaX) { + updateScroll(element, 'left', element.scrollLeft + (deltaX * i.settings.wheelSpeed)); + } else { + updateScroll(element, 'left', element.scrollLeft - (deltaY * i.settings.wheelSpeed)); + } + shouldPrevent = true; + } + + updateGeometry(element); + + shouldPrevent = (shouldPrevent || shouldPreventDefault(deltaX, deltaY)); + if (shouldPrevent) { + e.stopPropagation(); + e.preventDefault(); + } + } + + if (typeof window.onwheel !== "undefined") { + i.event.bind(element, 'wheel', mousewheelHandler); + } else if (typeof window.onmousewheel !== "undefined") { + i.event.bind(element, 'mousewheel', mousewheelHandler); + } +} + +module.exports = function (element) { + var i = instances.get(element); + bindMouseWheelHandler(element, i); +}; diff --git a/library/perfect-scrollbar/src/js/plugin/handler/native-scroll.js b/library/perfect-scrollbar/src/js/plugin/handler/native-scroll.js new file mode 100644 index 000000000..8664b23a3 --- /dev/null +++ b/library/perfect-scrollbar/src/js/plugin/handler/native-scroll.js @@ -0,0 +1,15 @@ +'use strict'; + +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); + +function bindNativeScrollHandler(element, i) { + i.event.bind(element, 'scroll', function () { + updateGeometry(element); + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindNativeScrollHandler(element, i); +}; diff --git a/library/perfect-scrollbar/src/js/plugin/handler/selection.js b/library/perfect-scrollbar/src/js/plugin/handler/selection.js new file mode 100644 index 000000000..705420f47 --- /dev/null +++ b/library/perfect-scrollbar/src/js/plugin/handler/selection.js @@ -0,0 +1,115 @@ +'use strict'; + +var _ = require('../../lib/helper'); +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindSelectionHandler(element, i) { + function getRangeNode() { + var selection = window.getSelection ? window.getSelection() : + document.getSelection ? document.getSelection() : ''; + if (selection.toString().length === 0) { + return null; + } else { + return selection.getRangeAt(0).commonAncestorContainer; + } + } + + var scrollingLoop = null; + var scrollDiff = {top: 0, left: 0}; + function startScrolling() { + if (!scrollingLoop) { + scrollingLoop = setInterval(function () { + if (!instances.get(element)) { + clearInterval(scrollingLoop); + return; + } + + updateScroll(element, 'top', element.scrollTop + scrollDiff.top); + updateScroll(element, 'left', element.scrollLeft + scrollDiff.left); + updateGeometry(element); + }, 50); // every .1 sec + } + } + function stopScrolling() { + if (scrollingLoop) { + clearInterval(scrollingLoop); + scrollingLoop = null; + } + _.stopScrolling(element); + } + + var isSelected = false; + i.event.bind(i.ownerDocument, 'selectionchange', function () { + if (element.contains(getRangeNode())) { + isSelected = true; + } else { + isSelected = false; + stopScrolling(); + } + }); + i.event.bind(window, 'mouseup', function () { + if (isSelected) { + isSelected = false; + stopScrolling(); + } + }); + i.event.bind(window, 'keyup', function () { + if (isSelected) { + isSelected = false; + stopScrolling(); + } + }); + + i.event.bind(window, 'mousemove', function (e) { + if (isSelected) { + var mousePosition = {x: e.pageX, y: e.pageY}; + var containerGeometry = { + left: element.offsetLeft, + right: element.offsetLeft + element.offsetWidth, + top: element.offsetTop, + bottom: element.offsetTop + element.offsetHeight + }; + + if (mousePosition.x < containerGeometry.left + 3) { + scrollDiff.left = -5; + _.startScrolling(element, 'x'); + } else if (mousePosition.x > containerGeometry.right - 3) { + scrollDiff.left = 5; + _.startScrolling(element, 'x'); + } else { + scrollDiff.left = 0; + } + + if (mousePosition.y < containerGeometry.top + 3) { + if (containerGeometry.top + 3 - mousePosition.y < 5) { + scrollDiff.top = -5; + } else { + scrollDiff.top = -20; + } + _.startScrolling(element, 'y'); + } else if (mousePosition.y > containerGeometry.bottom - 3) { + if (mousePosition.y - containerGeometry.bottom + 3 < 5) { + scrollDiff.top = 5; + } else { + scrollDiff.top = 20; + } + _.startScrolling(element, 'y'); + } else { + scrollDiff.top = 0; + } + + if (scrollDiff.top === 0 && scrollDiff.left === 0) { + stopScrolling(); + } else { + startScrolling(); + } + } + }); +} + +module.exports = function (element) { + var i = instances.get(element); + bindSelectionHandler(element, i); +}; diff --git a/library/perfect-scrollbar/src/js/plugin/handler/touch.js b/library/perfect-scrollbar/src/js/plugin/handler/touch.js new file mode 100644 index 000000000..ba5d13307 --- /dev/null +++ b/library/perfect-scrollbar/src/js/plugin/handler/touch.js @@ -0,0 +1,179 @@ +'use strict'; + +var _ = require('../../lib/helper'); +var instances = require('../instances'); +var updateGeometry = require('../update-geometry'); +var updateScroll = require('../update-scroll'); + +function bindTouchHandler(element, i, supportsTouch, supportsIePointer) { + function shouldPreventDefault(deltaX, deltaY) { + var scrollTop = element.scrollTop; + var scrollLeft = element.scrollLeft; + var magnitudeX = Math.abs(deltaX); + var magnitudeY = Math.abs(deltaY); + + if (magnitudeY > magnitudeX) { + // user is perhaps trying to swipe up/down the page + + if (((deltaY < 0) && (scrollTop === i.contentHeight - i.containerHeight)) || + ((deltaY > 0) && (scrollTop === 0))) { + return !i.settings.swipePropagation; + } + } else if (magnitudeX > magnitudeY) { + // user is perhaps trying to swipe left/right across the page + + if (((deltaX < 0) && (scrollLeft === i.contentWidth - i.containerWidth)) || + ((deltaX > 0) && (scrollLeft === 0))) { + return !i.settings.swipePropagation; + } + } + + return true; + } + + function applyTouchMove(differenceX, differenceY) { + updateScroll(element, 'top', element.scrollTop - differenceY); + updateScroll(element, 'left', element.scrollLeft - differenceX); + + updateGeometry(element); + } + + var startOffset = {}; + var startTime = 0; + var speed = {}; + var easingLoop = null; + var inGlobalTouch = false; + var inLocalTouch = false; + + function globalTouchStart() { + inGlobalTouch = true; + } + function globalTouchEnd() { + inGlobalTouch = false; + } + + function getTouch(e) { + if (e.targetTouches) { + return e.targetTouches[0]; + } else { + // Maybe IE pointer + return e; + } + } + function shouldHandle(e) { + if (e.targetTouches && e.targetTouches.length === 1) { + return true; + } + if (e.pointerType && e.pointerType !== 'mouse' && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) { + return true; + } + return false; + } + function touchStart(e) { + if (shouldHandle(e)) { + inLocalTouch = true; + + var touch = getTouch(e); + + startOffset.pageX = touch.pageX; + startOffset.pageY = touch.pageY; + + startTime = (new Date()).getTime(); + + if (easingLoop !== null) { + clearInterval(easingLoop); + } + + e.stopPropagation(); + } + } + function touchMove(e) { + if (!inLocalTouch && i.settings.swipePropagation) { + touchStart(e); + } + if (!inGlobalTouch && inLocalTouch && shouldHandle(e)) { + var touch = getTouch(e); + + var currentOffset = {pageX: touch.pageX, pageY: touch.pageY}; + + var differenceX = currentOffset.pageX - startOffset.pageX; + var differenceY = currentOffset.pageY - startOffset.pageY; + + applyTouchMove(differenceX, differenceY); + startOffset = currentOffset; + + var currentTime = (new Date()).getTime(); + + var timeGap = currentTime - startTime; + if (timeGap > 0) { + speed.x = differenceX / timeGap; + speed.y = differenceY / timeGap; + startTime = currentTime; + } + + if (shouldPreventDefault(differenceX, differenceY)) { + e.stopPropagation(); + e.preventDefault(); + } + } + } + function touchEnd() { + if (!inGlobalTouch && inLocalTouch) { + inLocalTouch = false; + + clearInterval(easingLoop); + easingLoop = setInterval(function () { + if (!instances.get(element)) { + clearInterval(easingLoop); + return; + } + + if (!speed.x && !speed.y) { + clearInterval(easingLoop); + return; + } + + if (Math.abs(speed.x) < 0.01 && Math.abs(speed.y) < 0.01) { + clearInterval(easingLoop); + return; + } + + applyTouchMove(speed.x * 30, speed.y * 30); + + speed.x *= 0.8; + speed.y *= 0.8; + }, 10); + } + } + + if (supportsTouch) { + i.event.bind(window, 'touchstart', globalTouchStart); + i.event.bind(window, 'touchend', globalTouchEnd); + i.event.bind(element, 'touchstart', touchStart); + i.event.bind(element, 'touchmove', touchMove); + i.event.bind(element, 'touchend', touchEnd); + } else if (supportsIePointer) { + if (window.PointerEvent) { + i.event.bind(window, 'pointerdown', globalTouchStart); + i.event.bind(window, 'pointerup', globalTouchEnd); + i.event.bind(element, 'pointerdown', touchStart); + i.event.bind(element, 'pointermove', touchMove); + i.event.bind(element, 'pointerup', touchEnd); + } else if (window.MSPointerEvent) { + i.event.bind(window, 'MSPointerDown', globalTouchStart); + i.event.bind(window, 'MSPointerUp', globalTouchEnd); + i.event.bind(element, 'MSPointerDown', touchStart); + i.event.bind(element, 'MSPointerMove', touchMove); + i.event.bind(element, 'MSPointerUp', touchEnd); + } + } +} + +module.exports = function (element) { + if (!_.env.supportsTouch && !_.env.supportsIePointer) { + return; + } + + var i = instances.get(element); + bindTouchHandler(element, i, _.env.supportsTouch, _.env.supportsIePointer); +}; diff --git a/library/perfect-scrollbar/src/js/plugin/initialize.js b/library/perfect-scrollbar/src/js/plugin/initialize.js new file mode 100644 index 000000000..05918a5a2 --- /dev/null +++ b/library/perfect-scrollbar/src/js/plugin/initialize.js @@ -0,0 +1,37 @@ +'use strict'; + +var _ = require('../lib/helper'); +var cls = require('../lib/class'); +var instances = require('./instances'); +var updateGeometry = require('./update-geometry'); + +// Handlers +var handlers = { + 'click-rail': require('./handler/click-rail'), + 'drag-scrollbar': require('./handler/drag-scrollbar'), + 'keyboard': require('./handler/keyboard'), + 'wheel': require('./handler/mouse-wheel'), + 'touch': require('./handler/touch'), + 'selection': require('./handler/selection') +}; +var nativeScrollHandler = require('./handler/native-scroll'); + +module.exports = function (element, userSettings) { + userSettings = typeof userSettings === 'object' ? userSettings : {}; + + cls.add(element, 'ps-container'); + + // Create a plugin instance. + var i = instances.add(element); + + i.settings = _.extend(i.settings, userSettings); + cls.add(element, 'ps-theme-' + i.settings.theme); + + i.settings.handlers.forEach(function (handlerName) { + handlers[handlerName](element); + }); + + nativeScrollHandler(element); + + updateGeometry(element); +}; diff --git a/library/perfect-scrollbar/src/js/plugin/instances.js b/library/perfect-scrollbar/src/js/plugin/instances.js new file mode 100644 index 000000000..d4d74f377 --- /dev/null +++ b/library/perfect-scrollbar/src/js/plugin/instances.js @@ -0,0 +1,107 @@ +'use strict'; + +var _ = require('../lib/helper'); +var cls = require('../lib/class'); +var defaultSettings = require('./default-setting'); +var dom = require('../lib/dom'); +var EventManager = require('../lib/event-manager'); +var guid = require('../lib/guid'); + +var instances = {}; + +function Instance(element) { + var i = this; + + i.settings = _.clone(defaultSettings); + i.containerWidth = null; + i.containerHeight = null; + i.contentWidth = null; + i.contentHeight = null; + + i.isRtl = dom.css(element, 'direction') === "rtl"; + i.isNegativeScroll = (function () { + var originalScrollLeft = element.scrollLeft; + var result = null; + element.scrollLeft = -1; + result = element.scrollLeft < 0; + element.scrollLeft = originalScrollLeft; + return result; + })(); + i.negativeScrollAdjustment = i.isNegativeScroll ? element.scrollWidth - element.clientWidth : 0; + i.event = new EventManager(); + i.ownerDocument = element.ownerDocument || document; + + function focus() { + cls.add(element, 'ps-focus'); + } + + function blur() { + cls.remove(element, 'ps-focus'); + } + + i.scrollbarXRail = dom.appendTo(dom.e('div', 'ps-scrollbar-x-rail'), element); + i.scrollbarX = dom.appendTo(dom.e('div', 'ps-scrollbar-x'), i.scrollbarXRail); + i.scrollbarX.setAttribute('tabindex', 0); + i.event.bind(i.scrollbarX, 'focus', focus); + i.event.bind(i.scrollbarX, 'blur', blur); + i.scrollbarXActive = null; + i.scrollbarXWidth = null; + i.scrollbarXLeft = null; + i.scrollbarXBottom = _.toInt(dom.css(i.scrollbarXRail, 'bottom')); + i.isScrollbarXUsingBottom = i.scrollbarXBottom === i.scrollbarXBottom; // !isNaN + i.scrollbarXTop = i.isScrollbarXUsingBottom ? null : _.toInt(dom.css(i.scrollbarXRail, 'top')); + i.railBorderXWidth = _.toInt(dom.css(i.scrollbarXRail, 'borderLeftWidth')) + _.toInt(dom.css(i.scrollbarXRail, 'borderRightWidth')); + // Set rail to display:block to calculate margins + dom.css(i.scrollbarXRail, 'display', 'block'); + i.railXMarginWidth = _.toInt(dom.css(i.scrollbarXRail, 'marginLeft')) + _.toInt(dom.css(i.scrollbarXRail, 'marginRight')); + dom.css(i.scrollbarXRail, 'display', ''); + i.railXWidth = null; + i.railXRatio = null; + + i.scrollbarYRail = dom.appendTo(dom.e('div', 'ps-scrollbar-y-rail'), element); + i.scrollbarY = dom.appendTo(dom.e('div', 'ps-scrollbar-y'), i.scrollbarYRail); + i.scrollbarY.setAttribute('tabindex', 0); + i.event.bind(i.scrollbarY, 'focus', focus); + i.event.bind(i.scrollbarY, 'blur', blur); + i.scrollbarYActive = null; + i.scrollbarYHeight = null; + i.scrollbarYTop = null; + i.scrollbarYRight = _.toInt(dom.css(i.scrollbarYRail, 'right')); + i.isScrollbarYUsingRight = i.scrollbarYRight === i.scrollbarYRight; // !isNaN + i.scrollbarYLeft = i.isScrollbarYUsingRight ? null : _.toInt(dom.css(i.scrollbarYRail, 'left')); + i.scrollbarYOuterWidth = i.isRtl ? _.outerWidth(i.scrollbarY) : null; + i.railBorderYWidth = _.toInt(dom.css(i.scrollbarYRail, 'borderTopWidth')) + _.toInt(dom.css(i.scrollbarYRail, 'borderBottomWidth')); + dom.css(i.scrollbarYRail, 'display', 'block'); + i.railYMarginHeight = _.toInt(dom.css(i.scrollbarYRail, 'marginTop')) + _.toInt(dom.css(i.scrollbarYRail, 'marginBottom')); + dom.css(i.scrollbarYRail, 'display', ''); + i.railYHeight = null; + i.railYRatio = null; +} + +function getId(element) { + return element.getAttribute('data-ps-id'); +} + +function setId(element, id) { + element.setAttribute('data-ps-id', id); +} + +function removeId(element) { + element.removeAttribute('data-ps-id'); +} + +exports.add = function (element) { + var newId = guid(); + setId(element, newId); + instances[newId] = new Instance(element); + return instances[newId]; +}; + +exports.remove = function (element) { + delete instances[getId(element)]; + removeId(element); +}; + +exports.get = function (element) { + return instances[getId(element)]; +}; diff --git a/library/perfect-scrollbar/src/js/plugin/update-geometry.js b/library/perfect-scrollbar/src/js/plugin/update-geometry.js new file mode 100644 index 000000000..b936ea1d3 --- /dev/null +++ b/library/perfect-scrollbar/src/js/plugin/update-geometry.js @@ -0,0 +1,126 @@ +'use strict'; + +var _ = require('../lib/helper'); +var cls = require('../lib/class'); +var dom = require('../lib/dom'); +var instances = require('./instances'); +var updateScroll = require('./update-scroll'); + +function getThumbSize(i, thumbSize) { + if (i.settings.minScrollbarLength) { + thumbSize = Math.max(thumbSize, i.settings.minScrollbarLength); + } + if (i.settings.maxScrollbarLength) { + thumbSize = Math.min(thumbSize, i.settings.maxScrollbarLength); + } + return thumbSize; +} + +function updateCss(element, i) { + var xRailOffset = {width: i.railXWidth}; + if (i.isRtl) { + xRailOffset.left = i.negativeScrollAdjustment + element.scrollLeft + i.containerWidth - i.contentWidth; + } else { + xRailOffset.left = element.scrollLeft; + } + if (i.isScrollbarXUsingBottom) { + xRailOffset.bottom = i.scrollbarXBottom - element.scrollTop; + } else { + xRailOffset.top = i.scrollbarXTop + element.scrollTop; + } + dom.css(i.scrollbarXRail, xRailOffset); + + var yRailOffset = {top: element.scrollTop, height: i.railYHeight}; + if (i.isScrollbarYUsingRight) { + if (i.isRtl) { + yRailOffset.right = i.contentWidth - (i.negativeScrollAdjustment + element.scrollLeft) - i.scrollbarYRight - i.scrollbarYOuterWidth; + } else { + yRailOffset.right = i.scrollbarYRight - element.scrollLeft; + } + } else { + if (i.isRtl) { + yRailOffset.left = i.negativeScrollAdjustment + element.scrollLeft + i.containerWidth * 2 - i.contentWidth - i.scrollbarYLeft - i.scrollbarYOuterWidth; + } else { + yRailOffset.left = i.scrollbarYLeft + element.scrollLeft; + } + } + dom.css(i.scrollbarYRail, yRailOffset); + + dom.css(i.scrollbarX, {left: i.scrollbarXLeft, width: i.scrollbarXWidth - i.railBorderXWidth}); + dom.css(i.scrollbarY, {top: i.scrollbarYTop, height: i.scrollbarYHeight - i.railBorderYWidth}); +} + +module.exports = function (element) { + var i = instances.get(element); + + i.containerWidth = element.clientWidth; + i.containerHeight = element.clientHeight; + i.contentWidth = element.scrollWidth; + i.contentHeight = element.scrollHeight; + + var existingRails; + if (!element.contains(i.scrollbarXRail)) { + existingRails = dom.queryChildren(element, '.ps-scrollbar-x-rail'); + if (existingRails.length > 0) { + existingRails.forEach(function (rail) { + dom.remove(rail); + }); + } + dom.appendTo(i.scrollbarXRail, element); + } + if (!element.contains(i.scrollbarYRail)) { + existingRails = dom.queryChildren(element, '.ps-scrollbar-y-rail'); + if (existingRails.length > 0) { + existingRails.forEach(function (rail) { + dom.remove(rail); + }); + } + dom.appendTo(i.scrollbarYRail, element); + } + + if (!i.settings.suppressScrollX && i.containerWidth + i.settings.scrollXMarginOffset < i.contentWidth) { + i.scrollbarXActive = true; + i.railXWidth = i.containerWidth - i.railXMarginWidth; + i.railXRatio = i.containerWidth / i.railXWidth; + i.scrollbarXWidth = getThumbSize(i, _.toInt(i.railXWidth * i.containerWidth / i.contentWidth)); + i.scrollbarXLeft = _.toInt((i.negativeScrollAdjustment + element.scrollLeft) * (i.railXWidth - i.scrollbarXWidth) / (i.contentWidth - i.containerWidth)); + } else { + i.scrollbarXActive = false; + } + + if (!i.settings.suppressScrollY && i.containerHeight + i.settings.scrollYMarginOffset < i.contentHeight) { + i.scrollbarYActive = true; + i.railYHeight = i.containerHeight - i.railYMarginHeight; + i.railYRatio = i.containerHeight / i.railYHeight; + i.scrollbarYHeight = getThumbSize(i, _.toInt(i.railYHeight * i.containerHeight / i.contentHeight)); + i.scrollbarYTop = _.toInt(element.scrollTop * (i.railYHeight - i.scrollbarYHeight) / (i.contentHeight - i.containerHeight)); + } else { + i.scrollbarYActive = false; + } + + if (i.scrollbarXLeft >= i.railXWidth - i.scrollbarXWidth) { + i.scrollbarXLeft = i.railXWidth - i.scrollbarXWidth; + } + if (i.scrollbarYTop >= i.railYHeight - i.scrollbarYHeight) { + i.scrollbarYTop = i.railYHeight - i.scrollbarYHeight; + } + + updateCss(element, i); + + if (i.scrollbarXActive) { + cls.add(element, 'ps-active-x'); + } else { + cls.remove(element, 'ps-active-x'); + i.scrollbarXWidth = 0; + i.scrollbarXLeft = 0; + updateScroll(element, 'left', 0); + } + if (i.scrollbarYActive) { + cls.add(element, 'ps-active-y'); + } else { + cls.remove(element, 'ps-active-y'); + i.scrollbarYHeight = 0; + i.scrollbarYTop = 0; + updateScroll(element, 'top', 0); + } +}; diff --git a/library/perfect-scrollbar/src/js/plugin/update-scroll.js b/library/perfect-scrollbar/src/js/plugin/update-scroll.js new file mode 100644 index 000000000..1a9ad2ccf --- /dev/null +++ b/library/perfect-scrollbar/src/js/plugin/update-scroll.js @@ -0,0 +1,97 @@ +'use strict'; + +var instances = require('./instances'); + +var lastTop; +var lastLeft; + +var createDOMEvent = function (name) { + var event = document.createEvent("Event"); + event.initEvent(name, true, true); + return event; +}; + +module.exports = function (element, axis, value) { + if (typeof element === 'undefined') { + throw 'You must provide an element to the update-scroll function'; + } + + if (typeof axis === 'undefined') { + throw 'You must provide an axis to the update-scroll function'; + } + + if (typeof value === 'undefined') { + throw 'You must provide a value to the update-scroll function'; + } + + if (axis === 'top' && value <= 0) { + element.scrollTop = value = 0; // don't allow negative scroll + element.dispatchEvent(createDOMEvent('ps-y-reach-start')); + } + + if (axis === 'left' && value <= 0) { + element.scrollLeft = value = 0; // don't allow negative scroll + element.dispatchEvent(createDOMEvent('ps-x-reach-start')); + } + + var i = instances.get(element); + + if (axis === 'top' && value >= i.contentHeight - i.containerHeight) { + // don't allow scroll past container + value = i.contentHeight - i.containerHeight; + if (value - element.scrollTop <= 1) { + // mitigates rounding errors on non-subpixel scroll values + value = element.scrollTop; + } else { + element.scrollTop = value; + } + element.dispatchEvent(createDOMEvent('ps-y-reach-end')); + } + + if (axis === 'left' && value >= i.contentWidth - i.containerWidth) { + // don't allow scroll past container + value = i.contentWidth - i.containerWidth; + if (value - element.scrollLeft <= 1) { + // mitigates rounding errors on non-subpixel scroll values + value = element.scrollLeft; + } else { + element.scrollLeft = value; + } + element.dispatchEvent(createDOMEvent('ps-x-reach-end')); + } + + if (!lastTop) { + lastTop = element.scrollTop; + } + + if (!lastLeft) { + lastLeft = element.scrollLeft; + } + + if (axis === 'top' && value < lastTop) { + element.dispatchEvent(createDOMEvent('ps-scroll-up')); + } + + if (axis === 'top' && value > lastTop) { + element.dispatchEvent(createDOMEvent('ps-scroll-down')); + } + + if (axis === 'left' && value < lastLeft) { + element.dispatchEvent(createDOMEvent('ps-scroll-left')); + } + + if (axis === 'left' && value > lastLeft) { + element.dispatchEvent(createDOMEvent('ps-scroll-right')); + } + + if (axis === 'top') { + element.scrollTop = lastTop = value; + element.dispatchEvent(createDOMEvent('ps-scroll-y')); + } + + if (axis === 'left') { + element.scrollLeft = lastLeft = value; + element.dispatchEvent(createDOMEvent('ps-scroll-x')); + } + +}; diff --git a/library/perfect-scrollbar/src/js/plugin/update.js b/library/perfect-scrollbar/src/js/plugin/update.js new file mode 100644 index 000000000..cc9da15f4 --- /dev/null +++ b/library/perfect-scrollbar/src/js/plugin/update.js @@ -0,0 +1,37 @@ +'use strict'; + +var _ = require('../lib/helper'); +var dom = require('../lib/dom'); +var instances = require('./instances'); +var updateGeometry = require('./update-geometry'); +var updateScroll = require('./update-scroll'); + +module.exports = function (element) { + var i = instances.get(element); + + if (!i) { + return; + } + + // Recalcuate negative scrollLeft adjustment + i.negativeScrollAdjustment = i.isNegativeScroll ? element.scrollWidth - element.clientWidth : 0; + + // Recalculate rail margins + dom.css(i.scrollbarXRail, 'display', 'block'); + dom.css(i.scrollbarYRail, 'display', 'block'); + i.railXMarginWidth = _.toInt(dom.css(i.scrollbarXRail, 'marginLeft')) + _.toInt(dom.css(i.scrollbarXRail, 'marginRight')); + i.railYMarginHeight = _.toInt(dom.css(i.scrollbarYRail, 'marginTop')) + _.toInt(dom.css(i.scrollbarYRail, 'marginBottom')); + + // Hide scrollbars not to affect scrollWidth and scrollHeight + dom.css(i.scrollbarXRail, 'display', 'none'); + dom.css(i.scrollbarYRail, 'display', 'none'); + + updateGeometry(element); + + // Update top/left scroll to trigger events + updateScroll(element, 'top', element.scrollTop); + updateScroll(element, 'left', element.scrollLeft); + + dom.css(i.scrollbarXRail, 'display', ''); + dom.css(i.scrollbarYRail, 'display', ''); +}; diff --git a/view/templates/head.tpl b/view/templates/head.tpl index 3896a4b4a..ceae498cf 100644 --- a/view/templates/head.tpl +++ b/view/templates/head.tpl @@ -6,7 +6,7 @@ - + {{foreach $stylesheets as $stylesheetUrl}} @@ -40,7 +40,7 @@ - + diff --git a/view/theme/frio/templates/head.tpl b/view/theme/frio/templates/head.tpl index b539a90d9..89fd37736 100644 --- a/view/theme/frio/templates/head.tpl +++ b/view/theme/frio/templates/head.tpl @@ -11,7 +11,7 @@ - + @@ -61,7 +61,7 @@ - + From f3bf723cc70b2a09cd04561a0161324c20d366d7 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 5 May 2020 12:42:30 +0000 Subject: [PATCH 17/28] Moved to view/js --- {library => view/js}/perfect-scrollbar/LICENSE | 0 {library => view/js}/perfect-scrollbar/README.md | 0 {library => view/js}/perfect-scrollbar/bower.json | 0 .../js}/perfect-scrollbar/css/perfect-scrollbar.css | 0 .../js}/perfect-scrollbar/css/perfect-scrollbar.min.css | 0 .../js}/perfect-scrollbar/js/perfect-scrollbar.jquery.js | 0 .../js}/perfect-scrollbar/js/perfect-scrollbar.jquery.min.js | 0 .../js}/perfect-scrollbar/js/perfect-scrollbar.js | 0 .../js}/perfect-scrollbar/js/perfect-scrollbar.min.js | 0 {library => view/js}/perfect-scrollbar/src/css/main.scss | 0 {library => view/js}/perfect-scrollbar/src/css/mixins.scss | 0 {library => view/js}/perfect-scrollbar/src/css/themes.scss | 0 {library => view/js}/perfect-scrollbar/src/css/variables.scss | 0 .../js}/perfect-scrollbar/src/js/adaptor/global.js | 0 .../js}/perfect-scrollbar/src/js/adaptor/jquery.js | 0 {library => view/js}/perfect-scrollbar/src/js/lib/class.js | 0 {library => view/js}/perfect-scrollbar/src/js/lib/dom.js | 0 .../js}/perfect-scrollbar/src/js/lib/event-manager.js | 0 {library => view/js}/perfect-scrollbar/src/js/lib/guid.js | 0 {library => view/js}/perfect-scrollbar/src/js/lib/helper.js | 0 {library => view/js}/perfect-scrollbar/src/js/main.js | 0 .../js}/perfect-scrollbar/src/js/plugin/default-setting.js | 0 .../js}/perfect-scrollbar/src/js/plugin/destroy.js | 0 .../js}/perfect-scrollbar/src/js/plugin/handler/click-rail.js | 0 .../perfect-scrollbar/src/js/plugin/handler/drag-scrollbar.js | 0 .../js}/perfect-scrollbar/src/js/plugin/handler/keyboard.js | 0 .../perfect-scrollbar/src/js/plugin/handler/mouse-wheel.js | 0 .../perfect-scrollbar/src/js/plugin/handler/native-scroll.js | 0 .../js}/perfect-scrollbar/src/js/plugin/handler/selection.js | 0 .../js}/perfect-scrollbar/src/js/plugin/handler/touch.js | 0 .../js}/perfect-scrollbar/src/js/plugin/initialize.js | 0 .../js}/perfect-scrollbar/src/js/plugin/instances.js | 0 .../js}/perfect-scrollbar/src/js/plugin/update-geometry.js | 0 .../js}/perfect-scrollbar/src/js/plugin/update-scroll.js | 0 .../js}/perfect-scrollbar/src/js/plugin/update.js | 0 view/templates/head.tpl | 4 ++-- view/theme/frio/templates/head.tpl | 4 ++-- 37 files changed, 4 insertions(+), 4 deletions(-) rename {library => view/js}/perfect-scrollbar/LICENSE (100%) rename {library => view/js}/perfect-scrollbar/README.md (100%) rename {library => view/js}/perfect-scrollbar/bower.json (100%) rename {library => view/js}/perfect-scrollbar/css/perfect-scrollbar.css (100%) rename {library => view/js}/perfect-scrollbar/css/perfect-scrollbar.min.css (100%) rename {library => view/js}/perfect-scrollbar/js/perfect-scrollbar.jquery.js (100%) rename {library => view/js}/perfect-scrollbar/js/perfect-scrollbar.jquery.min.js (100%) rename {library => view/js}/perfect-scrollbar/js/perfect-scrollbar.js (100%) rename {library => view/js}/perfect-scrollbar/js/perfect-scrollbar.min.js (100%) rename {library => view/js}/perfect-scrollbar/src/css/main.scss (100%) rename {library => view/js}/perfect-scrollbar/src/css/mixins.scss (100%) rename {library => view/js}/perfect-scrollbar/src/css/themes.scss (100%) rename {library => view/js}/perfect-scrollbar/src/css/variables.scss (100%) rename {library => view/js}/perfect-scrollbar/src/js/adaptor/global.js (100%) rename {library => view/js}/perfect-scrollbar/src/js/adaptor/jquery.js (100%) rename {library => view/js}/perfect-scrollbar/src/js/lib/class.js (100%) rename {library => view/js}/perfect-scrollbar/src/js/lib/dom.js (100%) rename {library => view/js}/perfect-scrollbar/src/js/lib/event-manager.js (100%) rename {library => view/js}/perfect-scrollbar/src/js/lib/guid.js (100%) rename {library => view/js}/perfect-scrollbar/src/js/lib/helper.js (100%) rename {library => view/js}/perfect-scrollbar/src/js/main.js (100%) rename {library => view/js}/perfect-scrollbar/src/js/plugin/default-setting.js (100%) rename {library => view/js}/perfect-scrollbar/src/js/plugin/destroy.js (100%) rename {library => view/js}/perfect-scrollbar/src/js/plugin/handler/click-rail.js (100%) rename {library => view/js}/perfect-scrollbar/src/js/plugin/handler/drag-scrollbar.js (100%) rename {library => view/js}/perfect-scrollbar/src/js/plugin/handler/keyboard.js (100%) rename {library => view/js}/perfect-scrollbar/src/js/plugin/handler/mouse-wheel.js (100%) rename {library => view/js}/perfect-scrollbar/src/js/plugin/handler/native-scroll.js (100%) rename {library => view/js}/perfect-scrollbar/src/js/plugin/handler/selection.js (100%) rename {library => view/js}/perfect-scrollbar/src/js/plugin/handler/touch.js (100%) rename {library => view/js}/perfect-scrollbar/src/js/plugin/initialize.js (100%) rename {library => view/js}/perfect-scrollbar/src/js/plugin/instances.js (100%) rename {library => view/js}/perfect-scrollbar/src/js/plugin/update-geometry.js (100%) rename {library => view/js}/perfect-scrollbar/src/js/plugin/update-scroll.js (100%) rename {library => view/js}/perfect-scrollbar/src/js/plugin/update.js (100%) diff --git a/library/perfect-scrollbar/LICENSE b/view/js/perfect-scrollbar/LICENSE similarity index 100% rename from library/perfect-scrollbar/LICENSE rename to view/js/perfect-scrollbar/LICENSE diff --git a/library/perfect-scrollbar/README.md b/view/js/perfect-scrollbar/README.md similarity index 100% rename from library/perfect-scrollbar/README.md rename to view/js/perfect-scrollbar/README.md diff --git a/library/perfect-scrollbar/bower.json b/view/js/perfect-scrollbar/bower.json similarity index 100% rename from library/perfect-scrollbar/bower.json rename to view/js/perfect-scrollbar/bower.json diff --git a/library/perfect-scrollbar/css/perfect-scrollbar.css b/view/js/perfect-scrollbar/css/perfect-scrollbar.css similarity index 100% rename from library/perfect-scrollbar/css/perfect-scrollbar.css rename to view/js/perfect-scrollbar/css/perfect-scrollbar.css diff --git a/library/perfect-scrollbar/css/perfect-scrollbar.min.css b/view/js/perfect-scrollbar/css/perfect-scrollbar.min.css similarity index 100% rename from library/perfect-scrollbar/css/perfect-scrollbar.min.css rename to view/js/perfect-scrollbar/css/perfect-scrollbar.min.css diff --git a/library/perfect-scrollbar/js/perfect-scrollbar.jquery.js b/view/js/perfect-scrollbar/js/perfect-scrollbar.jquery.js similarity index 100% rename from library/perfect-scrollbar/js/perfect-scrollbar.jquery.js rename to view/js/perfect-scrollbar/js/perfect-scrollbar.jquery.js diff --git a/library/perfect-scrollbar/js/perfect-scrollbar.jquery.min.js b/view/js/perfect-scrollbar/js/perfect-scrollbar.jquery.min.js similarity index 100% rename from library/perfect-scrollbar/js/perfect-scrollbar.jquery.min.js rename to view/js/perfect-scrollbar/js/perfect-scrollbar.jquery.min.js diff --git a/library/perfect-scrollbar/js/perfect-scrollbar.js b/view/js/perfect-scrollbar/js/perfect-scrollbar.js similarity index 100% rename from library/perfect-scrollbar/js/perfect-scrollbar.js rename to view/js/perfect-scrollbar/js/perfect-scrollbar.js diff --git a/library/perfect-scrollbar/js/perfect-scrollbar.min.js b/view/js/perfect-scrollbar/js/perfect-scrollbar.min.js similarity index 100% rename from library/perfect-scrollbar/js/perfect-scrollbar.min.js rename to view/js/perfect-scrollbar/js/perfect-scrollbar.min.js diff --git a/library/perfect-scrollbar/src/css/main.scss b/view/js/perfect-scrollbar/src/css/main.scss similarity index 100% rename from library/perfect-scrollbar/src/css/main.scss rename to view/js/perfect-scrollbar/src/css/main.scss diff --git a/library/perfect-scrollbar/src/css/mixins.scss b/view/js/perfect-scrollbar/src/css/mixins.scss similarity index 100% rename from library/perfect-scrollbar/src/css/mixins.scss rename to view/js/perfect-scrollbar/src/css/mixins.scss diff --git a/library/perfect-scrollbar/src/css/themes.scss b/view/js/perfect-scrollbar/src/css/themes.scss similarity index 100% rename from library/perfect-scrollbar/src/css/themes.scss rename to view/js/perfect-scrollbar/src/css/themes.scss diff --git a/library/perfect-scrollbar/src/css/variables.scss b/view/js/perfect-scrollbar/src/css/variables.scss similarity index 100% rename from library/perfect-scrollbar/src/css/variables.scss rename to view/js/perfect-scrollbar/src/css/variables.scss diff --git a/library/perfect-scrollbar/src/js/adaptor/global.js b/view/js/perfect-scrollbar/src/js/adaptor/global.js similarity index 100% rename from library/perfect-scrollbar/src/js/adaptor/global.js rename to view/js/perfect-scrollbar/src/js/adaptor/global.js diff --git a/library/perfect-scrollbar/src/js/adaptor/jquery.js b/view/js/perfect-scrollbar/src/js/adaptor/jquery.js similarity index 100% rename from library/perfect-scrollbar/src/js/adaptor/jquery.js rename to view/js/perfect-scrollbar/src/js/adaptor/jquery.js diff --git a/library/perfect-scrollbar/src/js/lib/class.js b/view/js/perfect-scrollbar/src/js/lib/class.js similarity index 100% rename from library/perfect-scrollbar/src/js/lib/class.js rename to view/js/perfect-scrollbar/src/js/lib/class.js diff --git a/library/perfect-scrollbar/src/js/lib/dom.js b/view/js/perfect-scrollbar/src/js/lib/dom.js similarity index 100% rename from library/perfect-scrollbar/src/js/lib/dom.js rename to view/js/perfect-scrollbar/src/js/lib/dom.js diff --git a/library/perfect-scrollbar/src/js/lib/event-manager.js b/view/js/perfect-scrollbar/src/js/lib/event-manager.js similarity index 100% rename from library/perfect-scrollbar/src/js/lib/event-manager.js rename to view/js/perfect-scrollbar/src/js/lib/event-manager.js diff --git a/library/perfect-scrollbar/src/js/lib/guid.js b/view/js/perfect-scrollbar/src/js/lib/guid.js similarity index 100% rename from library/perfect-scrollbar/src/js/lib/guid.js rename to view/js/perfect-scrollbar/src/js/lib/guid.js diff --git a/library/perfect-scrollbar/src/js/lib/helper.js b/view/js/perfect-scrollbar/src/js/lib/helper.js similarity index 100% rename from library/perfect-scrollbar/src/js/lib/helper.js rename to view/js/perfect-scrollbar/src/js/lib/helper.js diff --git a/library/perfect-scrollbar/src/js/main.js b/view/js/perfect-scrollbar/src/js/main.js similarity index 100% rename from library/perfect-scrollbar/src/js/main.js rename to view/js/perfect-scrollbar/src/js/main.js diff --git a/library/perfect-scrollbar/src/js/plugin/default-setting.js b/view/js/perfect-scrollbar/src/js/plugin/default-setting.js similarity index 100% rename from library/perfect-scrollbar/src/js/plugin/default-setting.js rename to view/js/perfect-scrollbar/src/js/plugin/default-setting.js diff --git a/library/perfect-scrollbar/src/js/plugin/destroy.js b/view/js/perfect-scrollbar/src/js/plugin/destroy.js similarity index 100% rename from library/perfect-scrollbar/src/js/plugin/destroy.js rename to view/js/perfect-scrollbar/src/js/plugin/destroy.js diff --git a/library/perfect-scrollbar/src/js/plugin/handler/click-rail.js b/view/js/perfect-scrollbar/src/js/plugin/handler/click-rail.js similarity index 100% rename from library/perfect-scrollbar/src/js/plugin/handler/click-rail.js rename to view/js/perfect-scrollbar/src/js/plugin/handler/click-rail.js diff --git a/library/perfect-scrollbar/src/js/plugin/handler/drag-scrollbar.js b/view/js/perfect-scrollbar/src/js/plugin/handler/drag-scrollbar.js similarity index 100% rename from library/perfect-scrollbar/src/js/plugin/handler/drag-scrollbar.js rename to view/js/perfect-scrollbar/src/js/plugin/handler/drag-scrollbar.js diff --git a/library/perfect-scrollbar/src/js/plugin/handler/keyboard.js b/view/js/perfect-scrollbar/src/js/plugin/handler/keyboard.js similarity index 100% rename from library/perfect-scrollbar/src/js/plugin/handler/keyboard.js rename to view/js/perfect-scrollbar/src/js/plugin/handler/keyboard.js diff --git a/library/perfect-scrollbar/src/js/plugin/handler/mouse-wheel.js b/view/js/perfect-scrollbar/src/js/plugin/handler/mouse-wheel.js similarity index 100% rename from library/perfect-scrollbar/src/js/plugin/handler/mouse-wheel.js rename to view/js/perfect-scrollbar/src/js/plugin/handler/mouse-wheel.js diff --git a/library/perfect-scrollbar/src/js/plugin/handler/native-scroll.js b/view/js/perfect-scrollbar/src/js/plugin/handler/native-scroll.js similarity index 100% rename from library/perfect-scrollbar/src/js/plugin/handler/native-scroll.js rename to view/js/perfect-scrollbar/src/js/plugin/handler/native-scroll.js diff --git a/library/perfect-scrollbar/src/js/plugin/handler/selection.js b/view/js/perfect-scrollbar/src/js/plugin/handler/selection.js similarity index 100% rename from library/perfect-scrollbar/src/js/plugin/handler/selection.js rename to view/js/perfect-scrollbar/src/js/plugin/handler/selection.js diff --git a/library/perfect-scrollbar/src/js/plugin/handler/touch.js b/view/js/perfect-scrollbar/src/js/plugin/handler/touch.js similarity index 100% rename from library/perfect-scrollbar/src/js/plugin/handler/touch.js rename to view/js/perfect-scrollbar/src/js/plugin/handler/touch.js diff --git a/library/perfect-scrollbar/src/js/plugin/initialize.js b/view/js/perfect-scrollbar/src/js/plugin/initialize.js similarity index 100% rename from library/perfect-scrollbar/src/js/plugin/initialize.js rename to view/js/perfect-scrollbar/src/js/plugin/initialize.js diff --git a/library/perfect-scrollbar/src/js/plugin/instances.js b/view/js/perfect-scrollbar/src/js/plugin/instances.js similarity index 100% rename from library/perfect-scrollbar/src/js/plugin/instances.js rename to view/js/perfect-scrollbar/src/js/plugin/instances.js diff --git a/library/perfect-scrollbar/src/js/plugin/update-geometry.js b/view/js/perfect-scrollbar/src/js/plugin/update-geometry.js similarity index 100% rename from library/perfect-scrollbar/src/js/plugin/update-geometry.js rename to view/js/perfect-scrollbar/src/js/plugin/update-geometry.js diff --git a/library/perfect-scrollbar/src/js/plugin/update-scroll.js b/view/js/perfect-scrollbar/src/js/plugin/update-scroll.js similarity index 100% rename from library/perfect-scrollbar/src/js/plugin/update-scroll.js rename to view/js/perfect-scrollbar/src/js/plugin/update-scroll.js diff --git a/library/perfect-scrollbar/src/js/plugin/update.js b/view/js/perfect-scrollbar/src/js/plugin/update.js similarity index 100% rename from library/perfect-scrollbar/src/js/plugin/update.js rename to view/js/perfect-scrollbar/src/js/plugin/update.js diff --git a/view/templates/head.tpl b/view/templates/head.tpl index ceae498cf..a83f3dd20 100644 --- a/view/templates/head.tpl +++ b/view/templates/head.tpl @@ -6,7 +6,7 @@ - + {{foreach $stylesheets as $stylesheetUrl}} @@ -40,7 +40,7 @@ - + diff --git a/view/theme/frio/templates/head.tpl b/view/theme/frio/templates/head.tpl index 89fd37736..ae00ea85e 100644 --- a/view/theme/frio/templates/head.tpl +++ b/view/theme/frio/templates/head.tpl @@ -11,7 +11,7 @@ - + @@ -61,7 +61,7 @@ - + From d838dcbd8cfdbe154ac49ad6428e659c6b14394e Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 5 May 2020 14:10:39 +0000 Subject: [PATCH 18/28] Removed unused fields --- src/Protocol/Diaspora.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index 3cdf21b57..708f5335a 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -2325,7 +2325,7 @@ class Diaspora Logger::info('Participation stored', ['id' => $message_id, 'guid' => $guid, 'parent_guid' => $parent_guid, 'author' => $author]); // Send all existing comments and likes to the requesting server - $comments = Item::select(['id', 'uri-id', 'parent', 'verb', 'self'], ['parent' => $parent_item['id']]); + $comments = Item::select(['id', 'uri-id', 'parent'], ['parent' => $parent_item['id']]); while ($comment = Item::fetch($comments)) { if ($comment['id'] == $comment['parent']) { continue; From 57257840553a2f91c85e4ad37143fffd50b1cc65 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 5 May 2020 15:47:35 +0000 Subject: [PATCH 19/28] Update database definition, cleaned code --- database.sql | 3 +++ src/Database/PostUpdate.php | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/database.sql b/database.sql index 5f6fc2932..a26b6f2bf 100644 --- a/database.sql +++ b/database.sql @@ -815,6 +815,8 @@ CREATE TABLE IF NOT EXISTS `notify` ( `link` varchar(255) NOT NULL DEFAULT '' COMMENT '', `iid` int unsigned NOT NULL DEFAULT 0 COMMENT 'item.id', `parent` int unsigned NOT NULL DEFAULT 0 COMMENT '', + `uri-id` int unsigned COMMENT 'Item-uri id of the related post', + `parent-uri-id` int unsigned COMMENT 'Item-uri id of the parent of the related post', `seen` boolean NOT NULL DEFAULT '0' COMMENT '', `verb` varchar(100) NOT NULL DEFAULT '' COMMENT '', `otype` varchar(10) NOT NULL DEFAULT '' COMMENT '', @@ -833,6 +835,7 @@ CREATE TABLE IF NOT EXISTS `notify-threads` ( `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', `notify-id` int unsigned NOT NULL DEFAULT 0 COMMENT '', `master-parent-item` int unsigned NOT NULL DEFAULT 0 COMMENT '', + `master-parent-uri-id` int unsigned COMMENT 'Item-uri id of the parent of the related post', `parent-item` int unsigned NOT NULL DEFAULT 0 COMMENT '', `receiver-uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', PRIMARY KEY(`id`) diff --git a/src/Database/PostUpdate.php b/src/Database/PostUpdate.php index 25cf99142..514956b22 100644 --- a/src/Database/PostUpdate.php +++ b/src/Database/PostUpdate.php @@ -688,6 +688,7 @@ class PostUpdate } $id = DI::config()->get('system', 'post_update_version_1345_id', 0); + Logger::info('Start', ['item' => $id]); $rows = 0; @@ -726,8 +727,6 @@ class PostUpdate return false; } - - /** * Fill the "tag" table with tags and mentions from the "term" table * From d67b9cb693cbce4df0d1b9b5889a053eb5714bee Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 5 May 2020 16:01:20 +0000 Subject: [PATCH 20/28] Formula according to documentation --- src/Module/Admin/Summary.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Module/Admin/Summary.php b/src/Module/Admin/Summary.php index 7bd7901f7..4aaeaaec0 100644 --- a/src/Module/Admin/Summary.php +++ b/src/Module/Admin/Summary.php @@ -59,7 +59,7 @@ class Summary extends BaseAdmin $table_definition_cache = DBA::getVariable('table_definition_cache'); $table_open_cache = DBA::getVariable('table_open_cache'); if (!empty($table_definition_cache) && !empty($table_open_cache)) { - $suggested_definition_cache = max(400, round($table_open_cache / 2, 1)); + $suggested_definition_cache = min(400 + round($table_open_cache / 2, 1), 2000); if ($suggested_definition_cache > $table_definition_cache) { $warningtext[] = DI::l10n()->t('Your table_definition_cache is too low (%d). This can lead to the database error "Prepared statement needs to be re-prepared". Please set it at least to %d (or -1 for autosizing). See here for more information.
', $table_definition_cache, $suggested_definition_cache, 'https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_table_definition_cache'); } From f67c8a73153a09826feda49a9c5fca030c7c1a19 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 5 May 2020 16:42:29 +0000 Subject: [PATCH 21/28] Fix test --- tests/include/ApiTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/include/ApiTest.php b/tests/include/ApiTest.php index 145619a75..e4443404e 100644 --- a/tests/include/ApiTest.php +++ b/tests/include/ApiTest.php @@ -3849,7 +3849,7 @@ class ApiTest extends DatabaseTest $assertXml=<< - + XML; $this->assertXmlStringEqualsXmlString($assertXml, $result); From 349e1d20151304ec31283ff8f04c1feb28f36da8 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 5 May 2020 17:32:11 +0000 Subject: [PATCH 22/28] Tests could work again --- tests/include/ApiTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/include/ApiTest.php b/tests/include/ApiTest.php index e4443404e..9970ced24 100644 --- a/tests/include/ApiTest.php +++ b/tests/include/ApiTest.php @@ -3849,7 +3849,7 @@ class ApiTest extends DatabaseTest $assertXml=<< - + XML; $this->assertXmlStringEqualsXmlString($assertXml, $result); From 9c36b63b33b7bae4da27a5d6257df9e74cf7c5f2 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 5 May 2020 18:53:38 +0000 Subject: [PATCH 23/28] The composer lock file had been updated --- composer.lock | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/composer.lock b/composer.lock index c04c2e9b1..52f1e80a3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e1a839b13f7ba5892c8730d0da3ddf1c", + "content-hash": "0d0fe0cafbe7bd050b41a5c9e96dba5f", "packages": [ { "name": "asika/simple-console", @@ -454,7 +454,6 @@ "jsonld.php" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -472,11 +471,11 @@ "description": "A JSON-LD Processor and API implementation in PHP.", "homepage": "https://git.friendi.ca/friendica/php-json-ld", "keywords": [ + "JSON", "JSON-LD", "Linked Data", "RDF", "Semantic Web", - "json", "jsonld" ], "time": "2018-10-08T20:41:00+00:00" @@ -3894,7 +3893,7 @@ } ], "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", + "homepage": "http://www.github.com/sebastianbergmann/comparator", "keywords": [ "comparator", "compare", @@ -3996,7 +3995,7 @@ } ], "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "https://github.com/sebastianbergmann/environment", + "homepage": "http://www.github.com/sebastianbergmann/environment", "keywords": [ "Xdebug", "environment", @@ -4064,7 +4063,7 @@ } ], "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://github.com/sebastianbergmann/exporter", + "homepage": "http://www.github.com/sebastianbergmann/exporter", "keywords": [ "export", "exporter" @@ -4116,7 +4115,7 @@ } ], "description": "Snapshotting of global state", - "homepage": "https://github.com/sebastianbergmann/global-state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", "keywords": [ "global state" ], @@ -4218,7 +4217,7 @@ } ], "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", "time": "2016-11-19T07:33:16+00:00" }, { From 980de370472a100e81b90a2937e8a4ae710d4e99 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 5 May 2020 19:54:25 +0000 Subject: [PATCH 24/28] Removed legacy usage of the "tag" field --- mod/photos.php | 5 +++-- mod/tagrm.php | 7 ++++--- src/Model/Tag.php | 17 +++++++++++++++++ 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/mod/photos.php b/mod/photos.php index 8f968d0c0..f4983a838 100644 --- a/mod/photos.php +++ b/mod/photos.php @@ -1307,9 +1307,10 @@ function photos_content(App $a) } $tags = null; + $tag_text = Tag::tagTextFromItemId($link_item['uri-id']); - if (!empty($link_item['id']) && !empty($link_item['tag'])) { - $arr = explode(',', $link_item['tag']); + if (!empty($link_item['id']) && !empty($tag_text)) { + $arr = explode(',', $tag_text); // parse tags and add links $tag_arr = []; foreach ($arr as $tag) { diff --git a/mod/tagrm.php b/mod/tagrm.php index 7e8ae8524..3aefe0680 100644 --- a/mod/tagrm.php +++ b/mod/tagrm.php @@ -97,15 +97,16 @@ function tagrm_content(App $a) // NOTREACHED } - $item = Item::selectFirst(['tag'], ['id' => $item_id, 'uid' => local_user()]); + $item = Item::selectFirst(['uri-id'], ['id' => $item_id, 'uid' => local_user()]); if (!DBA::isResult($item)) { DI::baseUrl()->redirect($_SESSION['photo_return']); } - $arr = explode(',', $item['tag']); + $tag_text = Tag::tagTextFromItemId($item['uri-id']); + $arr = explode(',', $tag_text); - if (empty($item['tag'])) { + if (empty($arr)) { DI::baseUrl()->redirect($_SESSION['photo_return']); } diff --git a/src/Model/Tag.php b/src/Model/Tag.php index 9b4b1eb41..f21d2ffec 100644 --- a/src/Model/Tag.php +++ b/src/Model/Tag.php @@ -339,6 +339,23 @@ class Tag return DBA::selectToArray('tag-view', ['type', 'name', 'url'], $condition); } + /** + * Return a string with all tags and mentions + * + * @param integer $uri_id + * @return string tags and mentions + */ + public static function tagTextFromItemId(int $uri_id) + { + $tag_list = []; + $tags = self::getByURIId($uri_id); + foreach ($tags as $tag) { + $tag_list[] = self::TAG_CHARACTER[$tag['type']] . '[url=' . $tag['url'] . ']' . $tag['name'] . '[/url]'; + } + + return implode(',', $tag_list); + } + /** * Sorts an item's tags into mentions, hashtags and other tags. Generate personalized URLs by user and modify the * provided item's body with them. From 0f042b064a4bdbf2937b24607ba99b99ac02d052 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 5 May 2020 21:49:48 +0000 Subject: [PATCH 25/28] Fix warning, notice and DB Error --- src/Database/PostUpdate.php | 2 +- src/Protocol/OStatus.php | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Database/PostUpdate.php b/src/Database/PostUpdate.php index 514956b22..82cc07c6d 100644 --- a/src/Database/PostUpdate.php +++ b/src/Database/PostUpdate.php @@ -707,7 +707,7 @@ class PostUpdate while ($delivery = DBA::fetch($deliveries)) { $id = $delivery['iid']; unset($delivery['iid']); - DBA::insert('post-delivery-data', $delivery); + DBA::insert('post-delivery-data', $delivery, true); ++$rows; } DBA::close($deliveries); diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index 9b75e5d12..07465c522 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -2073,7 +2073,8 @@ class OStatus } // uri-id isn't present for follow entry pseudo-items - foreach (Tag::getByURIId($item['uri-id'] ?? 0) as $tag) { + $tags = Tag::getByURIId($item['uri-id'] ?? 0); + foreach ($tags as $tag) { $mentioned[$tag['url']] = $tag['url']; } @@ -2123,11 +2124,9 @@ class OStatus XML::addElement($doc, $entry, "mastodon:scope", "public"); } - if (count($tags)) { - foreach ($tags as $tag) { - if ($tag['type'] == Tag::HASHTAG) { - XML::addElement($doc, $entry, "category", "", ["term" => $tag['name']]); - } + foreach ($tags as $tag) { + if ($tag['type'] == Tag::HASHTAG) { + XML::addElement($doc, $entry, "category", "", ["term" => $tag['name']]); } } From d33f6fc1677580dc80ee638d5130b491ab15701a Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 5 May 2020 21:58:25 +0000 Subject: [PATCH 26/28] Renamed function --- mod/photos.php | 2 +- mod/tagrm.php | 2 +- src/Model/Tag.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mod/photos.php b/mod/photos.php index f4983a838..261017490 100644 --- a/mod/photos.php +++ b/mod/photos.php @@ -1307,7 +1307,7 @@ function photos_content(App $a) } $tags = null; - $tag_text = Tag::tagTextFromItemId($link_item['uri-id']); + $tag_text = Tag::getCSVByURIId($link_item['uri-id']); if (!empty($link_item['id']) && !empty($tag_text)) { $arr = explode(',', $tag_text); diff --git a/mod/tagrm.php b/mod/tagrm.php index 3aefe0680..4022f999d 100644 --- a/mod/tagrm.php +++ b/mod/tagrm.php @@ -102,7 +102,7 @@ function tagrm_content(App $a) DI::baseUrl()->redirect($_SESSION['photo_return']); } - $tag_text = Tag::tagTextFromItemId($item['uri-id']); + $tag_text = Tag::getCSVByURIId($item['uri-id']); $arr = explode(',', $tag_text); diff --git a/src/Model/Tag.php b/src/Model/Tag.php index f21d2ffec..2f4628972 100644 --- a/src/Model/Tag.php +++ b/src/Model/Tag.php @@ -345,7 +345,7 @@ class Tag * @param integer $uri_id * @return string tags and mentions */ - public static function tagTextFromItemId(int $uri_id) + public static function getCSVByURIId(int $uri_id) { $tag_list = []; $tags = self::getByURIId($uri_id); From 36a7eceb897ff3201cb28fcfb3b2f8802a7188b5 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Tue, 5 May 2020 23:41:35 -0400 Subject: [PATCH 27/28] Remove preemptive autolinker from Text\Markdown::toBBCode - Autolinker is done centrally in Text\BBCode::convert --- src/Content/Text/Markdown.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Content/Text/Markdown.php b/src/Content/Text/Markdown.php index f5ad85e80..71437fb35 100644 --- a/src/Content/Text/Markdown.php +++ b/src/Content/Text/Markdown.php @@ -122,9 +122,6 @@ class Markdown // protect the recycle symbol from turning into a tag, but without unescaping angles and naked ampersands $s = str_replace('♲', html_entity_decode('♲', ENT_QUOTES, 'UTF-8'), $s); - // Convert everything that looks like a link to a link - $s = preg_replace('/([^\]=]|^)(https?\:\/\/)([a-zA-Z0-9:\/\-?&;.=_~#%$!+,@]+(? Date: Wed, 6 May 2020 04:58:25 +0000 Subject: [PATCH 28/28] fix a fatal error when displaying photos --- mod/photos.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mod/photos.php b/mod/photos.php index 261017490..311d2b1c1 100644 --- a/mod/photos.php +++ b/mod/photos.php @@ -1307,9 +1307,9 @@ function photos_content(App $a) } $tags = null; - $tag_text = Tag::getCSVByURIId($link_item['uri-id']); - if (!empty($link_item['id']) && !empty($tag_text)) { + if (!empty($link_item['id'])) { + $tag_text = Tag::getCSVByURIId($link_item['uri-id']); $arr = explode(',', $tag_text); // parse tags and add links $tag_arr = [];