Commit fb724c0b7f08e51d20d11ba1f06c9b4b2bdab75c

Authored by renz78
1 parent 94c48719ea
Exists in master

test

Showing 9 changed files with 3795 additions and 0 deletions Side-by-side Diff

k2shop/usr/k2shop/libs/browser/Browser.php
Changes suppressed. Click to show
  1 +<?php
  2 +
  3 +/**
  4 + * File: Browser.php
  5 + * Author: Chris Schuld (http://chrisschuld.com/)
  6 + * Last Modified: July 22nd, 2016
  7 + * @version 2.0
  8 + * @package PegasusPHP
  9 + *
  10 + * Copyright (C) 2008-2010 Chris Schuld (chris@chrisschuld.com)
  11 + *
  12 + * This program is free software; you can redistribute it and/or
  13 + * modify it under the terms of the GNU General Public License as
  14 + * published by the Free Software Foundation; either version 2 of
  15 + * the License, or (at your option) any later version.
  16 + *
  17 + * This program is distributed in the hope that it will be useful,
  18 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20 + * GNU General Public License for more details at:
  21 + * http://www.gnu.org/copyleft/gpl.html
  22 + *
  23 + *
  24 + * Typical Usage:
  25 + *
  26 + * $browser = new Browser();
  27 + * if( $browser->getBrowser() == Browser::BROWSER_FIREFOX && $browser->getVersion() >= 2 ) {
  28 + * echo 'You have FireFox version 2 or greater';
  29 + * }
  30 + *
  31 + * User Agents Sampled from: http://www.useragentstring.com/
  32 + *
  33 + * This implementation is based on the original work from Gary White
  34 + * http://apptools.com/phptools/browser/
  35 + *
  36 + */
  37 +class Browser
  38 +{
  39 + private $_agent = '';
  40 + private $_browser_name = '';
  41 + private $_version = '';
  42 + private $_platform = '';
  43 + private $_os = '';
  44 + private $_is_aol = false;
  45 + private $_is_mobile = false;
  46 + private $_is_tablet = false;
  47 + private $_is_robot = false;
  48 + private $_is_facebook = false;
  49 + private $_aol_version = '';
  50 +
  51 + const BROWSER_UNKNOWN = 'unknown';
  52 + const VERSION_UNKNOWN = 'unknown';
  53 +
  54 + const BROWSER_OPERA = 'Opera'; // http://www.opera.com/
  55 + const BROWSER_OPERA_MINI = 'Opera Mini'; // http://www.opera.com/mini/
  56 + const BROWSER_WEBTV = 'WebTV'; // http://www.webtv.net/pc/
  57 + const BROWSER_EDGE = 'Edge'; // https://www.microsoft.com/edge
  58 + const BROWSER_IE = 'Internet Explorer'; // http://www.microsoft.com/ie/
  59 + const BROWSER_POCKET_IE = 'Pocket Internet Explorer'; // http://en.wikipedia.org/wiki/Internet_Explorer_Mobile
  60 + const BROWSER_KONQUEROR = 'Konqueror'; // http://www.konqueror.org/
  61 + const BROWSER_ICAB = 'iCab'; // http://www.icab.de/
  62 + const BROWSER_OMNIWEB = 'OmniWeb'; // http://www.omnigroup.com/applications/omniweb/
  63 + const BROWSER_FIREBIRD = 'Firebird'; // http://www.ibphoenix.com/
  64 + const BROWSER_FIREFOX = 'Firefox'; // http://www.mozilla.com/en-US/firefox/firefox.html
  65 + const BROWSER_ICEWEASEL = 'Iceweasel'; // http://www.geticeweasel.org/
  66 + const BROWSER_SHIRETOKO = 'Shiretoko'; // http://wiki.mozilla.org/Projects/shiretoko
  67 + const BROWSER_MOZILLA = 'Mozilla'; // http://www.mozilla.com/en-US/
  68 + const BROWSER_AMAYA = 'Amaya'; // http://www.w3.org/Amaya/
  69 + const BROWSER_LYNX = 'Lynx'; // http://en.wikipedia.org/wiki/Lynx
  70 + const BROWSER_SAFARI = 'Safari'; // http://apple.com
  71 + const BROWSER_IPHONE = 'iPhone'; // http://apple.com
  72 + const BROWSER_IPOD = 'iPod'; // http://apple.com
  73 + const BROWSER_IPAD = 'iPad'; // http://apple.com
  74 + const BROWSER_CHROME = 'Chrome'; // http://www.google.com/chrome
  75 + const BROWSER_ANDROID = 'Android'; // http://www.android.com/
  76 + const BROWSER_GOOGLEBOT = 'GoogleBot'; // http://en.wikipedia.org/wiki/Googlebot
  77 +
  78 + const BROWSER_YANDEXBOT = 'YandexBot'; // http://yandex.com/bots
  79 + const BROWSER_YANDEXIMAGERESIZER_BOT = 'YandexImageResizer'; // http://yandex.com/bots
  80 + const BROWSER_YANDEXIMAGES_BOT = 'YandexImages'; // http://yandex.com/bots
  81 + const BROWSER_YANDEXVIDEO_BOT = 'YandexVideo'; // http://yandex.com/bots
  82 + const BROWSER_YANDEXMEDIA_BOT = 'YandexMedia'; // http://yandex.com/bots
  83 + const BROWSER_YANDEXBLOGS_BOT = 'YandexBlogs'; // http://yandex.com/bots
  84 + const BROWSER_YANDEXFAVICONS_BOT = 'YandexFavicons'; // http://yandex.com/bots
  85 + const BROWSER_YANDEXWEBMASTER_BOT = 'YandexWebmaster'; // http://yandex.com/bots
  86 + const BROWSER_YANDEXDIRECT_BOT = 'YandexDirect'; // http://yandex.com/bots
  87 + const BROWSER_YANDEXMETRIKA_BOT = 'YandexMetrika'; // http://yandex.com/bots
  88 + const BROWSER_YANDEXNEWS_BOT = 'YandexNews'; // http://yandex.com/bots
  89 + const BROWSER_YANDEXCATALOG_BOT = 'YandexCatalog'; // http://yandex.com/bots
  90 +
  91 + const BROWSER_SLURP = 'Yahoo! Slurp'; // http://en.wikipedia.org/wiki/Yahoo!_Slurp
  92 + const BROWSER_W3CVALIDATOR = 'W3C Validator'; // http://validator.w3.org/
  93 + const BROWSER_BLACKBERRY = 'BlackBerry'; // http://www.blackberry.com/
  94 + const BROWSER_ICECAT = 'IceCat'; // http://en.wikipedia.org/wiki/GNU_IceCat
  95 + const BROWSER_NOKIA_S60 = 'Nokia S60 OSS Browser'; // http://en.wikipedia.org/wiki/Web_Browser_for_S60
  96 + const BROWSER_NOKIA = 'Nokia Browser'; // * all other WAP-based browsers on the Nokia Platform
  97 + const BROWSER_MSN = 'MSN Browser'; // http://explorer.msn.com/
  98 + const BROWSER_MSNBOT = 'MSN Bot'; // http://search.msn.com/msnbot.htm
  99 + const BROWSER_BINGBOT = 'Bing Bot'; // http://en.wikipedia.org/wiki/Bingbot
  100 + const BROWSER_VIVALDI = 'Vivalidi'; // https://vivaldi.com/
  101 + const BROWSER_YANDEX = 'Yandex'; // https://browser.yandex.ua/
  102 +
  103 + const BROWSER_NETSCAPE_NAVIGATOR = 'Netscape Navigator'; // http://browser.netscape.com/ (DEPRECATED)
  104 + const BROWSER_GALEON = 'Galeon'; // http://galeon.sourceforge.net/ (DEPRECATED)
  105 + const BROWSER_NETPOSITIVE = 'NetPositive'; // http://en.wikipedia.org/wiki/NetPositive (DEPRECATED)
  106 + const BROWSER_PHOENIX = 'Phoenix'; // http://en.wikipedia.org/wiki/History_of_Mozilla_Firefox (DEPRECATED)
  107 + const BROWSER_PLAYSTATION = "PlayStation";
  108 + const BROWSER_SAMSUNG = "SamsungBrowser";
  109 + const BROWSER_SILK = "Silk";
  110 + const BROWSER_I_FRAME = "Iframely";
  111 + const BROWSER_COCOA = "CocoaRestClient";
  112 +
  113 + const PLATFORM_UNKNOWN = 'unknown';
  114 + const PLATFORM_WINDOWS = 'Windows';
  115 + const PLATFORM_WINDOWS_CE = 'Windows CE';
  116 + const PLATFORM_APPLE = 'Apple';
  117 + const PLATFORM_LINUX = 'Linux';
  118 + const PLATFORM_OS2 = 'OS/2';
  119 + const PLATFORM_BEOS = 'BeOS';
  120 + const PLATFORM_IPHONE = 'iPhone';
  121 + const PLATFORM_IPOD = 'iPod';
  122 + const PLATFORM_IPAD = 'iPad';
  123 + const PLATFORM_BLACKBERRY = 'BlackBerry';
  124 + const PLATFORM_NOKIA = 'Nokia';
  125 + const PLATFORM_FREEBSD = 'FreeBSD';
  126 + const PLATFORM_OPENBSD = 'OpenBSD';
  127 + const PLATFORM_NETBSD = 'NetBSD';
  128 + const PLATFORM_SUNOS = 'SunOS';
  129 + const PLATFORM_OPENSOLARIS = 'OpenSolaris';
  130 + const PLATFORM_ANDROID = 'Android';
  131 + const PLATFORM_PLAYSTATION = "Sony PlayStation";
  132 + const PLATFORM_ROKU = "Roku";
  133 + const PLATFORM_APPLE_TV = "Apple TV";
  134 + const PLATFORM_TERMINAL = "Terminal";
  135 + const PLATFORM_FIRE_OS = "Fire OS";
  136 + const PLATFORM_SMART_TV = "SMART-TV";
  137 + const PLATFORM_CHROME_OS = "Chrome OS";
  138 + const PLATFORM_JAVA_ANDROID = "Java/Android";
  139 + const PLATFORM_POSTMAN = "Postman";
  140 + const PLATFORM_I_FRAME = "Iframely";
  141 +
  142 + const OPERATING_SYSTEM_UNKNOWN = 'unknown';
  143 +
  144 + /**
  145 + * Class constructor
  146 + */
  147 + public function __construct($userAgent = "")
  148 + {
  149 + $this->reset();
  150 + if ($userAgent != "") {
  151 + $this->setUserAgent($userAgent);
  152 + } else {
  153 + $this->determine();
  154 + }
  155 + }
  156 +
  157 + /**
  158 + * Reset all properties
  159 + */
  160 + public function reset()
  161 + {
  162 + $this->_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : "";
  163 + $this->_browser_name = self::BROWSER_UNKNOWN;
  164 + $this->_version = self::VERSION_UNKNOWN;
  165 + $this->_platform = self::PLATFORM_UNKNOWN;
  166 + $this->_os = self::OPERATING_SYSTEM_UNKNOWN;
  167 + $this->_is_aol = false;
  168 + $this->_is_mobile = false;
  169 + $this->_is_tablet = false;
  170 + $this->_is_robot = false;
  171 + $this->_is_facebook = false;
  172 + $this->_aol_version = self::VERSION_UNKNOWN;
  173 + }
  174 +
  175 + /**
  176 + * Check to see if the specific browser is valid
  177 + * @param string $browserName
  178 + * @return bool True if the browser is the specified browser
  179 + */
  180 + function isBrowser($browserName)
  181 + {
  182 + return (0 == strcasecmp($this->_browser_name, trim($browserName)));
  183 + }
  184 +
  185 + /**
  186 + * The name of the browser. All return types are from the class contants
  187 + * @return string Name of the browser
  188 + */
  189 + public function getBrowser()
  190 + {
  191 + return $this->_browser_name;
  192 + }
  193 +
  194 + /**
  195 + * Set the name of the browser
  196 + * @param $browser string The name of the Browser
  197 + */
  198 + public function setBrowser($browser)
  199 + {
  200 + $this->_browser_name = $browser;
  201 + }
  202 +
  203 + /**
  204 + * The name of the platform. All return types are from the class contants
  205 + * @return string Name of the browser
  206 + */
  207 + public function getPlatform()
  208 + {
  209 + return $this->_platform;
  210 + }
  211 +
  212 + /**
  213 + * Set the name of the platform
  214 + * @param string $platform The name of the Platform
  215 + */
  216 + public function setPlatform($platform)
  217 + {
  218 + $this->_platform = $platform;
  219 + }
  220 +
  221 + /**
  222 + * The version of the browser.
  223 + * @return string Version of the browser (will only contain alpha-numeric characters and a period)
  224 + */
  225 + public function getVersion()
  226 + {
  227 + return $this->_version;
  228 + }
  229 +
  230 + /**
  231 + * Set the version of the browser
  232 + * @param string $version The version of the Browser
  233 + */
  234 + public function setVersion($version)
  235 + {
  236 + $this->_version = preg_replace('/[^0-9,.,a-z,A-Z-]/', '', $version);
  237 + }
  238 +
  239 + /**
  240 + * The version of AOL.
  241 + * @return string Version of AOL (will only contain alpha-numeric characters and a period)
  242 + */
  243 + public function getAolVersion()
  244 + {
  245 + return $this->_aol_version;
  246 + }
  247 +
  248 + /**
  249 + * Set the version of AOL
  250 + * @param string $version The version of AOL
  251 + */
  252 + public function setAolVersion($version)
  253 + {
  254 + $this->_aol_version = preg_replace('/[^0-9,.,a-z,A-Z]/', '', $version);
  255 + }
  256 +
  257 + /**
  258 + * Is the browser from AOL?
  259 + * @return boolean True if the browser is from AOL otherwise false
  260 + */
  261 + public function isAol()
  262 + {
  263 + return $this->_is_aol;
  264 + }
  265 +
  266 + /**
  267 + * Is the browser from a mobile device?
  268 + * @return boolean True if the browser is from a mobile device otherwise false
  269 + */
  270 + public function isMobile()
  271 + {
  272 + return $this->_is_mobile;
  273 + }
  274 +
  275 + /**
  276 + * Is the browser from a tablet device?
  277 + * @return boolean True if the browser is from a tablet device otherwise false
  278 + */
  279 + public function isTablet()
  280 + {
  281 + return $this->_is_tablet;
  282 + }
  283 +
  284 + /**
  285 + * Is the browser from a robot (ex Slurp,GoogleBot)?
  286 + * @return boolean True if the browser is from a robot otherwise false
  287 + */
  288 + public function isRobot()
  289 + {
  290 + return $this->_is_robot;
  291 + }
  292 +
  293 + /**
  294 + * Is the browser from facebook?
  295 + * @return boolean True if the browser is from facebook otherwise false
  296 + */
  297 + public function isFacebook()
  298 + {
  299 + return $this->_is_facebook;
  300 + }
  301 +
  302 + /**
  303 + * Set the browser to be from AOL
  304 + * @param $isAol
  305 + */
  306 + public function setAol($isAol)
  307 + {
  308 + $this->_is_aol = $isAol;
  309 + }
  310 +
  311 + /**
  312 + * Set the Browser to be mobile
  313 + * @param boolean $value is the browser a mobile browser or not
  314 + */
  315 + protected function setMobile($value = true)
  316 + {
  317 + $this->_is_mobile = $value;
  318 + }
  319 +
  320 + /**
  321 + * Set the Browser to be tablet
  322 + * @param boolean $value is the browser a tablet browser or not
  323 + */
  324 + protected function setTablet($value = true)
  325 + {
  326 + $this->_is_tablet = $value;
  327 + }
  328 +
  329 + /**
  330 + * Set the Browser to be a robot
  331 + * @param boolean $value is the browser a robot or not
  332 + */
  333 + protected function setRobot($value = true)
  334 + {
  335 + $this->_is_robot = $value;
  336 + }
  337 +
  338 + /**
  339 + * Set the Browser to be a Facebook request
  340 + * @param boolean $value is the browser a robot or not
  341 + */
  342 + protected function setFacebook($value = true)
  343 + {
  344 + $this->_is_facebook = $value;
  345 + }
  346 +
  347 + /**
  348 + * Get the user agent value in use to determine the browser
  349 + * @return string The user agent from the HTTP header
  350 + */
  351 + public function getUserAgent()
  352 + {
  353 + return $this->_agent;
  354 + }
  355 +
  356 + /**
  357 + * Set the user agent value (the construction will use the HTTP header value - this will overwrite it)
  358 + * @param string $agent_string The value for the User Agent
  359 + */
  360 + public function setUserAgent($agent_string)
  361 + {
  362 + $this->reset();
  363 + $this->_agent = $agent_string;
  364 + $this->determine();
  365 + }
  366 +
  367 + /**
  368 + * Used to determine if the browser is actually "chromeframe"
  369 + * @since 1.7
  370 + * @return boolean True if the browser is using chromeframe
  371 + */
  372 + public function isChromeFrame()
  373 + {
  374 + return (strpos($this->_agent, "chromeframe") !== false);
  375 + }
  376 +
  377 + /**
  378 + * Returns a formatted string with a summary of the details of the browser.
  379 + * @return string formatted string with a summary of the browser
  380 + */
  381 + public function __toString()
  382 + {
  383 + return "<strong>Browser Name:</strong> {$this->getBrowser()}<br/>\n" .
  384 + "<strong>Browser Version:</strong> {$this->getVersion()}<br/>\n" .
  385 + "<strong>Browser User Agent String:</strong> {$this->getUserAgent()}<br/>\n" .
  386 + "<strong>Platform:</strong> {$this->getPlatform()}<br/>";
  387 + }
  388 +
  389 + /**
  390 + * Protected routine to calculate and determine what the browser is in use (including platform)
  391 + */
  392 + protected function determine()
  393 + {
  394 + $this->checkPlatform();
  395 + $this->checkBrowsers();
  396 + $this->checkForAol();
  397 + }
  398 +
  399 + /**
  400 + * Protected routine to determine the browser type
  401 + * @return boolean True if the browser was detected otherwise false
  402 + */
  403 + protected function checkBrowsers()
  404 + {
  405 + return (
  406 + // well-known, well-used
  407 + // Special Notes:
  408 + // (1) Opera must be checked before FireFox due to the odd
  409 + // user agents used in some older versions of Opera
  410 + // (2) WebTV is strapped onto Internet Explorer so we must
  411 + // check for WebTV before IE
  412 + // (3) (deprecated) Galeon is based on Firefox and needs to be
  413 + // tested before Firefox is tested
  414 + // (4) OmniWeb is based on Safari so OmniWeb check must occur
  415 + // before Safari
  416 + // (5) Netscape 9+ is based on Firefox so Netscape checks
  417 + // before FireFox are necessary
  418 + // (6) Vivalid is UA contains both Firefox and Chrome so Vivalid checks
  419 + // before Firefox and Chrome
  420 + $this->checkBrowserWebTv() ||
  421 + $this->checkBrowserEdge() ||
  422 + $this->checkBrowserInternetExplorer() ||
  423 + $this->checkBrowserOpera() ||
  424 + $this->checkBrowserGaleon() ||
  425 + $this->checkBrowserNetscapeNavigator9Plus() ||
  426 + $this->checkBrowserVivaldi() ||
  427 + $this->checkBrowserYandex() ||
  428 + $this->checkBrowserFirefox() ||
  429 + $this->checkBrowserChrome() ||
  430 + $this->checkBrowserOmniWeb() ||
  431 +
  432 + // common mobile
  433 + $this->checkBrowserAndroid() ||
  434 + $this->checkBrowseriPad() ||
  435 + $this->checkBrowseriPod() ||
  436 + $this->checkBrowseriPhone() ||
  437 + $this->checkBrowserBlackBerry() ||
  438 + $this->checkBrowserNokia() ||
  439 +
  440 + // common bots
  441 + $this->checkBrowserGoogleBot() ||
  442 + $this->checkBrowserMSNBot() ||
  443 + $this->checkBrowserBingBot() ||
  444 + $this->checkBrowserSlurp() ||
  445 +
  446 + // Yandex bots
  447 + $this->checkBrowserYandexBot() ||
  448 + $this->checkBrowserYandexImageResizerBot() ||
  449 + $this->checkBrowserYandexBlogsBot() ||
  450 + $this->checkBrowserYandexCatalogBot() ||
  451 + $this->checkBrowserYandexDirectBot() ||
  452 + $this->checkBrowserYandexFaviconsBot() ||
  453 + $this->checkBrowserYandexImagesBot() ||
  454 + $this->checkBrowserYandexMediaBot() ||
  455 + $this->checkBrowserYandexMetrikaBot() ||
  456 + $this->checkBrowserYandexNewsBot() ||
  457 + $this->checkBrowserYandexVideoBot() ||
  458 + $this->checkBrowserYandexWebmasterBot() ||
  459 +
  460 + // check for facebook external hit when loading URL
  461 + $this->checkFacebookExternalHit() ||
  462 +
  463 + // WebKit base check (post mobile and others)
  464 + $this->checkBrowserSamsung() ||
  465 + $this->checkBrowserSilk() ||
  466 + $this->checkBrowserSafari() ||
  467 +
  468 + // everyone else
  469 + $this->checkBrowserNetPositive() ||
  470 + $this->checkBrowserFirebird() ||
  471 + $this->checkBrowserKonqueror() ||
  472 + $this->checkBrowserIcab() ||
  473 + $this->checkBrowserPhoenix() ||
  474 + $this->checkBrowserAmaya() ||
  475 + $this->checkBrowserLynx() ||
  476 + $this->checkBrowserShiretoko() ||
  477 + $this->checkBrowserIceCat() ||
  478 + $this->checkBrowserIceweasel() ||
  479 + $this->checkBrowserW3CValidator() ||
  480 + $this->checkBrowserPlayStation() ||
  481 + $this->checkBrowserIframely() ||
  482 + $this->checkBrowserCocoa() ||
  483 + $this->checkBrowserMozilla() /* Mozilla is such an open standard that you must check it last */
  484 +
  485 +
  486 + );
  487 + }
  488 +
  489 + /**
  490 + * Determine if the user is using a BlackBerry (last updated 1.7)
  491 + * @return boolean True if the browser is the BlackBerry browser otherwise false
  492 + */
  493 + protected function checkBrowserBlackBerry()
  494 + {
  495 + if (stripos($this->_agent, 'blackberry') !== false) {
  496 + $aresult = explode("/", stristr($this->_agent, "BlackBerry"));
  497 + if (isset($aresult[1])) {
  498 + $aversion = explode(' ', $aresult[1]);
  499 + $this->setVersion($aversion[0]);
  500 + $this->_browser_name = self::BROWSER_BLACKBERRY;
  501 + $this->setMobile(true);
  502 + return true;
  503 + }
  504 + }
  505 + return false;
  506 + }
  507 +
  508 + /**
  509 + * Determine if the user is using an AOL User Agent (last updated 1.7)
  510 + * @return boolean True if the browser is from AOL otherwise false
  511 + */
  512 + protected function checkForAol()
  513 + {
  514 + $this->setAol(false);
  515 + $this->setAolVersion(self::VERSION_UNKNOWN);
  516 +
  517 + if (stripos($this->_agent, 'aol') !== false) {
  518 + $aversion = explode(' ', stristr($this->_agent, 'AOL'));
  519 + if (isset($aversion[1])) {
  520 + $this->setAol(true);
  521 + $this->setAolVersion(preg_replace('/[^0-9\.a-z]/i', '', $aversion[1]));
  522 + return true;
  523 + }
  524 + }
  525 + return false;
  526 + }
  527 +
  528 + /**
  529 + * Determine if the browser is the GoogleBot or not (last updated 1.7)
  530 + * @return boolean True if the browser is the GoogletBot otherwise false
  531 + */
  532 + protected function checkBrowserGoogleBot()
  533 + {
  534 + if (stripos($this->_agent, 'googlebot') !== false) {
  535 + $aresult = explode('/', stristr($this->_agent, 'googlebot'));
  536 + if (isset($aresult[1])) {
  537 + $aversion = explode(' ', $aresult[1]);
  538 + $this->setVersion(str_replace(';', '', $aversion[0]));
  539 + $this->_browser_name = self::BROWSER_GOOGLEBOT;
  540 + $this->setRobot(true);
  541 + return true;
  542 + }
  543 + }
  544 + return false;
  545 + }
  546 +
  547 + /**
  548 + * Determine if the browser is the YandexBot or not
  549 + * @return boolean True if the browser is the YandexBot otherwise false
  550 + */
  551 + protected function checkBrowserYandexBot()
  552 + {
  553 + if (stripos($this->_agent, 'YandexBot') !== false) {
  554 + $aresult = explode('/', stristr($this->_agent, 'YandexBot'));
  555 + if (isset($aresult[1])) {
  556 + $aversion = explode(' ', $aresult[1]);
  557 + $this->setVersion(str_replace(';', '', $aversion[0]));
  558 + $this->_browser_name = self::BROWSER_YANDEXBOT;
  559 + $this->setRobot(true);
  560 + return true;
  561 + }
  562 + }
  563 + return false;
  564 + }
  565 +
  566 + /**
  567 + * Determine if the browser is the YandexImageResizer or not
  568 + * @return boolean True if the browser is the YandexImageResizer otherwise false
  569 + */
  570 + protected function checkBrowserYandexImageResizerBot()
  571 + {
  572 + if (stripos($this->_agent, 'YandexImageResizer') !== false) {
  573 + $aresult = explode('/', stristr($this->_agent, 'YandexImageResizer'));
  574 + if (isset($aresult[1])) {
  575 + $aversion = explode(' ', $aresult[1]);
  576 + $this->setVersion(str_replace(';', '', $aversion[0]));
  577 + $this->_browser_name = self::BROWSER_YANDEXIMAGERESIZER_BOT;
  578 + $this->setRobot(true);
  579 + return true;
  580 + }
  581 + }
  582 + return false;
  583 + }
  584 +
  585 + /**
  586 + * Determine if the browser is the YandexCatalog or not
  587 + * @return boolean True if the browser is the YandexCatalog otherwise false
  588 + */
  589 + protected function checkBrowserYandexCatalogBot()
  590 + {
  591 + if (stripos($this->_agent, 'YandexCatalog') !== false) {
  592 + $aresult = explode('/', stristr($this->_agent, 'YandexCatalog'));
  593 + if (isset($aresult[1])) {
  594 + $aversion = explode(' ', $aresult[1]);
  595 + $this->setVersion(str_replace(';', '', $aversion[0]));
  596 + $this->_browser_name = self::BROWSER_YANDEXCATALOG_BOT;
  597 + $this->setRobot(true);
  598 + return true;
  599 + }
  600 + }
  601 + return false;
  602 + }
  603 +
  604 + /**
  605 + * Determine if the browser is the YandexNews or not
  606 + * @return boolean True if the browser is the YandexNews otherwise false
  607 + */
  608 + protected function checkBrowserYandexNewsBot()
  609 + {
  610 + if (stripos($this->_agent, 'YandexNews') !== false) {
  611 + $aresult = explode('/', stristr($this->_agent, 'YandexNews'));
  612 + if (isset($aresult[1])) {
  613 + $aversion = explode(' ', $aresult[1]);
  614 + $this->setVersion(str_replace(';', '', $aversion[0]));
  615 + $this->_browser_name = self::BROWSER_YANDEXNEWS_BOT;
  616 + $this->setRobot(true);
  617 + return true;
  618 + }
  619 + }
  620 + return false;
  621 + }
  622 +
  623 + /**
  624 + * Determine if the browser is the YandexMetrika or not
  625 + * @return boolean True if the browser is the YandexMetrika otherwise false
  626 + */
  627 + protected function checkBrowserYandexMetrikaBot()
  628 + {
  629 + if (stripos($this->_agent, 'YandexMetrika') !== false) {
  630 + $aresult = explode('/', stristr($this->_agent, 'YandexMetrika'));
  631 + if (isset($aresult[1])) {
  632 + $aversion = explode(' ', $aresult[1]);
  633 + $this->setVersion(str_replace(';', '', $aversion[0]));
  634 + $this->_browser_name = self::BROWSER_YANDEXMETRIKA_BOT;
  635 + $this->setRobot(true);
  636 + return true;
  637 + }
  638 + }
  639 + return false;
  640 + }
  641 +
  642 + /**
  643 + * Determine if the browser is the YandexDirect or not
  644 + * @return boolean True if the browser is the YandexDirect otherwise false
  645 + */
  646 + protected function checkBrowserYandexDirectBot()
  647 + {
  648 + if (stripos($this->_agent, 'YandexDirect') !== false) {
  649 + $aresult = explode('/', stristr($this->_agent, 'YandexDirect'));
  650 + if (isset($aresult[1])) {
  651 + $aversion = explode(' ', $aresult[1]);
  652 + $this->setVersion(str_replace(';', '', $aversion[0]));
  653 + $this->_browser_name = self::BROWSER_YANDEXDIRECT_BOT;
  654 + $this->setRobot(true);
  655 + return true;
  656 + }
  657 + }
  658 + return false;
  659 + }
  660 +
  661 + /**
  662 + * Determine if the browser is the YandexWebmaster or not
  663 + * @return boolean True if the browser is the YandexWebmaster otherwise false
  664 + */
  665 + protected function checkBrowserYandexWebmasterBot()
  666 + {
  667 + if (stripos($this->_agent, 'YandexWebmaster') !== false) {
  668 + $aresult = explode('/', stristr($this->_agent, 'YandexWebmaster'));
  669 + if (isset($aresult[1])) {
  670 + $aversion = explode(' ', $aresult[1]);
  671 + $this->setVersion(str_replace(';', '', $aversion[0]));
  672 + $this->_browser_name = self::BROWSER_YANDEXWEBMASTER_BOT;
  673 + $this->setRobot(true);
  674 + return true;
  675 + }
  676 + }
  677 + return false;
  678 + }
  679 +
  680 + /**
  681 + * Determine if the browser is the YandexFavicons or not
  682 + * @return boolean True if the browser is the YandexFavicons otherwise false
  683 + */
  684 + protected function checkBrowserYandexFaviconsBot()
  685 + {
  686 + if (stripos($this->_agent, 'YandexFavicons') !== false) {
  687 + $aresult = explode('/', stristr($this->_agent, 'YandexFavicons'));
  688 + if (isset($aresult[1])) {
  689 + $aversion = explode(' ', $aresult[1]);
  690 + $this->setVersion(str_replace(';', '', $aversion[0]));
  691 + $this->_browser_name = self::BROWSER_YANDEXFAVICONS_BOT;
  692 + $this->setRobot(true);
  693 + return true;
  694 + }
  695 + }
  696 + return false;
  697 + }
  698 +
  699 + /**
  700 + * Determine if the browser is the YandexBlogs or not
  701 + * @return boolean True if the browser is the YandexBlogs otherwise false
  702 + */
  703 + protected function checkBrowserYandexBlogsBot()
  704 + {
  705 + if (stripos($this->_agent, 'YandexBlogs') !== false) {
  706 + $aresult = explode('/', stristr($this->_agent, 'YandexBlogs'));
  707 + if (isset($aresult[1])) {
  708 + $aversion = explode(' ', $aresult[1]);
  709 + $this->setVersion(str_replace(';', '', $aversion[0]));
  710 + $this->_browser_name = self::BROWSER_YANDEXBLOGS_BOT;
  711 + $this->setRobot(true);
  712 + return true;
  713 + }
  714 + }
  715 + return false;
  716 + }
  717 +
  718 + /**
  719 + * Determine if the browser is the YandexMedia or not
  720 + * @return boolean True if the browser is the YandexMedia otherwise false
  721 + */
  722 + protected function checkBrowserYandexMediaBot()
  723 + {
  724 + if (stripos($this->_agent, 'YandexMedia') !== false) {
  725 + $aresult = explode('/', stristr($this->_agent, 'YandexMedia'));
  726 + if (isset($aresult[1])) {
  727 + $aversion = explode(' ', $aresult[1]);
  728 + $this->setVersion(str_replace(';', '', $aversion[0]));
  729 + $this->_browser_name = self::BROWSER_YANDEXMEDIA_BOT;
  730 + $this->setRobot(true);
  731 + return true;
  732 + }
  733 + }
  734 + return false;
  735 + }
  736 +
  737 + /**
  738 + * Determine if the browser is the YandexVideo or not
  739 + * @return boolean True if the browser is the YandexVideo otherwise false
  740 + */
  741 + protected function checkBrowserYandexVideoBot()
  742 + {
  743 + if (stripos($this->_agent, 'YandexVideo') !== false) {
  744 + $aresult = explode('/', stristr($this->_agent, 'YandexVideo'));
  745 + if (isset($aresult[1])) {
  746 + $aversion = explode(' ', $aresult[1]);
  747 + $this->setVersion(str_replace(';', '', $aversion[0]));
  748 + $this->_browser_name = self::BROWSER_YANDEXVIDEO_BOT;
  749 + $this->setRobot(true);
  750 + return true;
  751 + }
  752 + }
  753 + return false;
  754 + }
  755 +
  756 + /**
  757 + * Determine if the browser is the YandexImages or not
  758 + * @return boolean True if the browser is the YandexImages otherwise false
  759 + */
  760 + protected function checkBrowserYandexImagesBot()
  761 + {
  762 + if (stripos($this->_agent, 'YandexImages') !== false) {
  763 + $aresult = explode('/', stristr($this->_agent, 'YandexImages'));
  764 + if (isset($aresult[1])) {
  765 + $aversion = explode(' ', $aresult[1]);
  766 + $this->setVersion(str_replace(';', '', $aversion[0]));
  767 + $this->_browser_name = self::BROWSER_YANDEXIMAGES_BOT;
  768 + $this->setRobot(true);
  769 + return true;
  770 + }
  771 + }
  772 + return false;
  773 + }
  774 +
  775 + /**
  776 + * Determine if the browser is the MSNBot or not (last updated 1.9)
  777 + * @return boolean True if the browser is the MSNBot otherwise false
  778 + */
  779 + protected function checkBrowserMSNBot()
  780 + {
  781 + if (stripos($this->_agent, "msnbot") !== false) {
  782 + $aresult = explode("/", stristr($this->_agent, "msnbot"));
  783 + if (isset($aresult[1])) {
  784 + $aversion = explode(" ", $aresult[1]);
  785 + $this->setVersion(str_replace(";", "", $aversion[0]));
  786 + $this->_browser_name = self::BROWSER_MSNBOT;
  787 + $this->setRobot(true);
  788 + return true;
  789 + }
  790 + }
  791 + return false;
  792 + }
  793 +
  794 + /**
  795 + * Determine if the browser is the BingBot or not (last updated 1.9)
  796 + * @return boolean True if the browser is the BingBot otherwise false
  797 + */
  798 + protected function checkBrowserBingBot()
  799 + {
  800 + if (stripos($this->_agent, "bingbot") !== false) {
  801 + $aresult = explode("/", stristr($this->_agent, "bingbot"));
  802 + if (isset($aresult[1])) {
  803 + $aversion = explode(" ", $aresult[1]);
  804 + $this->setVersion(str_replace(";", "", $aversion[0]));
  805 + $this->_browser_name = self::BROWSER_BINGBOT;
  806 + $this->setRobot(true);
  807 + return true;
  808 + }
  809 + }
  810 + return false;
  811 + }
  812 +
  813 + /**
  814 + * Determine if the browser is the W3C Validator or not (last updated 1.7)
  815 + * @return boolean True if the browser is the W3C Validator otherwise false
  816 + */
  817 + protected function checkBrowserW3CValidator()
  818 + {
  819 + if (stripos($this->_agent, 'W3C-checklink') !== false) {
  820 + $aresult = explode('/', stristr($this->_agent, 'W3C-checklink'));
  821 + if (isset($aresult[1])) {
  822 + $aversion = explode(' ', $aresult[1]);
  823 + $this->setVersion($aversion[0]);
  824 + $this->_browser_name = self::BROWSER_W3CVALIDATOR;
  825 + return true;
  826 + }
  827 + } else if (stripos($this->_agent, 'W3C_Validator') !== false) {
  828 + // Some of the Validator versions do not delineate w/ a slash - add it back in
  829 + $ua = str_replace("W3C_Validator ", "W3C_Validator/", $this->_agent);
  830 + $aresult = explode('/', stristr($ua, 'W3C_Validator'));
  831 + if (isset($aresult[1])) {
  832 + $aversion = explode(' ', $aresult[1]);
  833 + $this->setVersion($aversion[0]);
  834 + $this->_browser_name = self::BROWSER_W3CVALIDATOR;
  835 + return true;
  836 + }
  837 + } else if (stripos($this->_agent, 'W3C-mobileOK') !== false) {
  838 + $this->_browser_name = self::BROWSER_W3CVALIDATOR;
  839 + $this->setMobile(true);
  840 + return true;
  841 + }
  842 + return false;
  843 + }
  844 +
  845 + /**
  846 + * Determine if the browser is the Yahoo! Slurp Robot or not (last updated 1.7)
  847 + * @return boolean True if the browser is the Yahoo! Slurp Robot otherwise false
  848 + */
  849 + protected function checkBrowserSlurp()
  850 + {
  851 + if (stripos($this->_agent, 'slurp') !== false) {
  852 + $aresult = explode('/', stristr($this->_agent, 'Slurp'));
  853 + if (isset($aresult[1])) {
  854 + $aversion = explode(' ', $aresult[1]);
  855 + $this->setVersion($aversion[0]);
  856 + $this->_browser_name = self::BROWSER_SLURP;
  857 + $this->setRobot(true);
  858 + $this->setMobile(false);
  859 + return true;
  860 + }
  861 + }
  862 + return false;
  863 + }
  864 +
  865 + /**
  866 + * Determine if the browser is Edge or not
  867 + * @return boolean True if the browser is Edge otherwise false
  868 + */
  869 + protected function checkBrowserEdge()
  870 + {
  871 + if (stripos($this->_agent, 'Edge/') !== false) {
  872 + $aresult = explode('/', stristr($this->_agent, 'Edge'));
  873 + if (isset($aresult[1])) {
  874 + $aversion = explode(' ', $aresult[1]);
  875 + $this->setVersion($aversion[0]);
  876 + $this->setBrowser(self::BROWSER_EDGE);
  877 + if (stripos($this->_agent, 'Windows Phone') !== false || stripos($this->_agent, 'Android') !== false) {
  878 + $this->setMobile(true);
  879 + }
  880 + return true;
  881 + }
  882 + }
  883 + return false;
  884 + }
  885 +
  886 + /**
  887 + * Determine if the browser is Internet Explorer or not (last updated 1.7)
  888 + * @return boolean True if the browser is Internet Explorer otherwise false
  889 + */
  890 + protected function checkBrowserInternetExplorer()
  891 + {
  892 + // Test for IE11
  893 + if (stripos($this->_agent, 'Trident/7.0; rv:11.0') !== false) {
  894 + $this->setBrowser(self::BROWSER_IE);
  895 + $this->setVersion('11.0');
  896 + return true;
  897 + } // Test for v1 - v1.5 IE
  898 + else if (stripos($this->_agent, 'microsoft internet explorer') !== false) {
  899 + $this->setBrowser(self::BROWSER_IE);
  900 + $this->setVersion('1.0');
  901 + $aresult = stristr($this->_agent, '/');
  902 + if (preg_match('/308|425|426|474|0b1/i', $aresult)) {
  903 + $this->setVersion('1.5');
  904 + }
  905 + return true;
  906 + } // Test for versions > 1.5
  907 + else if (stripos($this->_agent, 'msie') !== false && stripos($this->_agent, 'opera') === false) {
  908 + // See if the browser is the odd MSN Explorer
  909 + if (stripos($this->_agent, 'msnb') !== false) {
  910 + $aresult = explode(' ', stristr(str_replace(';', '; ', $this->_agent), 'MSN'));
  911 + if (isset($aresult[1])) {
  912 + $this->setBrowser(self::BROWSER_MSN);
  913 + $this->setVersion(str_replace(array('(', ')', ';'), '', $aresult[1]));
  914 + return true;
  915 + }
  916 + }
  917 + $aresult = explode(' ', stristr(str_replace(';', '; ', $this->_agent), 'msie'));
  918 + if (isset($aresult[1])) {
  919 + $this->setBrowser(self::BROWSER_IE);
  920 + $this->setVersion(str_replace(array('(', ')', ';'), '', $aresult[1]));
  921 + if(preg_match('#trident/([0-9\.]+);#i', $this->_agent, $aresult)){
  922 + if($aresult[1] == '3.1'){
  923 + $this->setVersion('7.0');
  924 + }
  925 + else if($aresult[1] == '4.0'){
  926 + $this->setVersion('8.0');
  927 + }
  928 + else if($aresult[1] == '5.0'){
  929 + $this->setVersion('9.0');
  930 + }
  931 + else if($aresult[1] == '6.0'){
  932 + $this->setVersion('10.0');
  933 + }
  934 + else if($aresult[1] == '7.0'){
  935 + $this->setVersion('11.0');
  936 + }
  937 + else if($aresult[1] == '8.0'){
  938 + $this->setVersion('11.0');
  939 + }
  940 + }
  941 + if(stripos($this->_agent, 'IEMobile') !== false) {
  942 + $this->setBrowser(self::BROWSER_POCKET_IE);
  943 + $this->setMobile(true);
  944 + }
  945 + return true;
  946 + }
  947 + } // Test for versions > IE 10
  948 + else if (stripos($this->_agent, 'trident') !== false) {
  949 + $this->setBrowser(self::BROWSER_IE);
  950 + $result = explode('rv:', $this->_agent);
  951 + if (isset($result[1])) {
  952 + $this->setVersion(preg_replace('/[^0-9.]+/', '', $result[1]));
  953 + $this->_agent = str_replace(array("Mozilla", "Gecko"), "MSIE", $this->_agent);
  954 + }
  955 + } // Test for Pocket IE
  956 + else if (stripos($this->_agent, 'mspie') !== false || stripos($this->_agent, 'pocket') !== false) {
  957 + $aresult = explode(' ', stristr($this->_agent, 'mspie'));
  958 + if (isset($aresult[1])) {
  959 + $this->setPlatform(self::PLATFORM_WINDOWS_CE);
  960 + $this->setBrowser(self::BROWSER_POCKET_IE);
  961 + $this->setMobile(true);
  962 +
  963 + if (stripos($this->_agent, 'mspie') !== false) {
  964 + $this->setVersion($aresult[1]);
  965 + } else {
  966 + $aversion = explode('/', $this->_agent);
  967 + if (isset($aversion[1])) {
  968 + $this->setVersion($aversion[1]);
  969 + }
  970 + }
  971 + return true;
  972 + }
  973 + }
  974 + return false;
  975 + }
  976 +
  977 + /**
  978 + * Determine if the browser is Opera or not (last updated 1.7)
  979 + * @return boolean True if the browser is Opera otherwise false
  980 + */
  981 + protected function checkBrowserOpera()
  982 + {
  983 + if (stripos($this->_agent, 'opera mini') !== false) {
  984 + $resultant = stristr($this->_agent, 'opera mini');
  985 + if (preg_match('/\//', $resultant)) {
  986 + $aresult = explode('/', $resultant);
  987 + if (isset($aresult[1])) {
  988 + $aversion = explode(' ', $aresult[1]);
  989 + $this->setVersion($aversion[0]);
  990 + }
  991 + } else {
  992 + $aversion = explode(' ', stristr($resultant, 'opera mini'));
  993 + if (isset($aversion[1])) {
  994 + $this->setVersion($aversion[1]);
  995 + }
  996 + }
  997 + $this->_browser_name = self::BROWSER_OPERA_MINI;
  998 + $this->setMobile(true);
  999 + return true;
  1000 + } else if (stripos($this->_agent, 'opera') !== false) {
  1001 + $resultant = stristr($this->_agent, 'opera');
  1002 + if (preg_match('/Version\/(1*.*)$/', $resultant, $matches)) {
  1003 + $this->setVersion($matches[1]);
  1004 + } else if (preg_match('/\//', $resultant)) {
  1005 + $aresult = explode('/', str_replace("(", " ", $resultant));
  1006 + if (isset($aresult[1])) {
  1007 + $aversion = explode(' ', $aresult[1]);
  1008 + $this->setVersion($aversion[0]);
  1009 + }
  1010 + } else {
  1011 + $aversion = explode(' ', stristr($resultant, 'opera'));
  1012 + $this->setVersion(isset($aversion[1]) ? $aversion[1] : "");
  1013 + }
  1014 + if (stripos($this->_agent, 'Opera Mobi') !== false) {
  1015 + $this->setMobile(true);
  1016 + }
  1017 + $this->_browser_name = self::BROWSER_OPERA;
  1018 + return true;
  1019 + } else if (stripos($this->_agent, 'OPR') !== false) {
  1020 + $resultant = stristr($this->_agent, 'OPR');
  1021 + if (preg_match('/\//', $resultant)) {
  1022 + $aresult = explode('/', str_replace("(", " ", $resultant));
  1023 + if (isset($aresult[1])) {
  1024 + $aversion = explode(' ', $aresult[1]);
  1025 + $this->setVersion($aversion[0]);
  1026 + }
  1027 + }
  1028 + if (stripos($this->_agent, 'Mobile') !== false) {
  1029 + $this->setMobile(true);
  1030 + }
  1031 + $this->_browser_name = self::BROWSER_OPERA;
  1032 + return true;
  1033 + }
  1034 + return false;
  1035 + }
  1036 +
  1037 + /**
  1038 + * Determine if the browser is Chrome or not (last updated 1.7)
  1039 + * @return boolean True if the browser is Chrome otherwise false
  1040 + */
  1041 + protected function checkBrowserChrome()
  1042 + {
  1043 + if (stripos($this->_agent, 'Chrome') !== false) {
  1044 + $aresult = explode('/', stristr($this->_agent, 'Chrome'));
  1045 + if (isset($aresult[1])) {
  1046 + $aversion = explode(' ', $aresult[1]);
  1047 + $this->setVersion($aversion[0]);
  1048 + $this->setBrowser(self::BROWSER_CHROME);
  1049 + //Chrome on Android
  1050 + if (stripos($this->_agent, 'Android') !== false) {
  1051 + if (stripos($this->_agent, 'Mobile') !== false) {
  1052 + $this->setMobile(true);
  1053 + } else {
  1054 + $this->setTablet(true);
  1055 + }
  1056 + }
  1057 + return true;
  1058 + }
  1059 + }
  1060 + return false;
  1061 + }
  1062 +
  1063 +
  1064 + /**
  1065 + * Determine if the browser is WebTv or not (last updated 1.7)
  1066 + * @return boolean True if the browser is WebTv otherwise false
  1067 + */
  1068 + protected function checkBrowserWebTv()
  1069 + {
  1070 + if (stripos($this->_agent, 'webtv') !== false) {
  1071 + $aresult = explode('/', stristr($this->_agent, 'webtv'));
  1072 + if (isset($aresult[1])) {
  1073 + $aversion = explode(' ', $aresult[1]);
  1074 + $this->setVersion($aversion[0]);
  1075 + $this->setBrowser(self::BROWSER_WEBTV);
  1076 + return true;
  1077 + }
  1078 + }
  1079 + return false;
  1080 + }
  1081 +
  1082 + /**
  1083 + * Determine if the browser is NetPositive or not (last updated 1.7)
  1084 + * @return boolean True if the browser is NetPositive otherwise false
  1085 + */
  1086 + protected function checkBrowserNetPositive()
  1087 + {
  1088 + if (stripos($this->_agent, 'NetPositive') !== false) {
  1089 + $aresult = explode('/', stristr($this->_agent, 'NetPositive'));
  1090 + if (isset($aresult[1])) {
  1091 + $aversion = explode(' ', $aresult[1]);
  1092 + $this->setVersion(str_replace(array('(', ')', ';'), '', $aversion[0]));
  1093 + $this->setBrowser(self::BROWSER_NETPOSITIVE);
  1094 + return true;
  1095 + }
  1096 + }
  1097 + return false;
  1098 + }
  1099 +
  1100 + /**
  1101 + * Determine if the browser is Galeon or not (last updated 1.7)
  1102 + * @return boolean True if the browser is Galeon otherwise false
  1103 + */
  1104 + protected function checkBrowserGaleon()
  1105 + {
  1106 + if (stripos($this->_agent, 'galeon') !== false) {
  1107 + $aresult = explode(' ', stristr($this->_agent, 'galeon'));
  1108 + $aversion = explode('/', $aresult[0]);
  1109 + if (isset($aversion[1])) {
  1110 + $this->setVersion($aversion[1]);
  1111 + $this->setBrowser(self::BROWSER_GALEON);
  1112 + return true;
  1113 + }
  1114 + }
  1115 + return false;
  1116 + }
  1117 +
  1118 + /**
  1119 + * Determine if the browser is Konqueror or not (last updated 1.7)
  1120 + * @return boolean True if the browser is Konqueror otherwise false
  1121 + */
  1122 + protected function checkBrowserKonqueror()
  1123 + {
  1124 + if (stripos($this->_agent, 'Konqueror') !== false) {
  1125 + $aresult = explode(' ', stristr($this->_agent, 'Konqueror'));
  1126 + $aversion = explode('/', $aresult[0]);
  1127 + if (isset($aversion[1])) {
  1128 + $this->setVersion($aversion[1]);
  1129 + $this->setBrowser(self::BROWSER_KONQUEROR);
  1130 + return true;
  1131 + }
  1132 + }
  1133 + return false;
  1134 + }
  1135 +
  1136 + /**
  1137 + * Determine if the browser is iCab or not (last updated 1.7)
  1138 + * @return boolean True if the browser is iCab otherwise false
  1139 + */
  1140 + protected function checkBrowserIcab()
  1141 + {
  1142 + if (stripos($this->_agent, 'icab') !== false) {
  1143 + $aversion = explode(' ', stristr(str_replace('/', ' ', $this->_agent), 'icab'));
  1144 + if (isset($aversion[1])) {
  1145 + $this->setVersion($aversion[1]);
  1146 + $this->setBrowser(self::BROWSER_ICAB);
  1147 + return true;
  1148 + }
  1149 + }
  1150 + return false;
  1151 + }
  1152 +
  1153 + /**
  1154 + * Determine if the browser is OmniWeb or not (last updated 1.7)
  1155 + * @return boolean True if the browser is OmniWeb otherwise false
  1156 + */
  1157 + protected function checkBrowserOmniWeb()
  1158 + {
  1159 + if (stripos($this->_agent, 'omniweb') !== false) {
  1160 + $aresult = explode('/', stristr($this->_agent, 'omniweb'));
  1161 + $aversion = explode(' ', isset($aresult[1]) ? $aresult[1] : "");
  1162 + $this->setVersion($aversion[0]);
  1163 + $this->setBrowser(self::BROWSER_OMNIWEB);
  1164 + return true;
  1165 + }
  1166 + return false;
  1167 + }
  1168 +
  1169 + /**
  1170 + * Determine if the browser is Phoenix or not (last updated 1.7)
  1171 + * @return boolean True if the browser is Phoenix otherwise false
  1172 + */
  1173 + protected function checkBrowserPhoenix()
  1174 + {
  1175 + if (stripos($this->_agent, 'Phoenix') !== false) {
  1176 + $aversion = explode('/', stristr($this->_agent, 'Phoenix'));
  1177 + if (isset($aversion[1])) {
  1178 + $this->setVersion($aversion[1]);
  1179 + $this->setBrowser(self::BROWSER_PHOENIX);
  1180 + return true;
  1181 + }
  1182 + }
  1183 + return false;
  1184 + }
  1185 +
  1186 + /**
  1187 + * Determine if the browser is Firebird or not (last updated 1.7)
  1188 + * @return boolean True if the browser is Firebird otherwise false
  1189 + */
  1190 + protected function checkBrowserFirebird()
  1191 + {
  1192 + if (stripos($this->_agent, 'Firebird') !== false) {
  1193 + $aversion = explode('/', stristr($this->_agent, 'Firebird'));
  1194 + if (isset($aversion[1])) {
  1195 + $this->setVersion($aversion[1]);
  1196 + $this->setBrowser(self::BROWSER_FIREBIRD);
  1197 + return true;
  1198 + }
  1199 + }
  1200 + return false;
  1201 + }
  1202 +
  1203 + /**
  1204 + * Determine if the browser is Netscape Navigator 9+ or not (last updated 1.7)
  1205 + * NOTE: (http://browser.netscape.com/ - Official support ended on March 1st, 2008)
  1206 + * @return boolean True if the browser is Netscape Navigator 9+ otherwise false
  1207 + */
  1208 + protected function checkBrowserNetscapeNavigator9Plus()
  1209 + {
  1210 + if (stripos($this->_agent, 'Firefox') !== false && preg_match('/Navigator\/([^ ]*)/i', $this->_agent, $matches)) {
  1211 + $this->setVersion($matches[1]);
  1212 + $this->setBrowser(self::BROWSER_NETSCAPE_NAVIGATOR);
  1213 + return true;
  1214 + } else if (stripos($this->_agent, 'Firefox') === false && preg_match('/Netscape6?\/([^ ]*)/i', $this->_agent, $matches)) {
  1215 + $this->setVersion($matches[1]);
  1216 + $this->setBrowser(self::BROWSER_NETSCAPE_NAVIGATOR);
  1217 + return true;
  1218 + }
  1219 + return false;
  1220 + }
  1221 +
  1222 + /**
  1223 + * Determine if the browser is Shiretoko or not (https://wiki.mozilla.org/Projects/shiretoko) (last updated 1.7)
  1224 + * @return boolean True if the browser is Shiretoko otherwise false
  1225 + */
  1226 + protected function checkBrowserShiretoko()
  1227 + {
  1228 + if (stripos($this->_agent, 'Mozilla') !== false && preg_match('/Shiretoko\/([^ ]*)/i', $this->_agent, $matches)) {
  1229 + $this->setVersion($matches[1]);
  1230 + $this->setBrowser(self::BROWSER_SHIRETOKO);
  1231 + return true;
  1232 + }
  1233 + return false;
  1234 + }
  1235 +
  1236 + /**
  1237 + * Determine if the browser is Ice Cat or not (http://en.wikipedia.org/wiki/GNU_IceCat) (last updated 1.7)
  1238 + * @return boolean True if the browser is Ice Cat otherwise false
  1239 + */
  1240 + protected function checkBrowserIceCat()
  1241 + {
  1242 + if (stripos($this->_agent, 'Mozilla') !== false && preg_match('/IceCat\/([^ ]*)/i', $this->_agent, $matches)) {
  1243 + $this->setVersion($matches[1]);
  1244 + $this->setBrowser(self::BROWSER_ICECAT);
  1245 + return true;
  1246 + }
  1247 + return false;
  1248 + }
  1249 +
  1250 + /**
  1251 + * Determine if the browser is Nokia or not (last updated 1.7)
  1252 + * @return boolean True if the browser is Nokia otherwise false
  1253 + */
  1254 + protected function checkBrowserNokia()
  1255 + {
  1256 + if (preg_match("/Nokia([^\/]+)\/([^ SP]+)/i", $this->_agent, $matches)) {
  1257 + $this->setVersion($matches[2]);
  1258 + if (stripos($this->_agent, 'Series60') !== false || strpos($this->_agent, 'S60') !== false) {
  1259 + $this->setBrowser(self::BROWSER_NOKIA_S60);
  1260 + } else {
  1261 + $this->setBrowser(self::BROWSER_NOKIA);
  1262 + }
  1263 + $this->setMobile(true);
  1264 + return true;
  1265 + }
  1266 + return false;
  1267 + }
  1268 +
  1269 + /**
  1270 + * Determine if the browser is Firefox or not (last updated 1.7)
  1271 + * @return boolean True if the browser is Firefox otherwise false
  1272 + */
  1273 + protected function checkBrowserFirefox()
  1274 + {
  1275 + if (stripos($this->_agent, 'safari') === false) {
  1276 + if (preg_match("/Firefox[\/ \(]([^ ;\)]+)/i", $this->_agent, $matches)) {
  1277 + $this->setVersion($matches[1]);
  1278 + $this->setBrowser(self::BROWSER_FIREFOX);
  1279 + //Firefox on Android
  1280 + if (stripos($this->_agent, 'Android') !== false) {
  1281 + if (stripos($this->_agent, 'Mobile') !== false) {
  1282 + $this->setMobile(true);
  1283 + } else {
  1284 + $this->setTablet(true);
  1285 + }
  1286 + }
  1287 + return true;
  1288 + } else if (preg_match("/Firefox$/i", $this->_agent, $matches)) {
  1289 + $this->setVersion("");
  1290 + $this->setBrowser(self::BROWSER_FIREFOX);
  1291 + return true;
  1292 + }
  1293 + }
  1294 + return false;
  1295 + }
  1296 +
  1297 + /**
  1298 + * Determine if the browser is Firefox or not (last updated 1.7)
  1299 + * @return boolean True if the browser is Firefox otherwise false
  1300 + */
  1301 + protected function checkBrowserIceweasel()
  1302 + {
  1303 + if (stripos($this->_agent, 'Iceweasel') !== false) {
  1304 + $aresult = explode('/', stristr($this->_agent, 'Iceweasel'));
  1305 + if (isset($aresult[1])) {
  1306 + $aversion = explode(' ', $aresult[1]);
  1307 + $this->setVersion($aversion[0]);
  1308 + $this->setBrowser(self::BROWSER_ICEWEASEL);
  1309 + return true;
  1310 + }
  1311 + }
  1312 + return false;
  1313 + }
  1314 +
  1315 + /**
  1316 + * Determine if the browser is Mozilla or not (last updated 1.7)
  1317 + * @return boolean True if the browser is Mozilla otherwise false
  1318 + */
  1319 + protected function checkBrowserMozilla()
  1320 + {
  1321 + if (stripos($this->_agent, 'mozilla') !== false && preg_match('/rv:[0-9].[0-9][a-b]?/i', $this->_agent) && stripos($this->_agent, 'netscape') === false) {
  1322 + $aversion = explode(' ', stristr($this->_agent, 'rv:'));
  1323 + preg_match('/rv:[0-9].[0-9][a-b]?/i', $this->_agent, $aversion);
  1324 + $this->setVersion(str_replace('rv:', '', $aversion[0]));
  1325 + $this->setBrowser(self::BROWSER_MOZILLA);
  1326 + return true;
  1327 + } else if (stripos($this->_agent, 'mozilla') !== false && preg_match('/rv:[0-9]\.[0-9]/i', $this->_agent) && stripos($this->_agent, 'netscape') === false) {
  1328 + $aversion = explode('', stristr($this->_agent, 'rv:'));
  1329 + $this->setVersion(str_replace('rv:', '', $aversion[0]));
  1330 + $this->setBrowser(self::BROWSER_MOZILLA);
  1331 + return true;
  1332 + } else if (stripos($this->_agent, 'mozilla') !== false && preg_match('/mozilla\/([^ ]*)/i', $this->_agent, $matches) && stripos($this->_agent, 'netscape') === false) {
  1333 + $this->setVersion($matches[1]);
  1334 + $this->setBrowser(self::BROWSER_MOZILLA);
  1335 + return true;
  1336 + }
  1337 + return false;
  1338 + }
  1339 +
  1340 + /**
  1341 + * Determine if the browser is Lynx or not (last updated 1.7)
  1342 + * @return boolean True if the browser is Lynx otherwise false
  1343 + */
  1344 + protected function checkBrowserLynx()
  1345 + {
  1346 + if (stripos($this->_agent, 'lynx') !== false) {
  1347 + $aresult = explode('/', stristr($this->_agent, 'Lynx'));
  1348 + $aversion = explode(' ', (isset($aresult[1]) ? $aresult[1] : ""));
  1349 + $this->setVersion($aversion[0]);
  1350 + $this->setBrowser(self::BROWSER_LYNX);
  1351 + return true;
  1352 + }
  1353 + return false;
  1354 + }
  1355 +
  1356 + /**
  1357 + * Determine if the browser is Amaya or not (last updated 1.7)
  1358 + * @return boolean True if the browser is Amaya otherwise false
  1359 + */
  1360 + protected function checkBrowserAmaya()
  1361 + {
  1362 + if (stripos($this->_agent, 'amaya') !== false) {
  1363 + $aresult = explode('/', stristr($this->_agent, 'Amaya'));
  1364 + if (isset($aresult[1])) {
  1365 + $aversion = explode(' ', $aresult[1]);
  1366 + $this->setVersion($aversion[0]);
  1367 + $this->setBrowser(self::BROWSER_AMAYA);
  1368 + return true;
  1369 + }
  1370 + }
  1371 + return false;
  1372 + }
  1373 +
  1374 + /**
  1375 + * Determine if the browser is Safari or not (last updated 1.7)
  1376 + * @return boolean True if the browser is Safari otherwise false
  1377 + */
  1378 + protected function checkBrowserSafari()
  1379 + {
  1380 + if (stripos($this->_agent, 'Safari') !== false
  1381 + && stripos($this->_agent, 'iPhone') === false
  1382 + && stripos($this->_agent, 'iPod') === false
  1383 + ) {
  1384 +
  1385 + $aresult = explode('/', stristr($this->_agent, 'Version'));
  1386 + if (isset($aresult[1])) {
  1387 + $aversion = explode(' ', $aresult[1]);
  1388 + $this->setVersion($aversion[0]);
  1389 + } else {
  1390 + $this->setVersion(self::VERSION_UNKNOWN);
  1391 + }
  1392 + $this->setBrowser(self::BROWSER_SAFARI);
  1393 + return true;
  1394 + }
  1395 + return false;
  1396 + }
  1397 +
  1398 + protected function checkBrowserSamsung()
  1399 + {
  1400 + if (stripos($this->_agent, 'SamsungBrowser') !== false) {
  1401 +
  1402 + $aresult = explode('/', stristr($this->_agent, 'SamsungBrowser'));
  1403 + if (isset($aresult[1])) {
  1404 + $aversion = explode(' ', $aresult[1]);
  1405 + $this->setVersion($aversion[0]);
  1406 + } else {
  1407 + $this->setVersion(self::VERSION_UNKNOWN);
  1408 + }
  1409 + $this->setBrowser(self::BROWSER_SAMSUNG);
  1410 + return true;
  1411 + }
  1412 + return false;
  1413 + }
  1414 +
  1415 + protected function checkBrowserSilk()
  1416 + {
  1417 + if (stripos($this->_agent, 'Silk') !== false) {
  1418 + $aresult = explode('/', stristr($this->_agent, 'Silk'));
  1419 + if (isset($aresult[1])) {
  1420 + $aversion = explode(' ', $aresult[1]);
  1421 + $this->setVersion($aversion[0]);
  1422 + } else {
  1423 + $this->setVersion(self::VERSION_UNKNOWN);
  1424 + }
  1425 + $this->setBrowser(self::BROWSER_SILK);
  1426 + return true;
  1427 + }
  1428 + return false;
  1429 + }
  1430 +
  1431 + protected function checkBrowserIframely()
  1432 + {
  1433 + if (stripos($this->_agent, 'Iframely') !== false) {
  1434 + $aresult = explode('/', stristr($this->_agent, 'Iframely'));
  1435 + if (isset($aresult[1])) {
  1436 + $aversion = explode(' ', $aresult[1]);
  1437 + $this->setVersion($aversion[0]);
  1438 + } else {
  1439 + $this->setVersion(self::VERSION_UNKNOWN);
  1440 + }
  1441 + $this->setBrowser(self::BROWSER_I_FRAME);
  1442 + return true;
  1443 + }
  1444 + return false;
  1445 + }
  1446 +
  1447 + protected function checkBrowserCocoa()
  1448 + {
  1449 + if (stripos($this->_agent, 'CocoaRestClient') !== false) {
  1450 + $aresult = explode('/', stristr($this->_agent, 'CocoaRestClient'));
  1451 + if (isset($aresult[1])) {
  1452 + $aversion = explode(' ', $aresult[1]);
  1453 + $this->setVersion($aversion[0]);
  1454 + } else {
  1455 + $this->setVersion(self::VERSION_UNKNOWN);
  1456 + }
  1457 + $this->setBrowser(self::BROWSER_COCOA);
  1458 + return true;
  1459 + }
  1460 + return false;
  1461 + }
  1462 +
  1463 + /**
  1464 + * Detect if URL is loaded from FacebookExternalHit
  1465 + * @return boolean True if it detects FacebookExternalHit otherwise false
  1466 + */
  1467 + protected function checkFacebookExternalHit()
  1468 + {
  1469 + if (stristr($this->_agent, 'FacebookExternalHit')) {
  1470 + $this->setRobot(true);
  1471 + $this->setFacebook(true);
  1472 + return true;
  1473 + }
  1474 + return false;
  1475 + }
  1476 +
  1477 + /**
  1478 + * Detect if URL is being loaded from internal Facebook browser
  1479 + * @return boolean True if it detects internal Facebook browser otherwise false
  1480 + */
  1481 + protected function checkForFacebookIos()
  1482 + {
  1483 + if (stristr($this->_agent, 'FBIOS')) {
  1484 + $this->setFacebook(true);
  1485 + return true;
  1486 + }
  1487 + return false;
  1488 + }
  1489 +
  1490 + /**
  1491 + * Detect Version for the Safari browser on iOS devices
  1492 + * @return boolean True if it detects the version correctly otherwise false
  1493 + */
  1494 + protected function getSafariVersionOnIos()
  1495 + {
  1496 + $aresult = explode('/', stristr($this->_agent, 'Version'));
  1497 + if (isset($aresult[1])) {
  1498 + $aversion = explode(' ', $aresult[1]);
  1499 + $this->setVersion($aversion[0]);
  1500 + return true;
  1501 + }
  1502 + return false;
  1503 + }
  1504 +
  1505 + /**
  1506 + * Detect Version for the Chrome browser on iOS devices
  1507 + * @return boolean True if it detects the version correctly otherwise false
  1508 + */
  1509 + protected function getChromeVersionOnIos()
  1510 + {
  1511 + $aresult = explode('/', stristr($this->_agent, 'CriOS'));
  1512 + if (isset($aresult[1])) {
  1513 + $aversion = explode(' ', $aresult[1]);
  1514 + $this->setVersion($aversion[0]);
  1515 + $this->setBrowser(self::BROWSER_CHROME);
  1516 + return true;
  1517 + }
  1518 + return false;
  1519 + }
  1520 +
  1521 + /**
  1522 + * Determine if the browser is iPhone or not (last updated 1.7)
  1523 + * @return boolean True if the browser is iPhone otherwise false
  1524 + */
  1525 + protected function checkBrowseriPhone()
  1526 + {
  1527 + if (stripos($this->_agent, 'iPhone') !== false) {
  1528 + $this->setVersion(self::VERSION_UNKNOWN);
  1529 + $this->setBrowser(self::BROWSER_IPHONE);
  1530 + $this->getSafariVersionOnIos();
  1531 + $this->getChromeVersionOnIos();
  1532 + $this->checkForFacebookIos();
  1533 + $this->setMobile(true);
  1534 + return true;
  1535 +
  1536 + }
  1537 + return false;
  1538 + }
  1539 +
  1540 + /**
  1541 + * Determine if the browser is iPad or not (last updated 1.7)
  1542 + * @return boolean True if the browser is iPad otherwise false
  1543 + */
  1544 + protected function checkBrowseriPad()
  1545 + {
  1546 + if (stripos($this->_agent, 'iPad') !== false) {
  1547 + $this->setVersion(self::VERSION_UNKNOWN);
  1548 + $this->setBrowser(self::BROWSER_IPAD);
  1549 + $this->getSafariVersionOnIos();
  1550 + $this->getChromeVersionOnIos();
  1551 + $this->checkForFacebookIos();
  1552 + $this->setTablet(true);
  1553 + return true;
  1554 + }
  1555 + return false;
  1556 + }
  1557 +
  1558 + /**
  1559 + * Determine if the browser is iPod or not (last updated 1.7)
  1560 + * @return boolean True if the browser is iPod otherwise false
  1561 + */
  1562 + protected function checkBrowseriPod()
  1563 + {
  1564 + if (stripos($this->_agent, 'iPod') !== false) {
  1565 + $this->setVersion(self::VERSION_UNKNOWN);
  1566 + $this->setBrowser(self::BROWSER_IPOD);
  1567 + $this->getSafariVersionOnIos();
  1568 + $this->getChromeVersionOnIos();
  1569 + $this->checkForFacebookIos();
  1570 + $this->setMobile(true);
  1571 + return true;
  1572 + }
  1573 + return false;
  1574 + }
  1575 +
  1576 + /**
  1577 + * Determine if the browser is Android or not (last updated 1.7)
  1578 + * @return boolean True if the browser is Android otherwise false
  1579 + */
  1580 + protected function checkBrowserAndroid()
  1581 + {
  1582 + if (stripos($this->_agent, 'Android') !== false) {
  1583 + $aresult = explode(' ', stristr($this->_agent, 'Android'));
  1584 + if (isset($aresult[1])) {
  1585 + $aversion = explode(' ', $aresult[1]);
  1586 + $this->setVersion($aversion[0]);
  1587 + } else {
  1588 + $this->setVersion(self::VERSION_UNKNOWN);
  1589 + }
  1590 + if (stripos($this->_agent, 'Mobile') !== false) {
  1591 + $this->setMobile(true);
  1592 + } else {
  1593 + $this->setTablet(true);
  1594 + }
  1595 + $this->setBrowser(self::BROWSER_ANDROID);
  1596 + return true;
  1597 + }
  1598 + return false;
  1599 + }
  1600 +
  1601 + /**
  1602 + * Determine if the browser is Vivaldi
  1603 + * @return boolean True if the browser is Vivaldi otherwise false
  1604 + */
  1605 + protected function checkBrowserVivaldi()
  1606 + {
  1607 + if (stripos($this->_agent, 'Vivaldi') !== false) {
  1608 + $aresult = explode('/', stristr($this->_agent, 'Vivaldi'));
  1609 + if (isset($aresult[1])) {
  1610 + $aversion = explode(' ', $aresult[1]);
  1611 + $this->setVersion($aversion[0]);
  1612 + $this->setBrowser(self::BROWSER_VIVALDI);
  1613 + return true;
  1614 + }
  1615 + }
  1616 + return false;
  1617 + }
  1618 +
  1619 + /**
  1620 + * Determine if the browser is Yandex
  1621 + * @return boolean True if the browser is Yandex otherwise false
  1622 + */
  1623 + protected function checkBrowserYandex()
  1624 + {
  1625 + if (stripos($this->_agent, 'YaBrowser') !== false) {
  1626 + $aresult = explode('/', stristr($this->_agent, 'YaBrowser'));
  1627 + if (isset($aresult[1])) {
  1628 + $aversion = explode(' ', $aresult[1]);
  1629 + $this->setVersion($aversion[0]);
  1630 + $this->setBrowser(self::BROWSER_YANDEX);
  1631 +
  1632 + if (stripos($this->_agent, 'iPad') !== false) {
  1633 + $this->setTablet(true);
  1634 + } elseif (stripos($this->_agent, 'Mobile') !== false) {
  1635 + $this->setMobile(true);
  1636 + } elseif (stripos($this->_agent, 'Android') !== false) {
  1637 + $this->setTablet(true);
  1638 + }
  1639 +
  1640 + return true;
  1641 + }
  1642 + }
  1643 +
  1644 + return false;
  1645 + }
  1646 +
  1647 + /**
  1648 + * Determine if the browser is a PlayStation
  1649 + * @return boolean True if the browser is PlayStation otherwise false
  1650 + */
  1651 + protected function checkBrowserPlayStation()
  1652 + {
  1653 + if (stripos($this->_agent, 'PlayStation ') !== false) {
  1654 + $aresult = explode(' ', stristr($this->_agent, 'PlayStation '));
  1655 + $this->setBrowser(self::BROWSER_PLAYSTATION);
  1656 + if (isset($aresult[0])) {
  1657 + $aversion = explode(')', $aresult[2]);
  1658 + $this->setVersion($aversion[0]);
  1659 + if (stripos($this->_agent, 'Portable)') !== false || stripos($this->_agent, 'Vita') !== false) {
  1660 + $this->setMobile(true);
  1661 + }
  1662 + return true;
  1663 + }
  1664 + }
  1665 + return false;
  1666 + }
  1667 +
  1668 + /**
  1669 + * Determine the user's platform (last updated 2.0)
  1670 + */
  1671 + protected function checkPlatform()
  1672 + {
  1673 + if (stripos($this->_agent, 'windows') !== false) {
  1674 + $this->_platform = self::PLATFORM_WINDOWS;
  1675 + } else if (stripos($this->_agent, 'iPad') !== false) {
  1676 + $this->_platform = self::PLATFORM_IPAD;
  1677 + } else if (stripos($this->_agent, 'iPod') !== false) {
  1678 + $this->_platform = self::PLATFORM_IPOD;
  1679 + } else if (stripos($this->_agent, 'iPhone') !== false) {
  1680 + $this->_platform = self::PLATFORM_IPHONE;
  1681 + } elseif (stripos($this->_agent, 'mac') !== false) {
  1682 + $this->_platform = self::PLATFORM_APPLE;
  1683 + } elseif (stripos($this->_agent, 'android') !== false) {
  1684 + $this->_platform = self::PLATFORM_ANDROID;
  1685 + } elseif (stripos($this->_agent, 'Silk') !== false) {
  1686 + $this->_platform = self::PLATFORM_FIRE_OS;
  1687 + } elseif (stripos($this->_agent, 'linux') !== false && stripos($this->_agent, 'SMART-TV') !== false ) {
  1688 + $this->_platform = self::PLATFORM_LINUX .'/'.self::PLATFORM_SMART_TV;
  1689 + } elseif (stripos($this->_agent, 'linux') !== false) {
  1690 + $this->_platform = self::PLATFORM_LINUX;
  1691 + } else if (stripos($this->_agent, 'Nokia') !== false) {
  1692 + $this->_platform = self::PLATFORM_NOKIA;
  1693 + } else if (stripos($this->_agent, 'BlackBerry') !== false) {
  1694 + $this->_platform = self::PLATFORM_BLACKBERRY;
  1695 + } elseif (stripos($this->_agent, 'FreeBSD') !== false) {
  1696 + $this->_platform = self::PLATFORM_FREEBSD;
  1697 + } elseif (stripos($this->_agent, 'OpenBSD') !== false) {
  1698 + $this->_platform = self::PLATFORM_OPENBSD;
  1699 + } elseif (stripos($this->_agent, 'NetBSD') !== false) {
  1700 + $this->_platform = self::PLATFORM_NETBSD;
  1701 + } elseif (stripos($this->_agent, 'OpenSolaris') !== false) {
  1702 + $this->_platform = self::PLATFORM_OPENSOLARIS;
  1703 + } elseif (stripos($this->_agent, 'SunOS') !== false) {
  1704 + $this->_platform = self::PLATFORM_SUNOS;
  1705 + } elseif (stripos($this->_agent, 'OS\/2') !== false) {
  1706 + $this->_platform = self::PLATFORM_OS2;
  1707 + } elseif (stripos($this->_agent, 'BeOS') !== false) {
  1708 + $this->_platform = self::PLATFORM_BEOS;
  1709 + } elseif (stripos($this->_agent, 'win') !== false) {
  1710 + $this->_platform = self::PLATFORM_WINDOWS;
  1711 + } elseif (stripos($this->_agent, 'Playstation') !== false) {
  1712 + $this->_platform = self::PLATFORM_PLAYSTATION;
  1713 + } elseif (stripos($this->_agent, 'Roku') !== false) {
  1714 + $this->_platform = self::PLATFORM_ROKU;
  1715 + } elseif (stripos($this->_agent, 'iOS') !== false) {
  1716 + $this->_platform = self::PLATFORM_IPHONE . '/' . self::PLATFORM_IPAD;
  1717 + } elseif (stripos($this->_agent, 'tvOS') !== false) {
  1718 + $this->_platform = self::PLATFORM_APPLE_TV;
  1719 + } elseif (stripos($this->_agent, 'curl') !== false) {
  1720 + $this->_platform = self::PLATFORM_TERMINAL;
  1721 + } elseif (stripos($this->_agent, 'CrOS') !== false) {
  1722 + $this->_platform = self::PLATFORM_CHROME_OS;
  1723 + } elseif (stripos($this->_agent, 'okhttp') !== false) {
  1724 + $this->_platform = self::PLATFORM_JAVA_ANDROID;
  1725 + } elseif (stripos($this->_agent, 'PostmanRuntime') !== false) {
  1726 + $this->_platform = self::PLATFORM_POSTMAN;
  1727 + } elseif (stripos($this->_agent, 'Iframely') !== false) {
  1728 + $this->_platform = self::PLATFORM_I_FRAME;
  1729 + }
  1730 +
  1731 + }
  1732 +}
k2shop/usr/k2shop/libs/browser/Readme.txt
  1 +Определение версии и типа браузера.
  2 +Постоянно обновляется по адресу: https://github.com/cbschuld/Browser.php
  3 +Статья по данной теме: https://ruseller.com/adds.php?rub=11&id=1914
  4 +
  5 +Пример использования:
  6 +$browser = new Browser();
  7 +if( $browser->getBrowser() == Browser::BROWSER_FIREFOX && $browser->getVersion() >= 2 ) {
  8 + echo 'Вы используете браузер FireFox версии 2 или выше';
  9 +}
k2shop/usr/k2shop/libs/detectDevice/DetectDeviceTest.php
  1 +<?php
  2 +require "class.DetectDevice.php";
  3 +
  4 +class DetectDeviceTest extends PHPUnit_Framework_TestCase {
  5 +
  6 + private $device;
  7 +
  8 + public function __construct() {
  9 + $this->device = new DetectDevice("Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36");
  10 + }
  11 +
  12 + public function testComputer() {
  13 + // Google Chrome 29 on Windows 8 x64
  14 + $this->device->setUserAgent("Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36");
  15 + $this->assertEquals(true, $this->device->isComputer());
  16 +
  17 + // Mozilla Firefox 22 on Windows 8 x64
  18 + $this->device->setUserAgent("Mozilla/5.0 (Windows NT 6.2; WOW64; rv:22.0) Gecko/20100101 Firefox/22.0");
  19 + $this->assertEquals(true, $this->device->isComputer());
  20 + }
  21 +
  22 + public function testMobile() {
  23 + // LGE Nexus 4
  24 + $this->device->setUserAgent("Mozilla/5.0 (Linux; Android 4.2; Nexus 4 Build/JVP15Q) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19");
  25 + $this->assertEquals(true, $this->device->isMobile());
  26 +
  27 + // Samsung Galaxy S4
  28 + $this->device->setUserAgent("Mozilla/5.0 (Linux; U; Android 4.0.4; en-gb; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30");
  29 + $this->assertEquals(true, $this->device->isMobile());
  30 + }
  31 +
  32 + public function testTablet() {
  33 + // Asus Nexus 7
  34 + $this->device->setUserAgent("Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19");
  35 + $this->assertEquals(true, $this->device->isTablet());
  36 +
  37 + // Samsung Galaxy Note 10.1
  38 + $this->device->setUserAgent("Mozilla/5.0 (Linux; U; Android 4.0; xx-xx; GT-N8000 Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30");
  39 + $this->assertEquals(true, $this->device->isTablet());
  40 + }
  41 +
  42 + public function testBot() {
  43 + // GoogleBot
  44 + $this->device->setUserAgent("Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)");
  45 + $this->assertEquals(true, $this->device->isBot());
  46 +
  47 + // BingBot
  48 + $this->device->setUserAgent("Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)");
  49 + $this->assertEquals(true, $this->device->isBot());
  50 + }
  51 +
  52 + public function testConsole() {
  53 + // PS3
  54 + $this->device->setUserAgent("Mozilla/5.0 (PLAYSTATION 3; 3.55)");
  55 + $this->assertEquals(true, $this->device->isConsole());
  56 +
  57 + // XBox
  58 + $this->device->setUserAgent("Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; Xbox)");
  59 + $this->assertEquals(true, $this->device->isConsole());
  60 + }
  61 +
  62 + public function testIs() {
  63 + // Google Chrome
  64 + $this->device->setUserAgent("Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36");
  65 + $this->assertEquals(true, $this->device->is("chrome"));
  66 +
  67 + // Nintendo DS
  68 + $this->device->setUserAgent("Mozilla/5.0 (Windows NT 6.2; WOW64; rv:22.0) Gecko/20100101 Firefox/22.0");
  69 + $this->assertEquals(true, $this->device->is("firefox"));
  70 + }
  71 +}
k2shop/usr/k2shop/libs/detectDevice/README.md
  1 +DetectDevice
  2 +============
  3 +[![Build Status](https://travis-ci.org/Kocal/DetectDevice.png?branch=master)](https://travis-ci.org/Kocal/DetectDevice)
  4 +
  5 +DetectDevice is a little (~70 lines), simply, and functional PHP class which detect what kind of device is used to navigate on your website, a pc, a mobile, a tablet or a bot.
  6 +
  7 +
  8 +How use?
  9 +--------
  10 +#### Firstly, include DetectDevice class and initialize it
  11 +```php
  12 +<?php
  13 +require "class.DetectDevice.php";
  14 +$device = new DetectDevice();
  15 +// ...
  16 +```
  17 +
  18 +#### Optional : If you want, you can set an other user-agent
  19 +```php
  20 +<?php
  21 +$userAgent = "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36";
  22 +
  23 +// in the constructor
  24 +$device = new DetectDevice($userAgent);
  25 +
  26 +// or with the "setUserAgent()" method
  27 +$device->setUserAgent($userAgent);
  28 +```
  29 +
  30 +#### Use methods to find what kind of device is used
  31 +```php
  32 +<?php
  33 +// return a boolean (true or false)
  34 +echo $device->isComputer();
  35 +echo $device->isMobile();
  36 +echo $device->isTablet();
  37 +echo $device->isBot();
  38 +echo $device->isConsole();
  39 +
  40 +// You can use the "getDeviceType()" to return a string
  41 +echo $device->getDeviceType(); // may return "computer", "mobile, "tablet", "bot" or "console"
  42 +// ...
  43 +```
  44 +
  45 +#### Or use "is()"
  46 +```php
  47 +<?php
  48 +// The "is()" method match $str in UserAgent $str, and return true or false
  49 +
  50 +$str = "msie";
  51 +
  52 +if($device->is($str)) {
  53 + // do stuff for Internet Explorer only
  54 +}
  55 +
  56 +```
k2shop/usr/k2shop/libs/detectDevice/class.DetectDevice.php
  1 +<?php class DetectDevice {
  2 +
  3 + private $userAgent = "";
  4 +
  5 + private $devices = array(
  6 + "computer" => array("msie 10", "msie 9", "msie 8", "windows.*firefox", "windows.*chrome", "x11.*chrome", "x11.*firefox", "macintosh.*chrome", "macintosh.*firefox", "opera"),
  7 + "tablet" => array("tablet", "android", "ipad", "tablet.*firefox"),
  8 + "mobile" => array("mobile ", "android.*mobile", "iphone", "ipod", "opera mobi", "opera mini"),
  9 + "bot" => array("googlebot", "mediapartners-google", "adsbot-google", "duckduckbot", "msnbot", "bingbot", "ask", "facebook", "yahoo", "addthis"),
  10 + "console" => array("ps2", "playstation", "xbox", "wii", "nintendo 3?ds")
  11 + );
  12 +
  13 +
  14 + private $deviceType = "";
  15 +
  16 + private $isComputer = false;
  17 + private $isMobile = false;
  18 + private $isTablet = false;
  19 + private $isBot = false;
  20 + private $isConsole = false;
  21 +
  22 +
  23 + public function __construct($userAgent = "") {
  24 + if(!empty($userAgent)) {
  25 + $this->userAgent = $userAgent;
  26 + } else {
  27 + $this->userAgent = $_SERVER["HTTP_USER_AGENT"];
  28 + }
  29 +
  30 + $this->detectDevice();
  31 + }
  32 +
  33 + public function detectDevice() {
  34 + foreach($this->devices as $deviceType => $devices) {
  35 + foreach($devices as $device) {
  36 + if(preg_match("/" . $device . "/i", $this->userAgent)) {
  37 + $this->deviceType = $deviceType;
  38 + }
  39 + }
  40 + }
  41 +
  42 + $d = "is" . ucfirst($this->deviceType);
  43 + $this->$d = true;
  44 + }
  45 +
  46 + public function setUserAgent($userAgent) {
  47 + $this->userAgent = $userAgent;
  48 + $this->resetDevices();
  49 + $this->detectDevice();
  50 + }
  51 +
  52 + public function resetDevices() {
  53 + $this->isComputer = false;
  54 + $this->isMobile = false;
  55 + $this->isTablet = false;
  56 + $this->isBot = false;
  57 + }
  58 +
  59 + public function is($str, $userAgent = null) {
  60 + if(!$userAgent) {
  61 + $userAgent = $this->userAgent;
  62 + }
  63 +
  64 + return preg_match('/' . $str . '/i', $userAgent);
  65 + }
  66 +
  67 + public function getDeviceType() { return $this->deviceType; }
  68 + public function isComputer() { return $this->isComputer; }
  69 + public function isMobile() { return $this->isMobile; }
  70 + public function isTablet() { return $this->isTablet; }
  71 + public function isBot() { return $this->isBot; }
  72 + public function isConsole() { return $this->isConsole; }
  73 + public function getUserAgent() { return $this->userAgent; }
  74 +
  75 +
  76 +}
k2shop/usr/k2shop/libs/nestmenu/index.html
  1 +<!DOCTYPE html>
  2 +<!--[if lt IE 7]> <html lang="en" class="lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
  3 +<!--[if IE 7]> <html lang="en" class="lt-ie9 lt-ie8"> <![endif]-->
  4 +<!--[if IE 8]> <html lang="en" class="lt-ie9"> <![endif]-->
  5 +<!--[if IE 9]> <html lang="en" class="ie9"> <![endif]-->
  6 +<!--[if gt IE 9]><!--> <html lang="en"> <!--<![endif]-->
  7 +<head>
  8 + <meta charset="utf-8">
  9 + <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
  10 + <title>Nestable</title>
  11 + <style type="text/css">
  12 +.cf:after { visibility: hidden; display: block; font-size: 0; content: " "; clear: both; height: 0; }
  13 +* html .cf { zoom: 1; }
  14 +*:first-child+html .cf { zoom: 1; }
  15 +html { margin: 0; padding: 0; }
  16 +body { font-size: 100%; margin: 0; padding: 1.75em; font-family: 'Helvetica Neue', Arial, sans-serif; }
  17 +h1 { font-size: 1.75em; margin: 0 0 0.6em 0; }
  18 +a { color: #2996cc; }
  19 +a:hover { text-decoration: none; }
  20 +p { line-height: 1.5em; }
  21 +.small { color: #666; font-size: 0.875em; }
  22 +.large { font-size: 1.25em; }
  23 +/**
  24 + * Nestable
  25 + */
  26 +.dd { position: relative; display: block; margin: 0; padding: 0; max-width: 600px; list-style: none; font-size: 13px; line-height: 20px; }
  27 +.dd-list { display: block; position: relative; margin: 0; padding: 0; list-style: none; }
  28 +.dd-list .dd-list { padding-left: 30px; }
  29 +.dd-collapsed .dd-list { display: none; }
  30 +.dd-item,
  31 +.dd-empty,
  32 +.dd-placeholder { display: block; position: relative; margin: 0; padding: 0; min-height: 20px; font-size: 13px; line-height: 20px; }
  33 +.dd-handle { display: block; height: 30px; margin: 5px 0; padding: 5px 10px; color: #333; text-decoration: none; font-weight: bold; border: 1px solid #ccc;
  34 + background: #fafafa;
  35 + background: -webkit-linear-gradient(top, #fafafa 0%, #eee 100%);
  36 + background: -moz-linear-gradient(top, #fafafa 0%, #eee 100%);
  37 + background: linear-gradient(top, #fafafa 0%, #eee 100%);
  38 + -webkit-border-radius: 3px;
  39 + border-radius: 3px;
  40 + box-sizing: border-box; -moz-box-sizing: border-box;
  41 +}
  42 +.dd-handle:hover { color: #2ea8e5; background: #fff; }
  43 +.dd-item > button { display: block; position: relative; cursor: pointer; float: left; width: 25px; height: 20px; margin: 5px 0; padding: 0; text-indent: 100%; white-space: nowrap; overflow: hidden; border: 0; background: transparent; font-size: 12px; line-height: 1; text-align: center; font-weight: bold; }
  44 +.dd-item > button:before { content: '+'; display: block; position: absolute; width: 100%; text-align: center; text-indent: 0; }
  45 +.dd-item > button[data-action="collapse"]:before { content: '-'; }
  46 +.dd-placeholder,
  47 +.dd-empty { margin: 5px 0; padding: 0; min-height: 30px; background: #f2fbff; border: 1px dashed #b6bcbf; box-sizing: border-box; -moz-box-sizing: border-box; }
  48 +.dd-empty { border: 1px dashed #bbb; min-height: 100px; background-color: #e5e5e5;
  49 + background-image: -webkit-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff),
  50 + -webkit-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff);
  51 + background-image: -moz-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff),
  52 + -moz-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff);
  53 + background-image: linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff),
  54 + linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff);
  55 + background-size: 60px 60px;
  56 + background-position: 0 0, 30px 30px;
  57 +}
  58 +.dd-dragel { position: absolute; pointer-events: none; z-index: 9999; }
  59 +.dd-dragel > .dd-item .dd-handle { margin-top: 0; }
  60 +.dd-dragel .dd-handle {
  61 + -webkit-box-shadow: 2px 4px 6px 0 rgba(0,0,0,.1);
  62 + box-shadow: 2px 4px 6px 0 rgba(0,0,0,.1);
  63 +}
  64 +/**
  65 + * Nestable Extras
  66 + */
  67 +.nestable-lists { display: block; clear: both; padding: 30px 0; width: 100%; border: 0; border-top: 2px solid #ddd; border-bottom: 2px solid #ddd; }
  68 +#nestable-menu { padding: 0; margin: 20px 0; }
  69 +#nestable-output,
  70 +#nestable2-output { width: 100%; height: 7em; font-size: 0.75em; line-height: 1.333333em; font-family: Consolas, monospace; padding: 5px; box-sizing: border-box; -moz-box-sizing: border-box; }
  71 +#nestable2 .dd-handle {
  72 + color: #fff;
  73 + border: 1px solid #999;
  74 + background: #bbb;
  75 + background: -webkit-linear-gradient(top, #bbb 0%, #999 100%);
  76 + background: -moz-linear-gradient(top, #bbb 0%, #999 100%);
  77 + background: linear-gradient(top, #bbb 0%, #999 100%);
  78 +}
  79 +#nestable2 .dd-handle:hover { background: #bbb; }
  80 +#nestable2 .dd-item > button:before { color: #fff; }
  81 +@media only screen and (min-width: 700px) {
  82 + .dd { float: left; width: 48%; }
  83 + .dd + .dd { margin-left: 2%; }
  84 +}
  85 +.dd-hover > .dd-handle { background: #2ea8e5 !important; }
  86 +/**
  87 + * Nestable Draggable Handles
  88 + */
  89 +.dd3-content { display: block; height: 30px; margin: 5px 0; padding: 5px 10px 5px 40px; color: #333; text-decoration: none; font-weight: bold; border: 1px solid #ccc;
  90 + background: #fafafa;
  91 + background: -webkit-linear-gradient(top, #fafafa 0%, #eee 100%);
  92 + background: -moz-linear-gradient(top, #fafafa 0%, #eee 100%);
  93 + background: linear-gradient(top, #fafafa 0%, #eee 100%);
  94 + -webkit-border-radius: 3px;
  95 + border-radius: 3px;
  96 + box-sizing: border-box; -moz-box-sizing: border-box;
  97 +}
  98 +.dd3-content:hover { color: #2ea8e5; background: #fff; }
  99 +.dd-dragel > .dd3-item > .dd3-content { margin: 0; }
  100 +.dd3-item > button { margin-left: 30px; }
  101 +.dd3-handle { position: absolute; margin: 0; left: 0; top: 0; cursor: pointer; width: 30px; text-indent: 100%; white-space: nowrap; overflow: hidden;
  102 + border: 1px solid #aaa;
  103 + background: #ddd;
  104 + background: -webkit-linear-gradient(top, #ddd 0%, #bbb 100%);
  105 + background: -moz-linear-gradient(top, #ddd 0%, #bbb 100%);
  106 + background: linear-gradient(top, #ddd 0%, #bbb 100%);
  107 + border-top-right-radius: 0;
  108 + border-bottom-right-radius: 0;
  109 +}
  110 +.dd3-handle:before { content: '?'; display: block; position: absolute; left: 0; top: 3px; width: 100%; text-align: center; text-indent: 0; color: #fff; font-size: 20px; font-weight: normal; }
  111 +.dd3-handle:hover { background: #ddd; }
  112 +/**
  113 + * Socialite
  114 + */
  115 +.socialite { display: block; float: left; height: 35px; }
  116 + </style>
  117 +</head>
  118 +<body>
  119 +
  120 + <h1>Nestable</h1>
  121 +
  122 + <p>Drag &amp; drop hierarchical list with mouse and touch compatibility (jQuery plugin)</p>
  123 +
  124 + <p><strong><a href="https://github.com/dbushell/Nestable">Download on GitHub</a></strong></p>
  125 +
  126 + <div class="cf">
  127 + <a href="http://twitter.com/share" class="socialite twitter-share" data-via="dbushell" data-text="jQuery Nestable plugin" data-url="http://dbushell.github.com/Nestable/" data-size="large" rel="nofollow" target="_blank">
  128 + <span class="vhidden">Share on Twitter</span>
  129 + </a>
  130 + </div>
  131 +
  132 +
  133 +<p><strong>PLEASE NOTE: I cannot provide any support or guidance beyond this README. If this code helps you that's great but I have no plans to develop Nestable beyond this demo (it's not a final product and has limited functionality). I cannot reply to any requests for help.</strong></p>
  134 +
  135 + <menu id="nestable-menu">
  136 + <button type="button" data-action="expand-all">Expand All</button>
  137 + <button type="button" data-action="collapse-all">Collapse All</button>
  138 + </menu>
  139 +
  140 + <div class="cf nestable-lists">
  141 +
  142 + <div class="dd" id="nestable">
  143 + <ol class="dd-list">
  144 + <li class="dd-item" data-id="1">
  145 + <div class="dd-handle">Item 1</div>
  146 + </li>
  147 + <li class="dd-item" data-id="2">
  148 + <div class="dd-handle">Item 2</div>
  149 + <ol class="dd-list">
  150 + <li class="dd-item" data-id="3"><div class="dd-handle">Item 3</div></li>
  151 + <li class="dd-item" data-id="4"><div class="dd-handle">Item 4</div></li>
  152 + <li class="dd-item" data-id="5">
  153 + <div class="dd-handle">Item 5</div>
  154 + <ol class="dd-list">
  155 + <li class="dd-item" data-id="6"><div class="dd-handle">Item 6</div></li>
  156 + <li class="dd-item" data-id="7"><div class="dd-handle">Item 7</div></li>
  157 + <li class="dd-item" data-id="8"><div class="dd-handle">Item 8</div></li>
  158 + </ol>
  159 + </li>
  160 + <li class="dd-item" data-id="9"><div class="dd-handle">Item 9</div></li>
  161 + <li class="dd-item" data-id="10"><div class="dd-handle">Item 10</div></li>
  162 + </ol>
  163 + </li>
  164 + <li class="dd-item" data-id="11">
  165 + <div class="dd-handle">Item 11</div>
  166 + </li>
  167 + <li class="dd-item" data-id="12">
  168 + <div class="dd-handle">Item 12</div>
  169 + </li>
  170 + </ol>
  171 + </div>
  172 +
  173 + <div class="dd" id="nestable2">
  174 + <ol class="dd-list">
  175 + <li class="dd-item" data-id="13">
  176 + <div class="dd-handle">Item 13</div>
  177 + </li>
  178 + <li class="dd-item" data-id="14">
  179 + <div class="dd-handle">Item 14</div>
  180 + </li>
  181 + <li class="dd-item" data-id="15">
  182 + <div class="dd-handle">Item 15</div>
  183 + <ol class="dd-list">
  184 + <li class="dd-item" data-id="16"><div class="dd-handle">Item 16</div></li>
  185 + <li class="dd-item" data-id="17"><div class="dd-handle">Item 17</div></li>
  186 + <li class="dd-item" data-id="18"><div class="dd-handle">Item 18</div></li>
  187 + </ol>
  188 + </li>
  189 + </ol>
  190 + </div>
  191 +
  192 + </div>
  193 +
  194 + <p><strong>Serialised Output (per list)</strong></p>
  195 +
  196 + <textarea id="nestable-output"></textarea>
  197 + <textarea id="nestable2-output"></textarea>
  198 +
  199 + <p>&nbsp;</p>
  200 +
  201 + <div class="cf nestable-lists">
  202 +
  203 + <p><strong>Draggable Handles</strong></p>
  204 +
  205 + <p>If you're clever with your CSS and markup this can be achieved without any JavaScript changes.</p>
  206 +
  207 + <div class="dd" id="nestable3">
  208 + <ol class="dd-list">
  209 + <li class="dd-item dd3-item" data-id="13">
  210 + <div class="dd-handle dd3-handle">Drag</div><div class="dd3-content">Item 13</div>
  211 + </li>
  212 + <li class="dd-item dd3-item" data-id="14">
  213 + <div class="dd-handle dd3-handle">Drag</div><div class="dd3-content">Item 14</div>
  214 + </li>
  215 + <li class="dd-item dd3-item" data-id="15">
  216 + <div class="dd-handle dd3-handle">Drag</div><div class="dd3-content">Item 15</div>
  217 + <ol class="dd-list">
  218 + <li class="dd-item dd3-item" data-id="16">
  219 + <div class="dd-handle dd3-handle">Drag</div><div class="dd3-content">Item 16</div>
  220 + </li>
  221 + <li class="dd-item dd3-item" data-id="17">
  222 + <div class="dd-handle dd3-handle">Drag</div><div class="dd3-content">Item 17</div>
  223 + </li>
  224 + <li class="dd-item dd3-item" data-id="18">
  225 + <div class="dd-handle dd3-handle">Drag</div><div class="dd3-content">Item 18</div>
  226 + </li>
  227 + </ol>
  228 + </li>
  229 + </ol>
  230 + </div>
  231 +
  232 + </div>
  233 +
  234 + <p class="small">Copyright &copy; <a href="https://dbushell.com/">David Bushell</a> | Made for <a href="http://www.browserlondon.com/">Browser</a></p>
  235 +
  236 +<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
  237 +<script src="jquery.nestable.js"></script>
  238 +<script>
  239 +$(document).ready(function()
  240 +{
  241 + var updateOutput = function(e)
  242 + {
  243 + var list = e.length ? e : $(e.target),
  244 + output = list.data('output');
  245 + if (window.JSON) {
  246 + output.val(window.JSON.stringify(list.nestable('serialize')));//, null, 2));
  247 + } else {
  248 + output.val('JSON browser support required for this demo.');
  249 + }
  250 + };
  251 + // activate Nestable for list 1
  252 + $('#nestable').nestable({
  253 + group: 1
  254 + })
  255 + .on('change', updateOutput);
  256 + // activate Nestable for list 2
  257 + $('#nestable2').nestable({
  258 + group: 1
  259 + })
  260 + .on('change', updateOutput);
  261 + // output initial serialised data
  262 + updateOutput($('#nestable').data('output', $('#nestable-output')));
  263 + updateOutput($('#nestable2').data('output', $('#nestable2-output')));
  264 + $('#nestable-menu').on('click', function(e)
  265 + {
  266 + var target = $(e.target),
  267 + action = target.data('action');
  268 + if (action === 'expand-all') {
  269 + $('.dd').nestable('expandAll');
  270 + }
  271 + if (action === 'collapse-all') {
  272 + $('.dd').nestable('collapseAll');
  273 + }
  274 + });
  275 + $('#nestable3').nestable();
  276 +});
  277 +</script>
  278 +</body>
  279 +</html>
k2shop/usr/k2shop/libs/nestmenu/jquery.nestable.js
  1 +/*!
  2 + * Nestable jQuery Plugin - Copyright (c) 2012 David Bushell - http://dbushell.com/
  3 + * Dual-licensed under the BSD or MIT licenses
  4 + */
  5 +;(function($, window, document, undefined)
  6 +{
  7 + var hasTouch = 'ontouchstart' in document;
  8 +
  9 + /**
  10 + * Detect CSS pointer-events property
  11 + * events are normally disabled on the dragging element to avoid conflicts
  12 + * https://github.com/ausi/Feature-detection-technique-for-pointer-events/blob/master/modernizr-pointerevents.js
  13 + */
  14 + var hasPointerEvents = (function()
  15 + {
  16 + var el = document.createElement('div'),
  17 + docEl = document.documentElement;
  18 + if (!('pointerEvents' in el.style)) {
  19 + return false;
  20 + }
  21 + el.style.pointerEvents = 'auto';
  22 + el.style.pointerEvents = 'x';
  23 + docEl.appendChild(el);
  24 + var supports = window.getComputedStyle && window.getComputedStyle(el, '').pointerEvents === 'auto';
  25 + docEl.removeChild(el);
  26 + return !!supports;
  27 + })();
  28 +
  29 + var defaults = {
  30 + listNodeName : 'ol',
  31 + itemNodeName : 'li',
  32 + rootClass : 'dd',
  33 + listClass : 'dd-list',
  34 + itemClass : 'dd-item',
  35 + dragClass : 'dd-dragel',
  36 + handleClass : 'dd-handle',
  37 + collapsedClass : 'dd-collapsed',
  38 + placeClass : 'dd-placeholder',
  39 + noDragClass : 'dd-nodrag',
  40 + emptyClass : 'dd-empty',
  41 + expandBtnHTML : '<button data-action="expand" type="button">Expand</button>',
  42 + collapseBtnHTML : '<button data-action="collapse" type="button">Collapse</button>',
  43 + group : 0,
  44 + maxDepth : 5,
  45 + threshold : 20
  46 + };
  47 +
  48 + function Plugin(element, options)
  49 + {
  50 + this.w = $(document);
  51 + this.el = $(element);
  52 + this.options = $.extend({}, defaults, options);
  53 + this.init();
  54 + }
  55 +
  56 + Plugin.prototype = {
  57 +
  58 + init: function()
  59 + {
  60 + var list = this;
  61 +
  62 + list.reset();
  63 +
  64 + list.el.data('nestable-group', this.options.group);
  65 +
  66 + list.placeEl = $('<div class="' + list.options.placeClass + '"/>');
  67 +
  68 + $.each(this.el.find(list.options.itemNodeName), function(k, el) {
  69 + list.setParent($(el));
  70 + });
  71 +
  72 + list.el.on('click', 'button', function(e) {
  73 + if (list.dragEl) {
  74 + return;
  75 + }
  76 + var target = $(e.currentTarget),
  77 + action = target.data('action'),
  78 + item = target.parent(list.options.itemNodeName);
  79 + if (action === 'collapse') {
  80 + list.collapseItem(item);
  81 + }
  82 + if (action === 'expand') {
  83 + list.expandItem(item);
  84 + }
  85 + });
  86 +
  87 + var onStartEvent = function(e)
  88 + {
  89 + var handle = $(e.target);
  90 + if (!handle.hasClass(list.options.handleClass)) {
  91 + if (handle.closest('.' + list.options.noDragClass).length) {
  92 + return;
  93 + }
  94 + handle = handle.closest('.' + list.options.handleClass);
  95 + }
  96 +
  97 + if (!handle.length || list.dragEl) {
  98 + return;
  99 + }
  100 +
  101 + list.isTouch = /^touch/.test(e.type);
  102 + if (list.isTouch && e.touches.length !== 1) {
  103 + return;
  104 + }
  105 +
  106 + e.preventDefault();
  107 + list.dragStart(e.touches ? e.touches[0] : e);
  108 + };
  109 +
  110 + var onMoveEvent = function(e)
  111 + {
  112 + if (list.dragEl) {
  113 + e.preventDefault();
  114 + list.dragMove(e.touches ? e.touches[0] : e);
  115 + }
  116 + };
  117 +
  118 + var onEndEvent = function(e)
  119 + {
  120 + if (list.dragEl) {
  121 + e.preventDefault();
  122 + list.dragStop(e.touches ? e.touches[0] : e);
  123 + }
  124 + };
  125 +
  126 + if (hasTouch) {
  127 + list.el[0].addEventListener('touchstart', onStartEvent, false);
  128 + window.addEventListener('touchmove', onMoveEvent, false);
  129 + window.addEventListener('touchend', onEndEvent, false);
  130 + window.addEventListener('touchcancel', onEndEvent, false);
  131 + }
  132 +
  133 + list.el.on('mousedown', onStartEvent);
  134 + list.w.on('mousemove', onMoveEvent);
  135 + list.w.on('mouseup', onEndEvent);
  136 +
  137 + },
  138 +
  139 + serialize: function()
  140 + {
  141 + var data,
  142 + depth = 0,
  143 + list = this;
  144 + step = function(level, depth)
  145 + {
  146 + var array = [ ],
  147 + items = level.children(list.options.itemNodeName);
  148 + items.each(function()
  149 + {
  150 + var li = $(this),
  151 + item = $.extend({}, li.data()),
  152 + sub = li.children(list.options.listNodeName);
  153 + if (sub.length) {
  154 + item.children = step(sub, depth + 1);
  155 + }
  156 + array.push(item);
  157 + });
  158 + return array;
  159 + };
  160 + data = step(list.el.find(list.options.listNodeName).first(), depth);
  161 + return data;
  162 + },
  163 +
  164 + serialise: function()
  165 + {
  166 + return this.serialize();
  167 + },
  168 +
  169 + reset: function()
  170 + {
  171 + this.mouse = {
  172 + offsetX : 0,
  173 + offsetY : 0,
  174 + startX : 0,
  175 + startY : 0,
  176 + lastX : 0,
  177 + lastY : 0,
  178 + nowX : 0,
  179 + nowY : 0,
  180 + distX : 0,
  181 + distY : 0,
  182 + dirAx : 0,
  183 + dirX : 0,
  184 + dirY : 0,
  185 + lastDirX : 0,
  186 + lastDirY : 0,
  187 + distAxX : 0,
  188 + distAxY : 0
  189 + };
  190 + this.isTouch = false;
  191 + this.moving = false;
  192 + this.dragEl = null;
  193 + this.dragRootEl = null;
  194 + this.dragDepth = 0;
  195 + this.hasNewRoot = false;
  196 + this.pointEl = null;
  197 + },
  198 +
  199 + expandItem: function(li)
  200 + {
  201 + li.removeClass(this.options.collapsedClass);
  202 + li.children('[data-action="expand"]').hide();
  203 + li.children('[data-action="collapse"]').show();
  204 + li.children(this.options.listNodeName).show();
  205 + },
  206 +
  207 + collapseItem: function(li)
  208 + {
  209 + var lists = li.children(this.options.listNodeName);
  210 + if (lists.length) {
  211 + li.addClass(this.options.collapsedClass);
  212 + li.children('[data-action="collapse"]').hide();
  213 + li.children('[data-action="expand"]').show();
  214 + li.children(this.options.listNodeName).hide();
  215 + }
  216 + },
  217 +
  218 + expandAll: function()
  219 + {
  220 + var list = this;
  221 + list.el.find(list.options.itemNodeName).each(function() {
  222 + list.expandItem($(this));
  223 + });
  224 + },
  225 +
  226 + collapseAll: function()
  227 + {
  228 + var list = this;
  229 + list.el.find(list.options.itemNodeName).each(function() {
  230 + list.collapseItem($(this));
  231 + });
  232 + },
  233 +
  234 + setParent: function(li)
  235 + {
  236 + if (li.children(this.options.listNodeName).length) {
  237 + li.prepend($(this.options.expandBtnHTML));
  238 + li.prepend($(this.options.collapseBtnHTML));
  239 + }
  240 + li.children('[data-action="expand"]').hide();
  241 + },
  242 +
  243 + unsetParent: function(li)
  244 + {
  245 + li.removeClass(this.options.collapsedClass);
  246 + li.children('[data-action]').remove();
  247 + li.children(this.options.listNodeName).remove();
  248 + },
  249 +
  250 + dragStart: function(e)
  251 + {
  252 + var mouse = this.mouse,
  253 + target = $(e.target),
  254 + dragItem = target.closest(this.options.itemNodeName);
  255 +
  256 + this.placeEl.css('height', dragItem.height());
  257 +
  258 + mouse.offsetX = e.offsetX !== undefined ? e.offsetX : e.pageX - target.offset().left;
  259 + mouse.offsetY = e.offsetY !== undefined ? e.offsetY : e.pageY - target.offset().top;
  260 + mouse.startX = mouse.lastX = e.pageX;
  261 + mouse.startY = mouse.lastY = e.pageY;
  262 +
  263 + this.dragRootEl = this.el;
  264 +
  265 + this.dragEl = $(document.createElement(this.options.listNodeName)).addClass(this.options.listClass + ' ' + this.options.dragClass);
  266 + this.dragEl.css('width', dragItem.width());
  267 +
  268 + dragItem.after(this.placeEl);
  269 + dragItem[0].parentNode.removeChild(dragItem[0]);
  270 + dragItem.appendTo(this.dragEl);
  271 +
  272 + $(document.body).append(this.dragEl);
  273 + this.dragEl.css({
  274 + 'left' : e.pageX - mouse.offsetX,
  275 + 'top' : e.pageY - mouse.offsetY
  276 + });
  277 + // total depth of dragging item
  278 + var i, depth,
  279 + items = this.dragEl.find(this.options.itemNodeName);
  280 + for (i = 0; i < items.length; i++) {
  281 + depth = $(items[i]).parents(this.options.listNodeName).length;
  282 + if (depth > this.dragDepth) {
  283 + this.dragDepth = depth;
  284 + }
  285 + }
  286 + },
  287 +
  288 + dragStop: function(e)
  289 + {
  290 + var el = this.dragEl.children(this.options.itemNodeName).first();
  291 + el[0].parentNode.removeChild(el[0]);
  292 + this.placeEl.replaceWith(el);
  293 +
  294 + this.dragEl.remove();
  295 + this.el.trigger('change');
  296 + if (this.hasNewRoot) {
  297 + this.dragRootEl.trigger('change');
  298 + }
  299 + this.reset();
  300 + },
  301 +
  302 + dragMove: function(e)
  303 + {
  304 + var list, parent, prev, next, depth,
  305 + opt = this.options,
  306 + mouse = this.mouse;
  307 +
  308 + this.dragEl.css({
  309 + 'left' : e.pageX - mouse.offsetX,
  310 + 'top' : e.pageY - mouse.offsetY
  311 + });
  312 +
  313 + // mouse position last events
  314 + mouse.lastX = mouse.nowX;
  315 + mouse.lastY = mouse.nowY;
  316 + // mouse position this events
  317 + mouse.nowX = e.pageX;
  318 + mouse.nowY = e.pageY;
  319 + // distance mouse moved between events
  320 + mouse.distX = mouse.nowX - mouse.lastX;
  321 + mouse.distY = mouse.nowY - mouse.lastY;
  322 + // direction mouse was moving
  323 + mouse.lastDirX = mouse.dirX;
  324 + mouse.lastDirY = mouse.dirY;
  325 + // direction mouse is now moving (on both axis)
  326 + mouse.dirX = mouse.distX === 0 ? 0 : mouse.distX > 0 ? 1 : -1;
  327 + mouse.dirY = mouse.distY === 0 ? 0 : mouse.distY > 0 ? 1 : -1;
  328 + // axis mouse is now moving on
  329 + var newAx = Math.abs(mouse.distX) > Math.abs(mouse.distY) ? 1 : 0;
  330 +
  331 + // do nothing on first move
  332 + if (!mouse.moving) {
  333 + mouse.dirAx = newAx;
  334 + mouse.moving = true;
  335 + return;
  336 + }
  337 +
  338 + // calc distance moved on this axis (and direction)
  339 + if (mouse.dirAx !== newAx) {
  340 + mouse.distAxX = 0;
  341 + mouse.distAxY = 0;
  342 + } else {
  343 + mouse.distAxX += Math.abs(mouse.distX);
  344 + if (mouse.dirX !== 0 && mouse.dirX !== mouse.lastDirX) {
  345 + mouse.distAxX = 0;
  346 + }
  347 + mouse.distAxY += Math.abs(mouse.distY);
  348 + if (mouse.dirY !== 0 && mouse.dirY !== mouse.lastDirY) {
  349 + mouse.distAxY = 0;
  350 + }
  351 + }
  352 + mouse.dirAx = newAx;
  353 +
  354 + /**
  355 + * move horizontal
  356 + */
  357 + if (mouse.dirAx && mouse.distAxX >= opt.threshold) {
  358 + // reset move distance on x-axis for new phase
  359 + mouse.distAxX = 0;
  360 + prev = this.placeEl.prev(opt.itemNodeName);
  361 + // increase horizontal level if previous sibling exists and is not collapsed
  362 + if (mouse.distX > 0 && prev.length && !prev.hasClass(opt.collapsedClass)) {
  363 + // cannot increase level when item above is collapsed
  364 + list = prev.find(opt.listNodeName).last();
  365 + // check if depth limit has reached
  366 + depth = this.placeEl.parents(opt.listNodeName).length;
  367 + if (depth + this.dragDepth <= opt.maxDepth) {
  368 + // create new sub-level if one doesn't exist
  369 + if (!list.length) {
  370 + list = $('<' + opt.listNodeName + '/>').addClass(opt.listClass);
  371 + list.append(this.placeEl);
  372 + prev.append(list);
  373 + this.setParent(prev);
  374 + } else {
  375 + // else append to next level up
  376 + list = prev.children(opt.listNodeName).last();
  377 + list.append(this.placeEl);
  378 + }
  379 + }
  380 + }
  381 + // decrease horizontal level
  382 + if (mouse.distX < 0) {
  383 + // we can't decrease a level if an item preceeds the current one
  384 + next = this.placeEl.next(opt.itemNodeName);
  385 + if (!next.length) {
  386 + parent = this.placeEl.parent();
  387 + this.placeEl.closest(opt.itemNodeName).after(this.placeEl);
  388 + if (!parent.children().length) {
  389 + this.unsetParent(parent.parent());
  390 + }
  391 + }
  392 + }
  393 + }
  394 +
  395 + var isEmpty = false;
  396 +
  397 + // find list item under cursor
  398 + if (!hasPointerEvents) {
  399 + this.dragEl[0].style.visibility = 'hidden';
  400 + }
  401 + this.pointEl = $(document.elementFromPoint(e.pageX - document.body.scrollLeft, e.pageY - (window.pageYOffset || document.documentElement.scrollTop)));
  402 + if (!hasPointerEvents) {
  403 + this.dragEl[0].style.visibility = 'visible';
  404 + }
  405 + if (this.pointEl.hasClass(opt.handleClass)) {
  406 + this.pointEl = this.pointEl.parent(opt.itemNodeName);
  407 + }
  408 + if (this.pointEl.hasClass(opt.emptyClass)) {
  409 + isEmpty = true;
  410 + }
  411 + else if (!this.pointEl.length || !this.pointEl.hasClass(opt.itemClass)) {
  412 + return;
  413 + }
  414 +
  415 + // find parent list of item under cursor
  416 + var pointElRoot = this.pointEl.closest('.' + opt.rootClass),
  417 + isNewRoot = this.dragRootEl.data('nestable-id') !== pointElRoot.data('nestable-id');
  418 +
  419 + /**
  420 + * move vertical
  421 + */
  422 + if (!mouse.dirAx || isNewRoot || isEmpty) {
  423 + // check if groups match if dragging over new root
  424 + if (isNewRoot && opt.group !== pointElRoot.data('nestable-group')) {
  425 + return;
  426 + }
  427 + // check depth limit
  428 + depth = this.dragDepth - 1 + this.pointEl.parents(opt.listNodeName).length;
  429 + if (depth > opt.maxDepth) {
  430 + return;
  431 + }
  432 + var before = e.pageY < (this.pointEl.offset().top + this.pointEl.height() / 2);
  433 + parent = this.placeEl.parent();
  434 + // if empty create new list to replace empty placeholder
  435 + if (isEmpty) {
  436 + list = $(document.createElement(opt.listNodeName)).addClass(opt.listClass);
  437 + list.append(this.placeEl);
  438 + this.pointEl.replaceWith(list);
  439 + }
  440 + else if (before) {
  441 + this.pointEl.before(this.placeEl);
  442 + }
  443 + else {
  444 + this.pointEl.after(this.placeEl);
  445 + }
  446 + if (!parent.children().length) {
  447 + this.unsetParent(parent.parent());
  448 + }
  449 + if (!this.dragRootEl.find(opt.itemNodeName).length) {
  450 + this.dragRootEl.append('<div class="' + opt.emptyClass + '"/>');
  451 + }
  452 + // parent root list has changed
  453 + if (isNewRoot) {
  454 + this.dragRootEl = pointElRoot;
  455 + this.hasNewRoot = this.el[0] !== this.dragRootEl[0];
  456 + }
  457 + }
  458 + }
  459 +
  460 + };
  461 +
  462 + $.fn.nestable = function(params)
  463 + {
  464 + var lists = this,
  465 + retval = this;
  466 +
  467 + lists.each(function()
  468 + {
  469 + var plugin = $(this).data("nestable");
  470 +
  471 + if (!plugin) {
  472 + $(this).data("nestable", new Plugin(this, params));
  473 + $(this).data("nestable-id", new Date().getTime());
  474 + } else {
  475 + if (typeof params === 'string' && typeof plugin[params] === 'function') {
  476 + retval = plugin[params]();
  477 + }
  478 + }
  479 + });
  480 +
  481 + return retval || lists;
  482 + };
  483 +
  484 +})(window.jQuery || window.Zepto, window, document);
k2shop/usr/k2shop/libs/nestmenu/nestable.css
  1 +
  2 +/**
  3 + * Nestable
  4 + */
  5 +.dd { position: relative; display: block; margin: 0; padding: 0; /*max-width: 600px;*/ list-style: none; font-size: 13px; line-height: 20px; overflow:scroll; height:600px; }
  6 +.dd-list { display: block; position: relative; margin: 0; padding: 0; list-style: none; }
  7 +.dd-list .dd-list { padding-left: 30px; }
  8 +.dd-collapsed .dd-list { display: none; }
  9 +.dd-item,
  10 +.dd-empty,
  11 +.dd-placeholder { display: block; position: relative; margin: 0; padding: 0; min-height: 20px; font-size: 13px; line-height: 20px; }
  12 +.dd-handle { display: block; height: 30px; margin: 5px 0; padding: 5px 10px; color: #333; text-decoration: none; font-weight: bold; border: 1px solid #ccc; width:650px;
  13 + background: #fafafa;
  14 + background: -webkit-linear-gradient(top, #fafafa 0%, #eee 100%);
  15 + background: -moz-linear-gradient(top, #fafafa 0%, #eee 100%);
  16 + background: linear-gradient(top, #fafafa 0%, #eee 100%);
  17 + -webkit-border-radius: 3px;
  18 + border-radius: 3px;
  19 + box-sizing: border-box; -moz-box-sizing: border-box;
  20 +}
  21 +.dd-handle:hover { color: #2ea8e5; background: #fff; }
  22 +.dd-item > button { display: block; position: relative; cursor: pointer; float: left; width: 25px; height: 20px; margin: 5px 0; padding: 0; text-indent: 100%; white-space: nowrap; overflow: hidden; border: 0; background: transparent; font-size: 12px; line-height: 1; text-align: center; font-weight: bold; }
  23 +.dd-item > button:before { content: '+'; display: block; position: absolute; width: 100%; text-align: center; text-indent: 0; }
  24 +.dd-item > button[data-action="collapse"]:before { content: '-'; }
  25 +.dd-placeholder,
  26 +.dd-empty { margin: 5px 0; padding: 0; min-height: 30px; background: #f2fbff; border: 1px dashed #b6bcbf; box-sizing: border-box; -moz-box-sizing: border-box; }
  27 +.dd-empty { border: 1px dashed #bbb; min-height: 100px; background-color: #e5e5e5;
  28 + background-image: -webkit-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff),
  29 + -webkit-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff);
  30 + background-image: -moz-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff),
  31 + -moz-linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff);
  32 + background-image: linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff),
  33 + linear-gradient(45deg, #fff 25%, transparent 25%, transparent 75%, #fff 75%, #fff);
  34 + background-size: 60px 60px;
  35 + background-position: 0 0, 30px 30px;
  36 +}
  37 +.dd-dragel { position: absolute; pointer-events: none; z-index: 9999; }
  38 +.dd-dragel > .dd-item .dd-handle { margin-top: 0; }
  39 +.dd-dragel .dd-handle {
  40 + -webkit-box-shadow: 2px 4px 6px 0 rgba(0,0,0,.1);
  41 + box-shadow: 2px 4px 6px 0 rgba(0,0,0,.1);
  42 +}
  43 +/**
  44 + * Nestable Extras
  45 + */
  46 +.nestable-lists { display: block; clear: both; padding: 30px 0; width: 100%; border: 0; border-top: 2px solid #ddd; border-bottom: 2px solid #ddd; }
  47 +#nestable-menu { padding: 0; margin: 20px 0; }
  48 +#nestable-output,
  49 +#nestable2-output { width: 100%; height: 7em; font-size: 0.75em; line-height: 1.333333em; font-family: Consolas, monospace; padding: 5px; box-sizing: border-box; -moz-box-sizing: border-box; }
  50 +#nestable2 .dd-handle {
  51 + color: #fff;
  52 + border: 1px solid #999;
  53 + background: #bbb;
  54 + background: -webkit-linear-gradient(top, #bbb 0%, #999 100%);
  55 + background: -moz-linear-gradient(top, #bbb 0%, #999 100%);
  56 + background: linear-gradient(top, #bbb 0%, #999 100%);
  57 +}
  58 +#nestable2 .dd-handle:hover { background: #bbb; }
  59 +#nestable2 .dd-item > button:before { color: #fff; }
  60 +@media only screen and (min-width: 700px) {
  61 + /*.dd { float: left; width: 48%; }*/
  62 + .dd + .dd { margin-left: 2%; }
  63 +}
  64 +.dd-hover > .dd-handle { background: #2ea8e5 !important; }
  65 +/**
  66 + * Nestable Draggable Handles
  67 + */
  68 +.dd3-content { display: block; height: 30px; margin: 5px 0; padding: 5px 10px 5px 40px; color: #333; text-decoration: none; font-weight: bold; border: 1px solid #ccc;
  69 + background: #fafafa;
  70 + background: -webkit-linear-gradient(top, #fafafa 0%, #eee 100%);
  71 + background: -moz-linear-gradient(top, #fafafa 0%, #eee 100%);
  72 + background: linear-gradient(top, #fafafa 0%, #eee 100%);
  73 + -webkit-border-radius: 3px;
  74 + border-radius: 3px;
  75 + box-sizing: border-box; -moz-box-sizing: border-box;
  76 +}
  77 +.dd3-content:hover { color: #2ea8e5; background: #fff; }
  78 +.dd-dragel > .dd3-item > .dd3-content { margin: 0; }
  79 +.dd3-item > button { margin-left: 30px; }
  80 +.dd3-handle { position: absolute; margin: 0; left: 0; top: 0; cursor: pointer; width: 30px; text-indent: 100%; white-space: nowrap; overflow: hidden;
  81 + border: 1px solid #aaa;
  82 + background: #ddd;
  83 + background: -webkit-linear-gradient(top, #ddd 0%, #bbb 100%);
  84 + background: -moz-linear-gradient(top, #ddd 0%, #bbb 100%);
  85 + background: linear-gradient(top, #ddd 0%, #bbb 100%);
  86 + border-top-right-radius: 0;
  87 + border-bottom-right-radius: 0;
  88 +}
  89 +.dd3-handle:before { content: '?'; display: block; position: absolute; left: 0; top: 3px; width: 100%; text-align: center; text-indent: 0; color: #fff; font-size: 20px; font-weight: normal; }
  90 +.dd3-handle:hover { background: #ddd; }
  91 +/**
  92 + * Socialite
  93 + */
  94 +.socialite { display: block; float: left; height: 35px; }
k2shop/usr/k2shop/libs/yaml/Spyc.php
  1 +<?php
  2 +/**
  3 + * Spyc -- A Simple PHP YAML Class
  4 + * @version 0.6.2
  5 + * @author Vlad Andersen <vlad.andersen@gmail.com>
  6 + * @author Chris Wanstrath <chris@ozmm.org>
  7 + * @link https://github.com/mustangostang/spyc/
  8 + * @copyright Copyright 2005-2006 Chris Wanstrath, 2006-2011 Vlad Andersen
  9 + * @license http://www.opensource.org/licenses/mit-license.php MIT License
  10 + * @package Spyc
  11 + */
  12 +if (!function_exists('spyc_load')) {
  13 + /**
  14 + * Parses YAML to array.
  15 + * @param string $string YAML string.
  16 + * @return array
  17 + */
  18 + function spyc_load ($string) {
  19 + return Spyc::YAMLLoadString($string);
  20 + }
  21 +}
  22 +if (!function_exists('spyc_load_file')) {
  23 + /**
  24 + * Parses YAML to array.
  25 + * @param string $file Path to YAML file.
  26 + * @return array
  27 + */
  28 + function spyc_load_file ($file) {
  29 + return Spyc::YAMLLoad($file);
  30 + }
  31 +}
  32 +if (!function_exists('spyc_dump')) {
  33 + /**
  34 + * Dumps array to YAML.
  35 + * @param array $data Array.
  36 + * @return string
  37 + */
  38 + function spyc_dump ($data) {
  39 + return Spyc::YAMLDump($data, false, false, true);
  40 + }
  41 +}
  42 +if (!class_exists('Spyc')) {
  43 +/**
  44 + * The Simple PHP YAML Class.
  45 + *
  46 + * This class can be used to read a YAML file and convert its contents
  47 + * into a PHP array. It currently supports a very limited subsection of
  48 + * the YAML spec.
  49 + *
  50 + * Usage:
  51 + * <code>
  52 + * $Spyc = new Spyc;
  53 + * $array = $Spyc->load($file);
  54 + * </code>
  55 + * or:
  56 + * <code>
  57 + * $array = Spyc::YAMLLoad($file);
  58 + * </code>
  59 + * or:
  60 + * <code>
  61 + * $array = spyc_load_file($file);
  62 + * </code>
  63 + * @package Spyc
  64 + */
  65 +class Spyc {
  66 + // SETTINGS
  67 + const REMPTY = "\0\0\0\0\0";
  68 + /**
  69 + * Setting this to true will force YAMLDump to enclose any string value in
  70 + * quotes. False by default.
  71 + *
  72 + * @var bool
  73 + */
  74 + public $setting_dump_force_quotes = false;
  75 + /**
  76 + * Setting this to true will forse YAMLLoad to use syck_load function when
  77 + * possible. False by default.
  78 + * @var bool
  79 + */
  80 + public $setting_use_syck_is_possible = false;
  81 + /**#@+
  82 + * @access private
  83 + * @var mixed
  84 + */
  85 + private $_dumpIndent;
  86 + private $_dumpWordWrap;
  87 + private $_containsGroupAnchor = false;
  88 + private $_containsGroupAlias = false;
  89 + private $path;
  90 + private $result;
  91 + private $LiteralPlaceHolder = '___YAML_Literal_Block___';
  92 + private $SavedGroups = array();
  93 + private $indent;
  94 + /**
  95 + * Path modifier that should be applied after adding current element.
  96 + * @var array
  97 + */
  98 + private $delayedPath = array();
  99 + /**#@+
  100 + * @access public
  101 + * @var mixed
  102 + */
  103 + public $_nodeId;
  104 +/**
  105 + * Load a valid YAML string to Spyc.
  106 + * @param string $input
  107 + * @return array
  108 + */
  109 + public function load ($input) {
  110 + return $this->_loadString($input);
  111 + }
  112 + /**
  113 + * Load a valid YAML file to Spyc.
  114 + * @param string $file
  115 + * @return array
  116 + */
  117 + public function loadFile ($file) {
  118 + return $this->_load($file);
  119 + }
  120 + /**
  121 + * Load YAML into a PHP array statically
  122 + *
  123 + * The load method, when supplied with a YAML stream (string or file),
  124 + * will do its best to convert YAML in a file into a PHP array. Pretty
  125 + * simple.
  126 + * Usage:
  127 + * <code>
  128 + * $array = Spyc::YAMLLoad('lucky.yaml');
  129 + * print_r($array);
  130 + * </code>
  131 + * @access public
  132 + * @return array
  133 + * @param string $input Path of YAML file or string containing YAML
  134 + */
  135 + public static function YAMLLoad($input) {
  136 + $Spyc = new Spyc;
  137 + return $Spyc->_load($input);
  138 + }
  139 + /**
  140 + * Load a string of YAML into a PHP array statically
  141 + *
  142 + * The load method, when supplied with a YAML string, will do its best
  143 + * to convert YAML in a string into a PHP array. Pretty simple.
  144 + *
  145 + * Note: use this function if you don't want files from the file system
  146 + * loaded and processed as YAML. This is of interest to people concerned
  147 + * about security whose input is from a string.
  148 + *
  149 + * Usage:
  150 + * <code>
  151 + * $array = Spyc::YAMLLoadString("---\n0: hello world\n");
  152 + * print_r($array);
  153 + * </code>
  154 + * @access public
  155 + * @return array
  156 + * @param string $input String containing YAML
  157 + */
  158 + public static function YAMLLoadString($input) {
  159 + $Spyc = new Spyc;
  160 + return $Spyc->_loadString($input);
  161 + }
  162 + /**
  163 + * Dump YAML from PHP array statically
  164 + *
  165 + * The dump method, when supplied with an array, will do its best
  166 + * to convert the array into friendly YAML. Pretty simple. Feel free to
  167 + * save the returned string as nothing.yaml and pass it around.
  168 + *
  169 + * Oh, and you can decide how big the indent is and what the wordwrap
  170 + * for folding is. Pretty cool -- just pass in 'false' for either if
  171 + * you want to use the default.
  172 + *
  173 + * Indent's default is 2 spaces, wordwrap's default is 40 characters. And
  174 + * you can turn off wordwrap by passing in 0.
  175 + *
  176 + * @access public
  177 + * @return string
  178 + * @param array|\stdClass $array PHP array
  179 + * @param int $indent Pass in false to use the default, which is 2
  180 + * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
  181 + * @param bool $no_opening_dashes Do not start YAML file with "---\n"
  182 + */
  183 + public static function YAMLDump($array, $indent = false, $wordwrap = false, $no_opening_dashes = false) {
  184 + $spyc = new Spyc;
  185 + return $spyc->dump($array, $indent, $wordwrap, $no_opening_dashes);
  186 + }
  187 + /**
  188 + * Dump PHP array to YAML
  189 + *
  190 + * The dump method, when supplied with an array, will do its best
  191 + * to convert the array into friendly YAML. Pretty simple. Feel free to
  192 + * save the returned string as tasteful.yaml and pass it around.
  193 + *
  194 + * Oh, and you can decide how big the indent is and what the wordwrap
  195 + * for folding is. Pretty cool -- just pass in 'false' for either if
  196 + * you want to use the default.
  197 + *
  198 + * Indent's default is 2 spaces, wordwrap's default is 40 characters. And
  199 + * you can turn off wordwrap by passing in 0.
  200 + *
  201 + * @access public
  202 + * @return string
  203 + * @param array $array PHP array
  204 + * @param int $indent Pass in false to use the default, which is 2
  205 + * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
  206 + */
  207 + public function dump($array,$indent = false,$wordwrap = false, $no_opening_dashes = false) {
  208 + // Dumps to some very clean YAML. We'll have to add some more features
  209 + // and options soon. And better support for folding.
  210 + // New features and options.
  211 + if ($indent === false or !is_numeric($indent)) {
  212 + $this->_dumpIndent = 2;
  213 + } else {
  214 + $this->_dumpIndent = $indent;
  215 + }
  216 + if ($wordwrap === false or !is_numeric($wordwrap)) {
  217 + $this->_dumpWordWrap = 40;
  218 + } else {
  219 + $this->_dumpWordWrap = $wordwrap;
  220 + }
  221 + // New YAML document
  222 + $string = "";
  223 + if (!$no_opening_dashes) $string = "---\n";
  224 + // Start at the base of the array and move through it.
  225 + if ($array) {
  226 + $array = (array)$array;
  227 + $previous_key = -1;
  228 + foreach ($array as $key => $value) {
  229 + if (!isset($first_key)) $first_key = $key;
  230 + $string .= $this->_yamlize($key,$value,0,$previous_key, $first_key, $array);
  231 + $previous_key = $key;
  232 + }
  233 + }
  234 + return $string;
  235 + }
  236 + /**
  237 + * Attempts to convert a key / value array item to YAML
  238 + * @access private
  239 + * @return string
  240 + * @param $key The name of the key
  241 + * @param $value The value of the item
  242 + * @param $indent The indent of the current node
  243 + */
  244 + private function _yamlize($key,$value,$indent, $previous_key = -1, $first_key = 0, $source_array = null) {
  245 + if(is_object($value)) $value = (array)$value;
  246 + if (is_array($value)) {
  247 + if (empty ($value))
  248 + return $this->_dumpNode($key, array(), $indent, $previous_key, $first_key, $source_array);
  249 + // It has children. What to do?
  250 + // Make it the right kind of item
  251 + $string = $this->_dumpNode($key, self::REMPTY, $indent, $previous_key, $first_key, $source_array);
  252 + // Add the indent
  253 + $indent += $this->_dumpIndent;
  254 + // Yamlize the array
  255 + $string .= $this->_yamlizeArray($value,$indent);
  256 + } elseif (!is_array($value)) {
  257 + // It doesn't have children. Yip.
  258 + $string = $this->_dumpNode($key, $value, $indent, $previous_key, $first_key, $source_array);
  259 + }
  260 + return $string;
  261 + }
  262 + /**
  263 + * Attempts to convert an array to YAML
  264 + * @access private
  265 + * @return string
  266 + * @param $array The array you want to convert
  267 + * @param $indent The indent of the current level
  268 + */
  269 + private function _yamlizeArray($array,$indent) {
  270 + if (is_array($array)) {
  271 + $string = '';
  272 + $previous_key = -1;
  273 + foreach ($array as $key => $value) {
  274 + if (!isset($first_key)) $first_key = $key;
  275 + $string .= $this->_yamlize($key, $value, $indent, $previous_key, $first_key, $array);
  276 + $previous_key = $key;
  277 + }
  278 + return $string;
  279 + } else {
  280 + return false;
  281 + }
  282 + }
  283 + /**
  284 + * Returns YAML from a key and a value
  285 + * @access private
  286 + * @return string
  287 + * @param $key The name of the key
  288 + * @param $value The value of the item
  289 + * @param $indent The indent of the current node
  290 + */
  291 + private function _dumpNode($key, $value, $indent, $previous_key = -1, $first_key = 0, $source_array = null) {
  292 + // do some folding here, for blocks
  293 + if (is_string ($value) && ((strpos($value,"\n") !== false || strpos($value,": ") !== false || strpos($value,"- ") !== false ||
  294 + strpos($value,"*") !== false || strpos($value,"#") !== false || strpos($value,"<") !== false || strpos($value,">") !== false || strpos ($value, '%') !== false || strpos ($value, ' ') !== false ||
  295 + strpos($value,"[") !== false || strpos($value,"]") !== false || strpos($value,"{") !== false || strpos($value,"}") !== false) || strpos($value,"&") !== false || strpos($value, "'") !== false || strpos($value, "!") === 0 ||
  296 + substr ($value, -1, 1) == ':')
  297 + ) {
  298 + $value = $this->_doLiteralBlock($value,$indent);
  299 + } else {
  300 + $value = $this->_doFolding($value,$indent);
  301 + }
  302 + if ($value === array()) $value = '[ ]';
  303 + if ($value === "") $value = '""';
  304 + if (self::isTranslationWord($value)) {
  305 + $value = $this->_doLiteralBlock($value, $indent);
  306 + }
  307 + if (trim ($value) != $value)
  308 + $value = $this->_doLiteralBlock($value,$indent);
  309 + if (is_bool($value)) {
  310 + $value = $value ? "true" : "false";
  311 + }
  312 + if ($value === null) $value = 'null';
  313 + if ($value === "'" . self::REMPTY . "'") $value = null;
  314 + $spaces = str_repeat(' ',$indent);
  315 + //if (is_int($key) && $key - 1 == $previous_key && $first_key===0) {
  316 + if (is_array ($source_array) && array_keys($source_array) === range(0, count($source_array) - 1)) {
  317 + // It's a sequence
  318 + $string = $spaces.'- '.$value."\n";
  319 + } else {
  320 + // if ($first_key===0) throw new Exception('Keys are all screwy. The first one was zero, now it\'s "'. $key .'"');
  321 + // It's mapped
  322 + if (strpos($key, ":") !== false || strpos($key, "#") !== false) { $key = '"' . $key . '"'; }
  323 + $string = rtrim ($spaces.$key.': '.$value)."\n";
  324 + }
  325 + return $string;
  326 + }
  327 + /**
  328 + * Creates a literal block for dumping
  329 + * @access private
  330 + * @return string
  331 + * @param $value
  332 + * @param $indent int The value of the indent
  333 + */
  334 + private function _doLiteralBlock($value,$indent) {
  335 + if ($value === "\n") return '\n';
  336 + if (strpos($value, "\n") === false && strpos($value, "'") === false) {
  337 + return sprintf ("'%s'", $value);
  338 + }
  339 + if (strpos($value, "\n") === false && strpos($value, '"') === false) {
  340 + return sprintf ('"%s"', $value);
  341 + }
  342 + $exploded = explode("\n",$value);
  343 + $newValue = '|';
  344 + if (isset($exploded[0]) && ($exploded[0] == "|" || $exploded[0] == "|-" || $exploded[0] == ">")) {
  345 + $newValue = $exploded[0];
  346 + unset($exploded[0]);
  347 + }
  348 + $indent += $this->_dumpIndent;
  349 + $spaces = str_repeat(' ',$indent);
  350 + foreach ($exploded as $line) {
  351 + $line = trim($line);
  352 + if (strpos($line, '"') === 0 && strrpos($line, '"') == (strlen($line)-1) || strpos($line, "'") === 0 && strrpos($line, "'") == (strlen($line)-1)) {
  353 + $line = substr($line, 1, -1);
  354 + }
  355 + $newValue .= "\n" . $spaces . ($line);
  356 + }
  357 + return $newValue;
  358 + }
  359 + /**
  360 + * Folds a string of text, if necessary
  361 + * @access private
  362 + * @return string
  363 + * @param $value The string you wish to fold
  364 + */
  365 + private function _doFolding($value,$indent) {
  366 + // Don't do anything if wordwrap is set to 0
  367 + if ($this->_dumpWordWrap !== 0 && is_string ($value) && strlen($value) > $this->_dumpWordWrap) {
  368 + $indent += $this->_dumpIndent;
  369 + $indent = str_repeat(' ',$indent);
  370 + $wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent");
  371 + $value = ">\n".$indent.$wrapped;
  372 + } else {
  373 + if ($this->setting_dump_force_quotes && is_string ($value) && $value !== self::REMPTY)
  374 + $value = '"' . $value . '"';
  375 + if (is_numeric($value) && is_string($value))
  376 + $value = '"' . $value . '"';
  377 + }
  378 + return $value;
  379 + }
  380 + private function isTrueWord($value) {
  381 + $words = self::getTranslations(array('true', 'on', 'yes', 'y'));
  382 + return in_array($value, $words, true);
  383 + }
  384 + private function isFalseWord($value) {
  385 + $words = self::getTranslations(array('false', 'off', 'no', 'n'));
  386 + return in_array($value, $words, true);
  387 + }
  388 + private function isNullWord($value) {
  389 + $words = self::getTranslations(array('null', '~'));
  390 + return in_array($value, $words, true);
  391 + }
  392 + private function isTranslationWord($value) {
  393 + return (
  394 + self::isTrueWord($value) ||
  395 + self::isFalseWord($value) ||
  396 + self::isNullWord($value)
  397 + );
  398 + }
  399 + /**
  400 + * Coerce a string into a native type
  401 + * Reference: http://yaml.org/type/bool.html
  402 + * TODO: Use only words from the YAML spec.
  403 + * @access private
  404 + * @param $value The value to coerce
  405 + */
  406 + private function coerceValue(&$value) {
  407 + if (self::isTrueWord($value)) {
  408 + $value = true;
  409 + } else if (self::isFalseWord($value)) {
  410 + $value = false;
  411 + } else if (self::isNullWord($value)) {
  412 + $value = null;
  413 + }
  414 + }
  415 + /**
  416 + * Given a set of words, perform the appropriate translations on them to
  417 + * match the YAML 1.1 specification for type coercing.
  418 + * @param $words The words to translate
  419 + * @access private
  420 + */
  421 + private static function getTranslations(array $words) {
  422 + $result = array();
  423 + foreach ($words as $i) {
  424 + $result = array_merge($result, array(ucfirst($i), strtoupper($i), strtolower($i)));
  425 + }
  426 + return $result;
  427 + }
  428 +// LOADING FUNCTIONS
  429 + private function _load($input) {
  430 + $Source = $this->loadFromSource($input);
  431 + return $this->loadWithSource($Source);
  432 + }
  433 + private function _loadString($input) {
  434 + $Source = $this->loadFromString($input);
  435 + return $this->loadWithSource($Source);
  436 + }
  437 + private function loadWithSource($Source) {
  438 + if (empty ($Source)) return array();
  439 + if ($this->setting_use_syck_is_possible && function_exists ('syck_load')) {
  440 + $array = syck_load (implode ("\n", $Source));
  441 + return is_array($array) ? $array : array();
  442 + }
  443 + $this->path = array();
  444 + $this->result = array();
  445 + $cnt = count($Source);
  446 + for ($i = 0; $i < $cnt; $i++) {
  447 + $line = $Source[$i];
  448 + $this->indent = strlen($line) - strlen(ltrim($line));
  449 + $tempPath = $this->getParentPathByIndent($this->indent);
  450 + $line = self::stripIndent($line, $this->indent);
  451 + if (self::isComment($line)) continue;
  452 + if (self::isEmpty($line)) continue;
  453 + $this->path = $tempPath;
  454 + $literalBlockStyle = self::startsLiteralBlock($line);
  455 + if ($literalBlockStyle) {
  456 + $line = rtrim ($line, $literalBlockStyle . " \n");
  457 + $literalBlock = '';
  458 + $line .= ' '.$this->LiteralPlaceHolder;
  459 + $literal_block_indent = strlen($Source[$i+1]) - strlen(ltrim($Source[$i+1]));
  460 + while (++$i < $cnt && $this->literalBlockContinues($Source[$i], $this->indent)) {
  461 + $literalBlock = $this->addLiteralLine($literalBlock, $Source[$i], $literalBlockStyle, $literal_block_indent);
  462 + }
  463 + $i--;
  464 + }
  465 + // Strip out comments
  466 + if (strpos ($line, '#')) {
  467 + $line = preg_replace('/\s*#([^"\']+)$/','',$line);
  468 + }
  469 + while (++$i < $cnt && self::greedilyNeedNextLine($line)) {
  470 + $line = rtrim ($line, " \n\t\r") . ' ' . ltrim ($Source[$i], " \t");
  471 + }
  472 + $i--;
  473 + $lineArray = $this->_parseLine($line);
  474 + if ($literalBlockStyle)
  475 + $lineArray = $this->revertLiteralPlaceHolder ($lineArray, $literalBlock);
  476 + $this->addArray($lineArray, $this->indent);
  477 + foreach ($this->delayedPath as $indent => $delayedPath)
  478 + $this->path[$indent] = $delayedPath;
  479 + $this->delayedPath = array();
  480 + }
  481 + return $this->result;
  482 + }
  483 + private function loadFromSource ($input) {
  484 + if (!empty($input) && strpos($input, "\n") === false && file_exists($input))
  485 + $input = file_get_contents($input);
  486 + return $this->loadFromString($input);
  487 + }
  488 + private function loadFromString ($input) {
  489 + $lines = explode("\n",$input);
  490 + foreach ($lines as $k => $_) {
  491 + $lines[$k] = rtrim ($_, "\r");
  492 + }
  493 + return $lines;
  494 + }
  495 + /**
  496 + * Parses YAML code and returns an array for a node
  497 + * @access private
  498 + * @return array
  499 + * @param string $line A line from the YAML file
  500 + */
  501 + private function _parseLine($line) {
  502 + if (!$line) return array();
  503 + $line = trim($line);
  504 + if (!$line) return array();
  505 + $array = array();
  506 + $group = $this->nodeContainsGroup($line);
  507 + if ($group) {
  508 + $this->addGroup($line, $group);
  509 + $line = $this->stripGroup ($line, $group);
  510 + }
  511 + if ($this->startsMappedSequence($line))
  512 + return $this->returnMappedSequence($line);
  513 + if ($this->startsMappedValue($line))
  514 + return $this->returnMappedValue($line);
  515 + if ($this->isArrayElement($line))
  516 + return $this->returnArrayElement($line);
  517 + if ($this->isPlainArray($line))
  518 + return $this->returnPlainArray($line);
  519 + return $this->returnKeyValuePair($line);
  520 + }
  521 + /**
  522 + * Finds the type of the passed value, returns the value as the new type.
  523 + * @access private
  524 + * @param string $value
  525 + * @return mixed
  526 + */
  527 + private function _toType($value) {
  528 + if ($value === '') return "";
  529 + $first_character = $value[0];
  530 + $last_character = substr($value, -1, 1);
  531 + $is_quoted = false;
  532 + do {
  533 + if (!$value) break;
  534 + if ($first_character != '"' && $first_character != "'") break;
  535 + if ($last_character != '"' && $last_character != "'") break;
  536 + $is_quoted = true;
  537 + } while (0);
  538 + if ($is_quoted) {
  539 + $value = str_replace('\n', "\n", $value);
  540 + if ($first_character == "'")
  541 + return strtr(substr ($value, 1, -1), array ('\'\'' => '\'', '\\\''=> '\''));
  542 + return strtr(substr ($value, 1, -1), array ('\\"' => '"', '\\\''=> '\''));
  543 + }
  544 + if (strpos($value, ' #') !== false && !$is_quoted)
  545 + $value = preg_replace('/\s+#(.+)$/','',$value);
  546 + if ($first_character == '[' && $last_character == ']') {
  547 + // Take out strings sequences and mappings
  548 + $innerValue = trim(substr ($value, 1, -1));
  549 + if ($innerValue === '') return array();
  550 + $explode = $this->_inlineEscape($innerValue);
  551 + // Propagate value array
  552 + $value = array();
  553 + foreach ($explode as $v) {
  554 + $value[] = $this->_toType($v);
  555 + }
  556 + return $value;
  557 + }
  558 + if (strpos($value,': ')!==false && $first_character != '{') {
  559 + $array = explode(': ',$value);
  560 + $key = trim($array[0]);
  561 + array_shift($array);
  562 + $value = trim(implode(': ',$array));
  563 + $value = $this->_toType($value);
  564 + return array($key => $value);
  565 + }
  566 + if ($first_character == '{' && $last_character == '}') {
  567 + $innerValue = trim(substr ($value, 1, -1));
  568 + if ($innerValue === '') return array();
  569 + // Inline Mapping
  570 + // Take out strings sequences and mappings
  571 + $explode = $this->_inlineEscape($innerValue);
  572 + // Propagate value array
  573 + $array = array();
  574 + foreach ($explode as $v) {
  575 + $SubArr = $this->_toType($v);
  576 + if (empty($SubArr)) continue;
  577 + if (is_array ($SubArr)) {
  578 + $array[key($SubArr)] = $SubArr[key($SubArr)]; continue;
  579 + }
  580 + $array[] = $SubArr;
  581 + }
  582 + return $array;
  583 + }
  584 + if ($value == 'null' || $value == 'NULL' || $value == 'Null' || $value == '' || $value == '~') {
  585 + return null;
  586 + }
  587 + if ( is_numeric($value) && preg_match ('/^(-|)[1-9]+[0-9]*$/', $value) ){
  588 + $intvalue = (int)$value;
  589 + if ($intvalue != PHP_INT_MAX && $intvalue != ~PHP_INT_MAX)
  590 + $value = $intvalue;
  591 + return $value;
  592 + }
  593 + if ( is_string($value) && preg_match('/^0[xX][0-9a-fA-F]+$/', $value)) {
  594 + // Hexadecimal value.
  595 + return hexdec($value);
  596 + }
  597 + $this->coerceValue($value);
  598 + if (is_numeric($value)) {
  599 + if ($value === '0') return 0;
  600 + if (rtrim ($value, 0) === $value)
  601 + $value = (float)$value;
  602 + return $value;
  603 + }
  604 + return $value;
  605 + }
  606 + /**
  607 + * Used in inlines to check for more inlines or quoted strings
  608 + * @access private
  609 + * @return array
  610 + */
  611 + private function _inlineEscape($inline) {
  612 + // There's gotta be a cleaner way to do this...
  613 + // While pure sequences seem to be nesting just fine,
  614 + // pure mappings and mappings with sequences inside can't go very
  615 + // deep. This needs to be fixed.
  616 + $seqs = array();
  617 + $maps = array();
  618 + $saved_strings = array();
  619 + $saved_empties = array();
  620 + // Check for empty strings
  621 + $regex = '/("")|(\'\')/';
  622 + if (preg_match_all($regex,$inline,$strings)) {
  623 + $saved_empties = $strings[0];
  624 + $inline = preg_replace($regex,'YAMLEmpty',$inline);
  625 + }
  626 + unset($regex);
  627 + // Check for strings
  628 + $regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/';
  629 + if (preg_match_all($regex,$inline,$strings)) {
  630 + $saved_strings = $strings[0];
  631 + $inline = preg_replace($regex,'YAMLString',$inline);
  632 + }
  633 + unset($regex);
  634 + // echo $inline;
  635 + $i = 0;
  636 + do {
  637 + // Check for sequences
  638 + while (preg_match('/\[([^{}\[\]]+)\]/U',$inline,$matchseqs)) {
  639 + $seqs[] = $matchseqs[0];
  640 + $inline = preg_replace('/\[([^{}\[\]]+)\]/U', ('YAMLSeq' . (count($seqs) - 1) . 's'), $inline, 1);
  641 + }
  642 + // Check for mappings
  643 + while (preg_match('/{([^\[\]{}]+)}/U',$inline,$matchmaps)) {
  644 + $maps[] = $matchmaps[0];
  645 + $inline = preg_replace('/{([^\[\]{}]+)}/U', ('YAMLMap' . (count($maps) - 1) . 's'), $inline, 1);
  646 + }
  647 + if ($i++ >= 10) break;
  648 + } while (strpos ($inline, '[') !== false || strpos ($inline, '{') !== false);
  649 + $explode = explode(',',$inline);
  650 + $explode = array_map('trim', $explode);
  651 + $stringi = 0; $i = 0;
  652 + while (1) {
  653 + // Re-add the sequences
  654 + if (!empty($seqs)) {
  655 + foreach ($explode as $key => $value) {
  656 + if (strpos($value,'YAMLSeq') !== false) {
  657 + foreach ($seqs as $seqk => $seq) {
  658 + $explode[$key] = str_replace(('YAMLSeq'.$seqk.'s'),$seq,$value);
  659 + $value = $explode[$key];
  660 + }
  661 + }
  662 + }
  663 + }
  664 + // Re-add the mappings
  665 + if (!empty($maps)) {
  666 + foreach ($explode as $key => $value) {
  667 + if (strpos($value,'YAMLMap') !== false) {
  668 + foreach ($maps as $mapk => $map) {
  669 + $explode[$key] = str_replace(('YAMLMap'.$mapk.'s'), $map, $value);
  670 + $value = $explode[$key];
  671 + }
  672 + }
  673 + }
  674 + }
  675 + // Re-add the strings
  676 + if (!empty($saved_strings)) {
  677 + foreach ($explode as $key => $value) {
  678 + while (strpos($value,'YAMLString') !== false) {
  679 + $explode[$key] = preg_replace('/YAMLString/',$saved_strings[$stringi],$value, 1);
  680 + unset($saved_strings[$stringi]);
  681 + ++$stringi;
  682 + $value = $explode[$key];
  683 + }
  684 + }
  685 + }
  686 + // Re-add the empties
  687 + if (!empty($saved_empties)) {
  688 + foreach ($explode as $key => $value) {
  689 + while (strpos($value,'YAMLEmpty') !== false) {
  690 + $explode[$key] = preg_replace('/YAMLEmpty/', '', $value, 1);
  691 + $value = $explode[$key];
  692 + }
  693 + }
  694 + }
  695 + $finished = true;
  696 + foreach ($explode as $key => $value) {
  697 + if (strpos($value,'YAMLSeq') !== false) {
  698 + $finished = false; break;
  699 + }
  700 + if (strpos($value,'YAMLMap') !== false) {
  701 + $finished = false; break;
  702 + }
  703 + if (strpos($value,'YAMLString') !== false) {
  704 + $finished = false; break;
  705 + }
  706 + if (strpos($value,'YAMLEmpty') !== false) {
  707 + $finished = false; break;
  708 + }
  709 + }
  710 + if ($finished) break;
  711 + $i++;
  712 + if ($i > 10)
  713 + break; // Prevent infinite loops.
  714 + }
  715 + return $explode;
  716 + }
  717 + private function literalBlockContinues ($line, $lineIndent) {
  718 + if (!trim($line)) return true;
  719 + if (strlen($line) - strlen(ltrim($line)) > $lineIndent) return true;
  720 + return false;
  721 + }
  722 + private function referenceContentsByAlias ($alias) {
  723 + do {
  724 + if (!isset($this->SavedGroups[$alias])) { echo "Bad group name: $alias."; break; }
  725 + $groupPath = $this->SavedGroups[$alias];
  726 + $value = $this->result;
  727 + foreach ($groupPath as $k) {
  728 + $value = $value[$k];
  729 + }
  730 + } while (false);
  731 + return $value;
  732 + }
  733 + private function addArrayInline ($array, $indent) {
  734 + $CommonGroupPath = $this->path;
  735 + if (empty ($array)) return false;
  736 + foreach ($array as $k => $_) {
  737 + $this->addArray(array($k => $_), $indent);
  738 + $this->path = $CommonGroupPath;
  739 + }
  740 + return true;
  741 + }
  742 + private function addArray ($incoming_data, $incoming_indent) {
  743 + // print_r ($incoming_data);
  744 + if (count ($incoming_data) > 1)
  745 + return $this->addArrayInline ($incoming_data, $incoming_indent);
  746 + $key = key ($incoming_data);
  747 + $value = isset($incoming_data[$key]) ? $incoming_data[$key] : null;
  748 + if ($key === '__!YAMLZero') $key = '0';
  749 + if ($incoming_indent == 0 && !$this->_containsGroupAlias && !$this->_containsGroupAnchor) { // Shortcut for root-level values.
  750 + if ($key || $key === '' || $key === '0') {
  751 + $this->result[$key] = $value;
  752 + } else {
  753 + $this->result[] = $value; end ($this->result); $key = key ($this->result);
  754 + }
  755 + $this->path[$incoming_indent] = $key;
  756 + return;
  757 + }
  758 + $history = array();
  759 + // Unfolding inner array tree.
  760 + $history[] = $_arr = $this->result;
  761 + foreach ($this->path as $k) {
  762 + $history[] = $_arr = $_arr[$k];
  763 + }
  764 + if ($this->_containsGroupAlias) {
  765 + $value = $this->referenceContentsByAlias($this->_containsGroupAlias);
  766 + $this->_containsGroupAlias = false;
  767 + }
  768 + // Adding string or numeric key to the innermost level or $this->arr.
  769 + if (is_string($key) && $key == '<<') {
  770 + if (!is_array ($_arr)) { $_arr = array (); }
  771 + $_arr = array_merge ($_arr, $value);
  772 + } else if ($key || $key === '' || $key === '0') {
  773 + if (!is_array ($_arr))
  774 + $_arr = array ($key=>$value);
  775 + else
  776 + $_arr[$key] = $value;
  777 + } else {
  778 + if (!is_array ($_arr)) { $_arr = array ($value); $key = 0; }
  779 + else { $_arr[] = $value; end ($_arr); $key = key ($_arr); }
  780 + }
  781 + $reverse_path = array_reverse($this->path);
  782 + $reverse_history = array_reverse ($history);
  783 + $reverse_history[0] = $_arr;
  784 + $cnt = count($reverse_history) - 1;
  785 + for ($i = 0; $i < $cnt; $i++) {
  786 + $reverse_history[$i+1][$reverse_path[$i]] = $reverse_history[$i];
  787 + }
  788 + $this->result = $reverse_history[$cnt];
  789 + $this->path[$incoming_indent] = $key;
  790 + if ($this->_containsGroupAnchor) {
  791 + $this->SavedGroups[$this->_containsGroupAnchor] = $this->path;
  792 + if (is_array ($value)) {
  793 + $k = key ($value);
  794 + if (!is_int ($k)) {
  795 + $this->SavedGroups[$this->_containsGroupAnchor][$incoming_indent + 2] = $k;
  796 + }
  797 + }
  798 + $this->_containsGroupAnchor = false;
  799 + }
  800 + }
  801 + private static function startsLiteralBlock ($line) {
  802 + $lastChar = substr (trim($line), -1);
  803 + if ($lastChar != '>' && $lastChar != '|') return false;
  804 + if ($lastChar == '|') return $lastChar;
  805 + // HTML tags should not be counted as literal blocks.
  806 + if (preg_match ('#<.*?>$#', $line)) return false;
  807 + return $lastChar;
  808 + }
  809 + private static function greedilyNeedNextLine($line) {
  810 + $line = trim ($line);
  811 + if (!strlen($line)) return false;
  812 + if (substr ($line, -1, 1) == ']') return false;
  813 + if ($line[0] == '[') return true;
  814 + if (preg_match ('#^[^:]+?:\s*\[#', $line)) return true;
  815 + return false;
  816 + }
  817 + private function addLiteralLine ($literalBlock, $line, $literalBlockStyle, $indent = -1) {
  818 + $line = self::stripIndent($line, $indent);
  819 + if ($literalBlockStyle !== '|') {
  820 + $line = self::stripIndent($line);
  821 + }
  822 + $line = rtrim ($line, "\r\n\t ") . "\n";
  823 + if ($literalBlockStyle == '|') {
  824 + return $literalBlock . $line;
  825 + }
  826 + if (strlen($line) == 0)
  827 + return rtrim($literalBlock, ' ') . "\n";
  828 + if ($line == "\n" && $literalBlockStyle == '>') {
  829 + return rtrim ($literalBlock, " \t") . "\n";
  830 + }
  831 + if ($line != "\n")
  832 + $line = trim ($line, "\r\n ") . " ";
  833 + return $literalBlock . $line;
  834 + }
  835 + function revertLiteralPlaceHolder ($lineArray, $literalBlock) {
  836 + foreach ($lineArray as $k => $_) {
  837 + if (is_array($_))
  838 + $lineArray[$k] = $this->revertLiteralPlaceHolder ($_, $literalBlock);
  839 + else if (substr($_, -1 * strlen ($this->LiteralPlaceHolder)) == $this->LiteralPlaceHolder)
  840 + $lineArray[$k] = rtrim ($literalBlock, " \r\n");
  841 + }
  842 + return $lineArray;
  843 + }
  844 + private static function stripIndent ($line, $indent = -1) {
  845 + if ($indent == -1) $indent = strlen($line) - strlen(ltrim($line));
  846 + return substr ($line, $indent);
  847 + }
  848 + private function getParentPathByIndent ($indent) {
  849 + if ($indent == 0) return array();
  850 + $linePath = $this->path;
  851 + do {
  852 + end($linePath); $lastIndentInParentPath = key($linePath);
  853 + if ($indent <= $lastIndentInParentPath) array_pop ($linePath);
  854 + } while ($indent <= $lastIndentInParentPath);
  855 + return $linePath;
  856 + }
  857 + private function clearBiggerPathValues ($indent) {
  858 + if ($indent == 0) $this->path = array();
  859 + if (empty ($this->path)) return true;
  860 + foreach ($this->path as $k => $_) {
  861 + if ($k > $indent) unset ($this->path[$k]);
  862 + }
  863 + return true;
  864 + }
  865 + private static function isComment ($line) {
  866 + if (!$line) return false;
  867 + if ($line[0] == '#') return true;
  868 + if (trim($line, " \r\n\t") == '---') return true;
  869 + return false;
  870 + }
  871 + private static function isEmpty ($line) {
  872 + return (trim ($line) === '');
  873 + }
  874 + private function isArrayElement ($line) {
  875 + if (!$line || !is_scalar($line)) return false;
  876 + if (substr($line, 0, 2) != '- ') return false;
  877 + if (strlen ($line) > 3)
  878 + if (substr($line,0,3) == '---') return false;
  879 + return true;
  880 + }
  881 + private function isHashElement ($line) {
  882 + return strpos($line, ':');
  883 + }
  884 + private function isLiteral ($line) {
  885 + if ($this->isArrayElement($line)) return false;
  886 + if ($this->isHashElement($line)) return false;
  887 + return true;
  888 + }
  889 + private static function unquote ($value) {
  890 + if (!$value) return $value;
  891 + if (!is_string($value)) return $value;
  892 + if ($value[0] == '\'') return trim ($value, '\'');
  893 + if ($value[0] == '"') return trim ($value, '"');
  894 + return $value;
  895 + }
  896 + private function startsMappedSequence ($line) {
  897 + return (substr($line, 0, 2) == '- ' && substr ($line, -1, 1) == ':');
  898 + }
  899 + private function returnMappedSequence ($line) {
  900 + $array = array();
  901 + $key = self::unquote(trim(substr($line,1,-1)));
  902 + $array[$key] = array();
  903 + $this->delayedPath = array(strpos ($line, $key) + $this->indent => $key);
  904 + return array($array);
  905 + }
  906 + private function checkKeysInValue($value) {
  907 + if (strchr('[{"\'', $value[0]) === false) {
  908 + if (strchr($value, ': ') !== false) {
  909 + throw new Exception('Too many keys: '.$value);
  910 + }
  911 + }
  912 + }
  913 + private function returnMappedValue ($line) {
  914 + $this->checkKeysInValue($line);
  915 + $array = array();
  916 + $key = self::unquote (trim(substr($line,0,-1)));
  917 + $array[$key] = '';
  918 + return $array;
  919 + }
  920 + private function startsMappedValue ($line) {
  921 + return (substr ($line, -1, 1) == ':');
  922 + }
  923 + private function isPlainArray ($line) {
  924 + return ($line[0] == '[' && substr ($line, -1, 1) == ']');
  925 + }
  926 + private function returnPlainArray ($line) {
  927 + return $this->_toType($line);
  928 + }
  929 + private function returnKeyValuePair ($line) {
  930 + $array = array();
  931 + $key = '';
  932 + if (strpos ($line, ': ')) {
  933 + // It's a key/value pair most likely
  934 + // If the key is in double quotes pull it out
  935 + if (($line[0] == '"' || $line[0] == "'") && preg_match('/^(["\'](.*)["\'](\s)*:)/',$line,$matches)) {
  936 + $value = trim(str_replace($matches[1],'',$line));
  937 + $key = $matches[2];
  938 + } else {
  939 + // Do some guesswork as to the key and the value
  940 + $explode = explode(': ', $line);
  941 + $key = trim(array_shift($explode));
  942 + $value = trim(implode(': ', $explode));
  943 + $this->checkKeysInValue($value);
  944 + }
  945 + // Set the type of the value. Int, string, etc
  946 + $value = $this->_toType($value);
  947 + if ($key === '0') $key = '__!YAMLZero';
  948 + $array[$key] = $value;
  949 + } else {
  950 + $array = array ($line);
  951 + }
  952 + return $array;
  953 + }
  954 + private function returnArrayElement ($line) {
  955 + if (strlen($line) <= 1) return array(array()); // Weird %)
  956 + $array = array();
  957 + $value = trim(substr($line,1));
  958 + $value = $this->_toType($value);
  959 + if ($this->isArrayElement($value)) {
  960 + $value = $this->returnArrayElement($value);
  961 + }
  962 + $array[] = $value;
  963 + return $array;
  964 + }
  965 + private function nodeContainsGroup ($line) {
  966 + $symbolsForReference = 'A-z0-9_\-';
  967 + if (strpos($line, '&') === false && strpos($line, '*') === false) return false; // Please die fast ;-)
  968 + if ($line[0] == '&' && preg_match('/^(&['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1];
  969 + if ($line[0] == '*' && preg_match('/^(\*['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1];
  970 + if (preg_match('/(&['.$symbolsForReference.']+)$/', $line, $matches)) return $matches[1];
  971 + if (preg_match('/(\*['.$symbolsForReference.']+$)/', $line, $matches)) return $matches[1];
  972 + if (preg_match ('#^\s*<<\s*:\s*(\*[^\s]+).*$#', $line, $matches)) return $matches[1];
  973 + return false;
  974 + }
  975 + private function addGroup ($line, $group) {
  976 + if ($group[0] == '&') $this->_containsGroupAnchor = substr ($group, 1);
  977 + if ($group[0] == '*') $this->_containsGroupAlias = substr ($group, 1);
  978 + //print_r ($this->path);
  979 + }
  980 + private function stripGroup ($line, $group) {
  981 + $line = trim(str_replace($group, '', $line));
  982 + return $line;
  983 + }
  984 +}
  985 +}
  986 +// Enable use of Spyc from command line
  987 +// The syntax is the following: php Spyc.php spyc.yaml
  988 +do {
  989 + if (PHP_SAPI != 'cli') break;
  990 + if (empty ($_SERVER['argc']) || $_SERVER['argc'] < 2) break;
  991 + if (empty ($_SERVER['PHP_SELF']) || FALSE === strpos ($_SERVER['PHP_SELF'], 'Spyc.php') ) break;
  992 + $file = $argv[1];
  993 + echo json_encode (spyc_load_file ($file));
  994 +} while (0);