diff --git a/externals/JsShrink/jsShrink.php b/externals/JsShrink/jsShrink.php index 34a5a5b8e8..a6e2fb6f82 100644 --- a/externals/JsShrink/jsShrink.php +++ b/externals/JsShrink/jsShrink.php @@ -11,7 +11,8 @@ function jsShrink($input) { return preg_replace_callback('( (?: - (^|[-+\([{}=,:;!%^&*|?~]|/(?![/*])|return|throw) # context before regexp + (`(?:\\\\.|[^`\\\\])*`) # template literal + |(^|[-+\([{}=,:;!%^&*|?~]|/(?![/*])|return|throw) # context before regexp (?:\s|//[^\n]*+\n|/\*(?:[^*]|\*(?!/))*+\*/)* # optional space (/(?![/*])(?: \\\\[^\n] @@ -31,9 +32,11 @@ function jsShrink($input) { function jsShrinkCallback($match) { static $last = ''; - $match += array_fill(1, 5, null); // avoid E_NOTICE - list(, $context, $regexp, $result, $word, $operator) = $match; - if ($word != '') { + $match += array_fill(1, 7, null); // avoid E_NOTICE + list(, $template, $context, $regexp, $result, $word, $operator) = $match; + if ($template) { + $result = $template; + } elseif ($word != '') { $result = ($last == 'word' ? "\n" : ($last == 'return' ? " " : "")) . $result; $last = ($word == 'return' || $word == 'throw' || $word == 'break' || $word == 'async' ? 'return' : 'word'); } elseif ($operator) { diff --git a/externals/cldr/cldr_windows_timezones.xml b/externals/cldr/cldr_windows_timezones.xml index 2fae6c3c18..a81d0beb83 100644 --- a/externals/cldr/cldr_windows_timezones.xml +++ b/externals/cldr/cldr_windows_timezones.xml @@ -537,7 +537,8 @@ For terms of use, see http://www.unicode.org/copyright.html - + + @@ -569,13 +570,12 @@ For terms of use, see http://www.unicode.org/copyright.html - - + + - diff --git a/externals/phpmailer/class.phpmailer-lite.php b/externals/phpmailer/class.phpmailer-lite.php index 335625ebad..033f68ec0b 100644 --- a/externals/phpmailer/class.phpmailer-lite.php +++ b/externals/phpmailer/class.phpmailer-lite.php @@ -1311,11 +1311,6 @@ class PHPMailerLite { if (!is_readable($path)) { throw new phpmailerException($this->Lang('file_open') . $path, self::STOP_CONTINUE); } - if (function_exists('get_magic_quotes')) { - function get_magic_quotes() { - return false; - } - } if (PHP_VERSION < 6) { $magic_quotes = get_magic_quotes_runtime(); set_magic_quotes_runtime(0); @@ -1492,7 +1487,7 @@ class PHPMailerLite { $eol = "\r\n"; $escape = '='; $output = ''; - while( list(, $line) = each($lines) ) { + foreach ($lines as $line) { $linlen = strlen($line); $newline = ''; for($i = 0; $i < $linlen; $i++) { @@ -1523,7 +1518,7 @@ class PHPMailerLite { $newline .= $c; } // end of for $output .= $newline.$eol; - } // end of while + } // end of foreach return $output; } @@ -1985,7 +1980,6 @@ class PHPMailerLite { 'mov' => 'video/quicktime', 'avi' => 'video/x-msvideo', 'movie' => 'video/x-sgi-movie', - 'doc' => 'application/msword', 'word' => 'application/msword', 'xl' => 'application/excel', 'eml' => 'message/rfc822' @@ -2054,7 +2048,6 @@ class PHPMailerLite { * @param string $key_pass Password for private key */ public function DKIM_QP($txt) { - $tmp=""; $line=""; for ($i=0;$iLang('file_open') . $path, self::STOP_CONTINUE); } - if (function_exists('get_magic_quotes')) { - function get_magic_quotes() { - return false; - } - } if (PHP_VERSION < 6) { $magic_quotes = get_magic_quotes_runtime(); set_magic_quotes_runtime(0); @@ -1624,7 +1619,7 @@ class PHPMailer { $eol = "\r\n"; $escape = '='; $output = ''; - while( list(, $line) = each($lines) ) { + foreach ($lines as $line) { $linlen = strlen($line); $newline = ''; for($i = 0; $i < $linlen; $i++) { @@ -1655,7 +1650,7 @@ class PHPMailer { $newline .= $c; } // end of for $output .= $newline.$eol; - } // end of while + } // end of foreach return $output; } @@ -2123,7 +2118,6 @@ class PHPMailer { 'mov' => 'video/quicktime', 'avi' => 'video/x-msvideo', 'movie' => 'video/x-sgi-movie', - 'doc' => 'application/msword', 'word' => 'application/msword', 'xl' => 'application/excel', 'eml' => 'message/rfc822' @@ -2192,7 +2186,6 @@ class PHPMailer { * @param string $key_pass Password for private key */ public function DKIM_QP($txt) { - $tmp=""; $line=""; for ($i=0;$ihost = $host; // If no port value is passed, retrieve it @@ -239,16 +239,7 @@ class POP3 { } // Increase the stream time-out - - // Check for PHP 4.3.0 or later - if (version_compare(phpversion(), '5.0.0', 'ge')) { - stream_set_timeout($this->pop_conn, $tval, 0); - } else { - // Does not work on Windows - if (substr(PHP_OS, 0, 3) !== 'WIN') { - socket_set_timeout($this->pop_conn, $tval, 0); - } - } + stream_set_timeout($this->pop_conn, $tval, 0); // Get the POP3 server response $pop3_response = $this->getResponse(); @@ -404,4 +395,4 @@ class POP3 { // End of class } -?> \ No newline at end of file +?> diff --git a/externals/phpqrcode/phpqrcode.php b/externals/phpqrcode/phpqrcode.php index 54cde3ee9e..a25d313769 100644 --- a/externals/phpqrcode/phpqrcode.php +++ b/externals/phpqrcode/phpqrcode.php @@ -33,19 +33,19 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - - -/* - * Version: 1.1.4 - * Build: 2010100721 - */ - - - -//---- qrconst.php ----------------------------- - - - + + +/* + * Version: 1.9.9 + * Build: 20130526 + */ + + + +//---- qrconst.php ----------------------------- + + + /* @@ -57,7 +57,7 @@ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi * * PHP QR Code is distributed under LGPL 3 - * Copyright (C) 2010 Dominik Dzienia + * Copyright (C) 2010-2013 Dominik Dzienia * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -74,39 +74,60 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - // Encoding modes - - define('QR_MODE_NUL', -1); - define('QR_MODE_NUM', 0); - define('QR_MODE_AN', 1); - define('QR_MODE_8', 2); - define('QR_MODE_KANJI', 3); - define('QR_MODE_STRUCTURE', 4); - // Levels of error correction. + /** \defgroup QR_CONST Global Constants + Constant used globally for function arguments. + Make PHP calls a little bit more clear, in place of missing (in dynamicaly typed language) enum types. + * @{ + */ + + /** @name QR-Code Encoding Modes */ + /** @{ */ + + /** null encoding, used when no encoding was speciffied yet */ + define('QR_MODE_NUL', -1); + /** Numerical encoding, only numbers (0-9) */ + define('QR_MODE_NUM', 0); + /** AlphaNumerical encoding, numbers (0-9) uppercase text (A-Z) and few special characters (space, $, %, *, +, -, ., /, :) */ + define('QR_MODE_AN', 1); + /** 8-bit encoding, raw 8 bit encoding */ + define('QR_MODE_8', 2); + /** Kanji encoding */ + define('QR_MODE_KANJI', 3); + /** Structure, internal encoding for structure-related data */ + define('QR_MODE_STRUCTURE', 4); + /**@}*/ + + /** @name QR-Code Levels of Error Correction + Constants speciffy ECC level from lowest __L__ to the highest __H__. + Higher levels are recomended for Outdoor-presented codes, but generates bigger codes. + */ + /** @{*/ + /** ~7% of codewords can be restored */ + define('QR_ECLEVEL_L', 0); + /** ~15% of codewords can be restored */ + define('QR_ECLEVEL_M', 1); + /** ~25% of codewords can be restored */ + define('QR_ECLEVEL_Q', 2); + /** ~30% of codewords can be restored */ + define('QR_ECLEVEL_H', 3); + /** @}*/ + + /** @name QR-Code Supported output formats */ + /** @{*/ + define('QR_FORMAT_TEXT', 0); + define('QR_FORMAT_PNG', 1); + /** @}*/ + + /** @}*/ + + + + +//---- merged_config.php ----------------------------- + + - define('QR_ECLEVEL_L', 0); - define('QR_ECLEVEL_M', 1); - define('QR_ECLEVEL_Q', 2); - define('QR_ECLEVEL_H', 3); - - // Supported output formats - - define('QR_FORMAT_TEXT', 0); - define('QR_FORMAT_PNG', 1); - - class qrstr { - public static function set(&$srctab, $x, $y, $repl, $replLen = false) { - $srctab[$y] = substr_replace($srctab[$y], ($replLen !== false)?substr($repl,0,$replLen):$repl, $x, ($replLen !== false)?$replLen:strlen($repl)); - } - } - - - -//---- merged_config.php ----------------------------- - - - /* * PHP QR Code encoder @@ -123,14 +144,14 @@ define('QR_DEFAULT_MASK', 2); // when QR_FIND_BEST_MASK === false define('QR_PNG_MAXIMUM_SIZE', 1024); // maximum allowed png image width (in pixels), tune to make sure GD and PHP can handle such big images - - - - -//---- qrtools.php ----------------------------- - - - + + + + +//---- qrtools.php ----------------------------- + + + /* * PHP QR Code encoder @@ -138,7 +159,7 @@ * Toolset, handy and debug utilites. * * PHP QR Code is distributed under LGPL 3 - * Copyright (C) 2010 Dominik Dzienia + * Copyright (C) 2010-2013 Dominik Dzienia * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -155,8 +176,14 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + /** @addtogroup CoreGroup */ + /** @{ */ + + /** Helper class */ class QRtools { + public static $timeBenchmarkStarted = false; + //---------------------------------------------------------------------- public static function binarize($frame) { @@ -255,6 +282,13 @@ } } + //---------------------------------------------------------------------- + public static function startTimeBenchmark() + { + $GLOBALS['qr_time_bench'] = array(); + self::markTime('start'); + } + //---------------------------------------------------------------------- public static function markTime($markerId) { @@ -265,6 +299,11 @@ $GLOBALS['qr_time_bench'] = array(); $GLOBALS['qr_time_bench'][$markerId] = $time; + + if ((!self::$timeBenchmarkStarted)&&($markerId != 'start')) { + self::$timeBenchmarkStarted = true; + $GLOBALS['qr_time_bench']['start'] = $time; + } } //---------------------------------------------------------------------- @@ -299,17 +338,17 @@ } + /** @}*/ + //########################################################################## - QRtools::markTime('start'); - - - - -//---- qrspec.php ----------------------------- - - - + + + +//---- qrspec.php ----------------------------- + + + /* * PHP QR Code encoder @@ -320,7 +359,7 @@ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi * * PHP QR Code is distributed under LGPL 3 - * Copyright (C) 2010 Dominik Dzienia + * Copyright (C) 2010-2013 Dominik Dzienia * * The following data / specifications are taken from * "Two dimensional symbol -- QR-code -- Basic Specification" (JIS X0510:2004) @@ -343,16 +382,35 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + /** Maximal Version no allowed by QR-Code spec */ define('QRSPEC_VERSION_MAX', 40); + /** Maximal Code size in pixels allowed by QR-Code spec */ define('QRSPEC_WIDTH_MAX', 177); define('QRCAP_WIDTH', 0); define('QRCAP_WORDS', 1); define('QRCAP_REMINDER', 2); define('QRCAP_EC', 3); + + /** @addtogroup CoreGroup */ + /** @{ */ + /** QR-Code specification and Code Frame handling. + Contains code specifications, calculates base frame, code structure + and base properties + */ class QRspec { + /** Array specifying properties of QR-Code "versions". + Each so-called version has specified code area size and capacity. + There are 40 versions, this table specifies for each of them four parameters: + + - Integer __QRCAP_WIDTH__ - size of code in pixels + - Integer __QRCAP_WORDS__ - code capacity, in words + - Integer __QRCAP_REMINDER__ - remainder words + - Array of Integers __QRCAP_EC__ - RS correction code count for each of four ECC levels + \hideinitializer + */ public static $capacity = array( array( 0, 0, 0, array( 0, 0, 0, 0)), array( 21, 26, 0, array( 7, 10, 13, 17)), // 1 @@ -398,30 +456,53 @@ ); //---------------------------------------------------------------------- + /** Calculates data length for specified code configuration. + @param Integer $version Code version + @param Integer $level ECC level + @returns Code data capacity + */ public static function getDataLength($version, $level) { return self::$capacity[$version][QRCAP_WORDS] - self::$capacity[$version][QRCAP_EC][$level]; } //---------------------------------------------------------------------- + /** Calculates count of Error Correction Codes for specified code configuration. + @param Integer $version Code version + @param Integer $level ECC level + @returns ECC code count + */ public static function getECCLength($version, $level) { return self::$capacity[$version][QRCAP_EC][$level]; } //---------------------------------------------------------------------- + /** Gets pixel width of code. + @param Integer $version Code version + @returns Code width, in pixels + */ public static function getWidth($version) { return self::$capacity[$version][QRCAP_WIDTH]; } //---------------------------------------------------------------------- + /** Gets reminder chars length. + @param Integer $version Code version + @returns Reminder length + */ public static function getRemainder($version) { return self::$capacity[$version][QRCAP_REMINDER]; } //---------------------------------------------------------------------- + /** Finds minimal code version capable of hosting specified data length. + @param Integer $size amount of raw data + @param Integer $level ECC level + @returns code version capable of hosting specified amount of data at specified ECC level + */ public static function getMinimumVersion($size, $level) { @@ -435,7 +516,10 @@ } //###################################################################### - + + /** Length bits Table. + \hideinitializer + */ public static $lengthTableBits = array( array(10, 12, 14), array( 9, 11, 13), @@ -485,8 +569,10 @@ } // Error correction code ----------------------------------------------- - // Table of the error correction code (Reed-Solomon block) - // See Table 12-16 (pp.30-36), JIS X0510:2004. + /** Table of the error correction code (Reed-Solomon block). + @see Table 12-16 (pp.30-36), JIS X0510:2004. + \hideinitializer + */ public static $eccTable = array( array(array( 0, 0), array( 0, 0), array( 0, 0), array( 0, 0)), @@ -563,12 +649,14 @@ // Alignment pattern --------------------------------------------------- - // Positions of alignment patterns. - // This array includes only the second and the third position of the - // alignment patterns. Rest of them can be calculated from the distance - // between them. + /** Positions of alignment patterns. + This array includes only the second and the third position of the + lignment patterns. Rest of them can be calculated from the distance + between them. - // See Table 1 in Appendix E (pp.71) of JIS X0510:2004. + @see Table 1 in Appendix E (pp.71) of JIS X0510:2004. + \hideinitializer + */ public static $alignmentPattern = array( array( 0, 0), @@ -582,13 +670,12 @@ array(24, 50), array(28, 54), array(32, 58), array(26, 54), array(30, 58), //35-40 ); - - /** -------------------------------------------------------------------- - * Put an alignment marker. - * @param frame - * @param width - * @param ox,oy center coordinate of the pattern - */ + //---------------------------------------------------------------------- + /** Puts an alignment marker. + @param frame + @param width + @param ox,oy center coordinate of the pattern + */ public static function putAlignmentMarker(array &$frame, $ox, $oy) { $finder = array( @@ -603,7 +690,7 @@ $xStart = $ox-2; for($y=0; $y<5; $y++) { - QRstr::set($frame, $xStart, $yStart+$y, $finder[$y]); + self::set($frame, $xStart, $yStart+$y, $finder[$y]); } } @@ -646,12 +733,12 @@ } // Version information pattern ----------------------------------------- - - // Version information pattern (BCH coded). - // See Table 1 in Appendix D (pp.68) of JIS X0510:2004. + /** Version information pattern (BCH coded). + size: [QRSPEC_VERSION_MAX - 6] + @see Table 1 in Appendix D (pp.68) of JIS X0510:2004. + \hideinitializer + */ - // size: [QRSPEC_VERSION_MAX - 6] - public static $versionPattern = array( 0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d, 0x0f928, 0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9, @@ -669,8 +756,11 @@ return self::$versionPattern[$version -7]; } - // Format information -------------------------------------------------- - // See calcFormatInfo in tests/test_qrspec.c (orginal qrencode c lib) + //---------------------------------------------------------------------- + /** Format information. + @see calcFormatInfo in tests/test_qrspec.c (orginal qrencode c lib) + \hideinitializer + */ public static $formatInfo = array( array(0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976), @@ -691,16 +781,16 @@ } // Frame --------------------------------------------------------------- - // Cache of initial frames. - + + /** Cache of initial frames. */ public static $frames = array(); - /** -------------------------------------------------------------------- - * Put a finder pattern. - * @param frame - * @param width - * @param ox,oy upper-left coordinate of the pattern - */ + /** Put a finder pattern. + @param frame + @param width + @param ox,oy upper-left coordinate of the pattern + \hideinitializer + */ public static function putFinderPattern(&$frame, $ox, $oy) { $finder = array( @@ -714,7 +804,7 @@ ); for($y=0; $y<7; $y++) { - QRstr::set($frame, $ox, $oy+$y, $finder[$y]); + self::set($frame, $ox, $oy+$y, $finder[$y]); } } @@ -742,14 +832,14 @@ $setPattern = str_repeat("\xc0", 8); - QRstr::set($frame, 0, 7, $setPattern); - QRstr::set($frame, $width-8, 7, $setPattern); - QRstr::set($frame, 0, $width - 8, $setPattern); + self::set($frame, 0, 7, $setPattern); + self::set($frame, $width-8, 7, $setPattern); + self::set($frame, 0, $width - 8, $setPattern); // Format info $setPattern = str_repeat("\x84", 9); - QRstr::set($frame, 0, 8, $setPattern); - QRstr::set($frame, $width - 8, 8, $setPattern, 8); + self::set($frame, 0, 8, $setPattern); + self::set($frame, $width - 8, 8, $setPattern, 8); $yOffset = $width - 8; @@ -797,6 +887,10 @@ } //---------------------------------------------------------------------- + /** Dumps debug HTML of frame. + @param Array $frame code frame + @param Boolean $binary_mode in binary mode only contents is dumped, without styling + */ public static function debug($frame, $binary_mode = false) { if ($binary_mode) { @@ -806,11 +900,7 @@ $frameLine = join('██', explode('1', $frameLine)); } - ?> - - .m { background-color: white; } '; echo '



        '; echo join("
        ", $frame); echo '






'; @@ -818,45 +908,71 @@ } else { foreach ($frame as &$frameLine) { - $frameLine = join(' ', explode("\xc0", $frameLine)); - $frameLine = join('', explode("\xc1", $frameLine)); - $frameLine = join(' ', explode("\xa0", $frameLine)); - $frameLine = join('', explode("\xa1", $frameLine)); - $frameLine = join('', explode("\x84", $frameLine)); //format 0 - $frameLine = join('', explode("\x85", $frameLine)); //format 1 - $frameLine = join('', explode("\x81", $frameLine)); //special bit - $frameLine = join(' ', explode("\x90", $frameLine)); //clock 0 - $frameLine = join('', explode("\x91", $frameLine)); //clock 1 - $frameLine = join(' ', explode("\x88", $frameLine)); //version - $frameLine = join('', explode("\x89", $frameLine)); //version - $frameLine = join('♦', explode("\x01", $frameLine)); - $frameLine = join('⋅', explode("\0", $frameLine)); - } - ?> - - "; + $frameLine = strtr($frameLine, array( + "\xc0" => ' ', //marker 0 + "\xc1" => '', //marker 1 + "\xa0" => ' ', //submarker 0 + "\xa1" => '', //submarker 1 + "\x84" => 'F', //format 0 + "\x85" => 'f', //format 1 + "\x81" => 'S', //special bit + "\x90" => 'C', //clock 0 + "\x91" => 'c', //clock 1 + "\x88" => ' ', //version 0 + "\x89" => '', //version 1 + "\x03" => '1', // 1 + "\x02" => '0', // 0 + )); + } + + echo ''; + + echo ""; echo join("
", $frame); - echo "
"; + echo "
Legend:
"; + echo '1 - data 1
'; + echo '0 - data 0
'; + echo '  - marker bit 0
'; + echo ' - marker bit 1
'; + echo '  - secondary marker bit 0
'; + echo ' - secondary marker bit 1
'; + echo 'F - format bit 0
'; + echo 'f - format bit 1
'; + echo 'S - special bit
'; + echo 'C - clock bit 0
'; + echo 'c - clock bit 1
'; + echo '  - version bit 0
'; + echo ' - version bit 1
'; + echo "
"; } } //---------------------------------------------------------------------- + /** Serializes frame. + Create compressed, serialized version of frame. + @param Array $frame Code Frame + @return String binary compresed Code Frame + */ public static function serial($frame) { return gzcompress(join("\n", $frame), 9); } //---------------------------------------------------------------------- + /** Deserializes frame. + Loads frame from serialized compressed binary + @param String $code binary, GZipped, serialized frame + @return Array Code Frame array + */ public static function unserial($code) { return explode("\n", gzuncompress($code)); @@ -891,6 +1007,23 @@ } //---------------------------------------------------------------------- + /** Sets code frame with speciffied code. + @param Array $frame target frame (modified by reference) + @param Integer $x X-axis position of replacement + @param Integer $y Y-axis position of replacement + @param Byte $repl replacement string + @param Integer $replLen (optional) replacement string length, when __Integer__ > 1 subset of given $repl is used, when __false__ whole $repl is used + */ + public static function set(&$frame, $x, $y, $repl, $replLen = false) { + $frame[$y] = substr_replace($frame[$y], ($replLen !== false)?substr($repl,0,$replLen):$repl, $x, ($replLen !== false)?$replLen:strlen($repl)); + } + + //---------------------------------------------------------------------- + + /** @name Reed-Solomon related shorthand getters. + Syntax-sugar to access code speciffication by getter name, not by spec array field. + */ + /** @{*/ public static function rsBlockNum($spec) { return $spec[0] + $spec[3]; } public static function rsBlockNum1($spec) { return $spec[0]; } public static function rsDataCodes1($spec) { return $spec[1]; } @@ -900,15 +1033,19 @@ public static function rsEccCodes2($spec) { return $spec[2]; } public static function rsDataLength($spec) { return ($spec[0] * $spec[1]) + ($spec[3] * $spec[4]); } public static function rsEccLength($spec) { return ($spec[0] + $spec[3]) * $spec[2]; } - - } - - - -//---- qrimage.php ----------------------------- - - - + /** @}*/ + } + + /** @}*/ + + + + + +//---- qrimage.php ----------------------------- + + + /* * PHP QR Code encoder @@ -916,7 +1053,7 @@ * Image output of code using GD2 * * PHP QR Code is distributed under LGPL 3 - * Copyright (C) 2010 Dominik Dzienia + * Copyright (C) 2010-2013 Dominik Dzienia * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -935,9 +1072,27 @@ define('QR_IMAGE', true); + + /** @defgroup OutputGroup Standard API Output + Provide simple Raster & Vector output */ + + /** @addtogroup OutputGroup */ + /** @{ */ + + /** Image rendering helper. + Uses GD2 image to render QR Code into image file */ class QRimage { //---------------------------------------------------------------------- + /** + Creates PNG image. + @param Array $frame frame containing code + @param String $filename (optional) output file name, if __false__ outputs to browser with required headers + @param Integer $pixelPerPoint (optional) pixel size, multiplier for each 'virtual' pixel + @param Integer $outerFrame (optional) code margin (silent zone) in 'virtual' pixels + @param Boolean $saveandprint (optional) if __true__ code is outputed to browser and saved to file, otherwise only saved to file. It is effective only if $outfile is specified. + */ + public static function png($frame, $filename = false, $pixelPerPoint = 4, $outerFrame = 4,$saveandprint=FALSE) { $image = self::image($frame, $pixelPerPoint, $outerFrame); @@ -959,6 +1114,15 @@ } //---------------------------------------------------------------------- + /** + Creates JPEG image. + @param Array $frame frame containing code + @param String $filename (optional) output file name, if __false__ outputs to browser with required headers + @param Integer $pixelPerPoint (optional) pixel size, multiplier for each 'virtual' pixel + @param Integer $outerFrame (optional) code margin (silent zone) in 'virtual' pixels + @param Integer $q (optional) JPEG compression level (__0__ .. __100__) + */ + public static function jpg($frame, $filename = false, $pixelPerPoint = 8, $outerFrame = 4, $q = 85) { $image = self::image($frame, $pixelPerPoint, $outerFrame); @@ -974,7 +1138,14 @@ } //---------------------------------------------------------------------- - private static function image($frame, $pixelPerPoint = 4, $outerFrame = 4) + /** + Creates generic GD2 image object + @param Array $frame frame containing code + @param Integer $pixelPerPoint (optional) pixel size, multiplier for each 'virtual' pixel + @param Integer $outerFrame (optional) code margin (silent zone) in 'virtual' pixels + @return __Resource__ GD2 image resource (remember to ImageDestroy it!) + */ + public static function image($frame, $pixelPerPoint = 4, $outerFrame = 4) { $h = count($frame); $w = strlen($frame[0]); @@ -1003,14 +1174,16 @@ return $target_image; } - } - - - -//---- qrinput.php ----------------------------- - - - + } + + /** @} */ + + + +//---- qrinput.php ----------------------------- + + + /* * PHP QR Code encoder @@ -1021,7 +1194,7 @@ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi * * PHP QR Code is distributed under LGPL 3 - * Copyright (C) 2010 Dominik Dzienia + * Copyright (C) 2010-2013 Dominik Dzienia * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -1038,8 +1211,11 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - define('STRUCTURE_HEADER_BITS', 20); + define('STRUCTURE_HEADER_BITS', 20); define('MAX_STRUCTURED_SYMBOLS', 16); + + /** @addtogroup CoreGroup */ + /** @{ */ class QRinputItem { @@ -1159,7 +1335,7 @@ { try { - $bs = new QRbitrtream(); + $bs = new QRbitstream(); $bs->appendNum(4, 0x8); $bs->appendNum(QRspec::lengthIndicator(QR_MODE_KANJI, $version), (int)($this->size / 2)); @@ -1738,15 +1914,14 @@ } } - - - - - -//---- qrbitstream.php ----------------------------- - - - + /** @}*/ + + + +//---- qrbitstream.php ----------------------------- + + + /* * PHP QR Code encoder @@ -1757,7 +1932,7 @@ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi * * PHP QR Code is distributed under LGPL 3 - * Copyright (C) 2010 Dominik Dzienia + * Copyright (C) 2010-2013 Dominik Dzienia * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -1773,18 +1948,38 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - + + /** @addtogroup CoreGroup */ + /** @{ */ + + /** + PHP bit stream. + Class implementing array of bits (= 1 or 0 ints). Allows to initialize and append + bits from given Integer or array of Bytes. + */ class QRbitstream { + /** + Array containing bit data stream + */ public $data = array(); //---------------------------------------------------------------------- + /** + @return Integer size of byte stream + */ public function size() { return count($this->data); } //---------------------------------------------------------------------- + /** + Allocates bit stream, fills bit data stream with 0's. + This operation is __destructive__, will replace orginal stream contents! + @param Integer $setLength desired target stream size + @return Integer 0 on success, other on failure + */ public function allocate($setLength) { $this->data = array_fill(0, $setLength, 0); @@ -1792,6 +1987,12 @@ } //---------------------------------------------------------------------- + /** + Creates new bit stream from given Integer number. + @param Integer $bits bit count + @param Integer $num integer to convert + @return QRbitstream bit stream object containing first $bits bits from $num in order from LSB to MSB + */ public static function newFromNum($bits, $num) { $bstream = new QRbitstream(); @@ -1811,6 +2012,12 @@ } //---------------------------------------------------------------------- + /** + Creates new bit stream from given byte array. + @param Integer $size size of array + @param Array $data array ob bytes + @return QRbitstream bit stream object containing bit contents of given bytes array + */ public static function newFromBytes($size, $data) { $bstream = new QRbitstream(); @@ -1834,6 +2041,11 @@ } //---------------------------------------------------------------------- + /** + Appends given bit stream at end of this stream. + @param QRbitstream $arg bit stream to be appended + @return Integer status of append operation, 0 when success, -1 when $arg is null + */ public function append(QRbitstream $arg) { if (is_null($arg)) { @@ -1855,6 +2067,12 @@ } //---------------------------------------------------------------------- + /** + Appends bit stream cteated from given Integer number at end of current stream. + @param Integer $bits bit count + @param Integer $num integer to convert + @return Integer status of append operation, status of append operation, 0 when success, -1 otherwise + */ public function appendNum($bits, $num) { if ($bits == 0) @@ -1872,6 +2090,12 @@ } //---------------------------------------------------------------------- + /** + Appends bit stream created from from given byte array at end of current stream. + @param Integer $size size of array + @param Array $data array ob bytes + @return Integer status of append operation, status of append operation, 0 when success, -1 otherwise + */ public function appendBytes($size, $data) { if ($size == 0) @@ -1889,6 +2113,10 @@ } //---------------------------------------------------------------------- + /** + Converts current bit stream into byte array. + @returns Array array of bytes + */ public function toByte() { @@ -1927,14 +2155,15 @@ } } - - - - -//---- qrsplit.php ----------------------------- - - - + + /** @}*/ + + + +//---- qrsplit.php ----------------------------- + + + /* * PHP QR Code encoder @@ -1945,7 +2174,7 @@ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi * * PHP QR Code is distributed under LGPL 3 - * Copyright (C) 2010 Dominik Dzienia + * Copyright (C) 2010-2013 Dominik Dzienia * * The following data / specifications are taken from * "Two dimensional symbol -- QR-code -- Basic Specification" (JIS X0510:2004) @@ -1967,6 +2196,11 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + /** @addtogroup CoreGroup */ + /** @{ */ + + /** Input stream splitter. */ class QRsplit { public $dataStr = ''; @@ -2245,14 +2479,16 @@ return $split->splitString(); } - } - - - -//---- qrrscode.php ----------------------------- - - - + } + + /** @} */ + + + +//---- qrrscode.php ----------------------------- + + + /* * PHP QR Code encoder @@ -2266,7 +2502,7 @@ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi * * PHP QR Code is distributed under LGPL 3 - * Copyright (C) 2010 Dominik Dzienia + * Copyright (C) 2010-2013 Dominik Dzienia * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -2283,22 +2519,40 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + /** @addtogroup CoreGroup */ + /** @{ */ + + /** Reed-Solomon encoder item */ class QRrsItem { - public $mm; // Bits per symbol - public $nn; // Symbols per block (= (1<= $this->nn) { $x -= $this->nn; @@ -2309,7 +2563,15 @@ } //---------------------------------------------------------------------- - public static function init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad) + /** Encoder initialisation + @param Integer $symsize symbol size, bit count (1..8) + @param Integer $gfpoly Galois Field Polynomial + @param Integer $fcr First consecutive root + @param Integer $prim Primitive element + @param Integer $nroots Number of generator roots = number of parity symbols + @param Integer $pad Padding bytes in shortened block + */ + public static function init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad) { // Common code for intializing a Reed-Solomon control block (char or int symbols) // Copyright 2004 Phil Karn, KA9Q @@ -2395,6 +2657,10 @@ } //---------------------------------------------------------------------- + /** Appends char into encoder + @param String input + @param Array parity table + */ public function encode_rs_char($data, &$parity) { $MM =& $this->mm; @@ -2438,12 +2704,21 @@ } //########################################################################## - + /** Reed-Solomon encoder */ class QRrs { + /** Encoder items array */ public static $items = array(); //---------------------------------------------------------------------- + /** Encoder initialisation + @param Integer $symsize symbol size, bit count (1..8) + @param Integer $gfpoly Galois Field Polynomial + @param Integer $fcr First consecutive root + @param Integer $prim Primitive element + @param Integer $nroots Number of generator roots = number of parity symbols + @param Integer $pad Padding bytes in shortened block + */ public static function init_rs($symsize, $gfpoly, $fcr, $prim, $nroots, $pad) { foreach(self::$items as $rs) { @@ -2462,14 +2737,16 @@ return $rs; } - } - - - -//---- qrmask.php ----------------------------- - - - + } + + /** @}*/ + + + +//---- qrmask.php ----------------------------- + + + /* * PHP QR Code encoder @@ -2480,7 +2757,7 @@ * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi * * PHP QR Code is distributed under LGPL 3 - * Copyright (C) 2010 Dominik Dzienia + * Copyright (C) 2010-2013 Dominik Dzienia * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -2497,11 +2774,16 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + define('N1', 3); define('N2', 3); define('N3', 40); define('N4', 10); + /** @addtogroup CoreGroup */ + /** @{ */ + class QRmask { public $runLength = array(); @@ -2643,7 +2925,7 @@ $b += (int)(ord($d[$y][$x]) & 1); } } - + return $b; } @@ -2798,25 +3080,619 @@ //---------------------------------------------------------------------- } - - - - -//---- qrencode.php ----------------------------- - - - + + /** @}*/ + + + +//---- qrarea.php ----------------------------- + + + /* * PHP QR Code encoder * - * Main encoder classes. + * Area finding for SVG and CANVAS output * * Based on libqrencode C library distributed under LGPL 2.1 * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi * * PHP QR Code is distributed under LGPL 3 - * Copyright (C) 2010 Dominik Dzienia + * Copyright (C) 2010-2013 Dominik Dzienia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + // N + // W E + // S + + define('QR_AREA_N', 0); + define('QR_AREA_E', 1); + define('QR_AREA_S', 2); + define('QR_AREA_W', 3); + + define('QR_AREA_X', 0); + define('QR_AREA_Y', 1); + + define('QR_AREA_TRACKER', 0); + define('QR_AREA_PATH', 1); + define('QR_AREA_POINT', 2); + define('QR_AREA_RECT', 3); + define('QR_AREA_LSHAPE', 4); + + /** @addtogroup OutputGroup */ + /** @{ */ + + class QRareaGroup { + public $total = 0; + public $vertical = false; + public $horizontal = false; + public $points = array(); + public $id = 0; + public $paths = array(); + + //---------------------------------------------------------------------- + public function __construct($selfId, $sx, $sy) + { + $this->total = 1; + $this->points = array(array($sx,$sy,false)); + $this->id = $selfId; + } + + } + + //########################################################################## + + class QRarea { + + public $width = 0; + private $tab = array(); + private $tab_edges = array(); + private $groups = array(); + private $curr_group = 0; + public $paths = array(); + + + //---------------------------------------------------------------------- + public function __construct($source_tab) + { + $py = 0; + $this->width = count($source_tab); + $this->tab = array(); + $this->tab_edges = array(); + $this->paths = array(); + + foreach ($source_tab as $line) { + $arr = array(); + $arr_edge = array(); + $px=0; + + foreach (str_split($line) as $item) { + + if ($py<7 && $px<7) + $item = 0; + + if ($py<7 && $px>=($this->width-7)) + $item = 0; + + if ($py>=($this->width-7) && $px<7) + $item = 0; + + $arr[] = (int)$item; + $arr_edge[] = array(false, false, false, false); + + $px++; + } + + $this->tab[] = $arr; + $this->tab_edges[] = $arr_edge; + $py++; + } + + $this->paths[] = array(QR_AREA_TRACKER, 0,0); + $this->paths[] = array(QR_AREA_TRACKER, 0,($this->width-7)); + $this->paths[] = array(QR_AREA_TRACKER, ($this->width-7),0); + + $this->groups = array(); + $this->curr_group = 1; + } + + //---------------------------------------------------------------------- + public function getGroups() + { + return $this->groups; + } + + //---------------------------------------------------------------------- + public function getPaths() + { + return $this->paths; + } + + //---------------------------------------------------------------------- + public function getWidth() + { + return $this->width; + } + + //---------------------------------------------------------------------- + public function dumpTab() + { + echo ""; + echo ""; + + $colorTab = array(); + + foreach($this->tab as $line) { + foreach($line as $item) { + if (!isset($colorTab[$item])) { + $colorTab[$item] = 'hsl('.mt_rand(0, 360).', '.floor((mt_rand(0, 25))+75).'%, 50%)'; + } + } + } + + foreach($this->tab as $line) { + echo ""; + foreach($line as $item) { + if ($item == 0) { + echo ""; + } else { + echo ""; + } + } + echo ""; + } + echo "
 ".$item."
"; + } + + //---------------------------------------------------------------------- + public function dumpEdges() + { + $style_off = '1px dotted silver;'; + $style_on = '3px solid red;'; + + $colorAlloc = array(); + + echo ""; + $py = 0; + foreach($this->tab_edges as $line) { + $px = 0; + echo ""; + foreach($line as $item) { + + $styles = 'border-top:'; + if ($item[QR_AREA_N]) + $styles .= $style_on; + else $styles .= $style_off; + + $styles .= 'border-bottom:'; + if ($item[QR_AREA_S]) + $styles .= $style_on; + else $styles .= $style_off; + + $styles .= 'border-right:'; + if ($item[QR_AREA_E]) + $styles .= $style_on; + else $styles .= $style_off; + + $styles .= 'border-left:'; + if ($item[QR_AREA_W]) + $styles .= $style_on; + else $styles .= $style_off; + + $color = ''; + $grp = $this->tab[$py][$px]; + + if ($grp>0) { + if (!isset($colorAlloc[$grp])) { + $colorAlloc[$grp] = 'hsl('.mt_rand(0, 360).', '.floor((mt_rand(0, 25))+75).'%, 50%)'; + } + + $color = 'background:'.$colorAlloc[$grp]; + } + + if ($grp == 0) + $grp = ' '; + + echo ""; + $px++; + } + echo ""; + $py++; + } + echo "
".$grp."
"; + } + + //---------------------------------------------------------------------- + private static function rle(&$stringData) + { + $outArray = array(); + $symbolArray = str_split($stringData); + $last = ''; + $run = 1; + + while (count($symbolArray) > 0) { + $symbol = array_shift($symbolArray); + + if ($symbol != $last) { + if ($run > 1) + $outArray[] = $run; + + if ($last != '') + $outArray[] = $last; + + $run = 1; + $last = $symbol; + } else { + $run++; + } + } + + if ($run > 1) + $outArray[] = $run; + + $outArray[] = $last; + + $stringData = $outArray; + } + + + //---------------------------------------------------------------------- + private function getAt($posx, $posy) + { + if (($posx<0)||($posy<0)||($posx>=$this->width)||($posy>=$this->width)) + return 0; + + return $this->tab[$posy][$posx]; + } + + //---------------------------------------------------------------------- + private function getOnElem($elem, $deltax = 0, $deltay = 0) + { + $posx = $elem[0]+$deltax; + $posy = $elem[1]+$deltay; + + if (($posx<0)||($posy<0)||($posx>=$this->width)||($posy>=$this->width)) + return 0; + + return $this->tab[$posy][$posx]; + } + + //---------------------------------------------------------------------- + private function addGroupElement($groupId, $h, $v, $sx, $sy) + { + $this->groups[$groupId]->total++; + if ($h) + $this->groups[$groupId]->horizontal = true; + if ($v) + $this->groups[$groupId]->vertical = true; + $this->groups[$groupId]->points[] = array($sx, $sy, false); + } + + //---------------------------------------------------------------------- + public function detectGroups() + { + for ($sy = 0; $sy < $this->width; $sy++) { + for ($sx = 0; $sx < $this->width; $sx++) { + + if ($this->tab[$sy][$sx] == 1) { // non-allocated + + $gid_left = 0; + $gid_top = 0; + + $grouped = false; + + if ($sx>0) { + + $gid_left = $this->tab[$sy][$sx-1]; // previous on left + + if ($gid_left > 1) { // if already in group + $this->tab[$sy][$sx] = $gid_left; + $grouped = true; + $this->addGroupElement($gid_left, true, false, $sx, $sy); + } + } + + if ($sy > 0) { + + $gid_top = $this->tab[$sy-1][$sx]; // previous on top + + if ($gid_top > 1) { //if in group + if (!$grouped) { // and not grouped + + $this->tab[$sy][$sx] = $gid_top; + $grouped = true; + + $this->addGroupElement($gid_top, false, true, $sx, $sy); + + } else if($gid_top != $gid_left) { // was in left group + + $grouped = true; + + $this->groups[$gid_top]->vertical = true; + $this->groups[$gid_top]->horizontal = true; + + $this->groups[$gid_top]->total = $this->groups[$gid_top]->total + $this->groups[$gid_left]->total; + + foreach($this->groups[$gid_left]->points as $elem) + $this->tab[$elem[1]][$elem[0]] = $gid_top; + + $this->groups[$gid_top]->points = array_values(array_merge($this->groups[$gid_top]->points, $this->groups[$gid_left]->points)); + unset($this->groups[$gid_left]); + + //refarb group + } + } + } + + if (!$grouped) { + $this->curr_group++; + $this->tab[$sy][$sx] = $this->curr_group; + $this->groups[$this->curr_group] = new QRareaGroup($this->curr_group, $sx, $sy); + } + + } + } + } + } + + //---------------------------------------------------------------------- + private function detectSquare($group) + { + $max_x = 0; + $max_y = 0; + $min_x = $this->width; + $min_y = $this->width; + + foreach($group->points as $elem) { + $min_x = min($min_x, $elem[QR_AREA_X]); + $max_x = max($max_x, $elem[QR_AREA_X]); + $min_y = min($min_y, $elem[QR_AREA_Y]); + $max_y = max($max_y, $elem[QR_AREA_Y]); + } + + return array($min_x, $min_y, $max_x+1, $max_y+1); + } + + //---------------------------------------------------------------------- + public function detectAreas() + { + $squares = array(); + $points = array(); + $lshapes = array(); + + foreach ($this->groups as $groupId=>&$group) { + if ($group->total > 3) { + + if ((!$group->vertical)||(!$group->horizontal)) { + + $squareCoord = $this->detectSquare($group); + array_unshift($squareCoord, QR_AREA_RECT); + + $this->paths[] = $squareCoord; + + } else { + + $this->detectPaths($group); + unset($group->points); + + foreach($group->paths as &$path) + self::rle($path[2]); + + $this->paths[] = array(QR_AREA_PATH, $group->paths); + } + } else if (($group->total == 3)&&($group->vertical)&&($group->horizontal)) { + $squareCoord = $this->detectSquare($group); + $variant = 0; + + if ($this->getOnElem($squareCoord, 0, 0) != $group->id) + $variant = 0; + + if ($this->getOnElem($squareCoord, 1, 0) != $group->id) + $variant = 1; + + if ($this->getOnElem($squareCoord, 0, 1) != $group->id) + $variant = 2; + + if ($this->getOnElem($squareCoord, 1, 1) != $group->id) + $variant = 3; + + $lshapes[] = $squareCoord[QR_AREA_X]; + $lshapes[] = $squareCoord[QR_AREA_Y]; + $lshapes[] = $variant; + + } else if ($group->total >= 2) { + $squareCoord = $this->detectSquare($group); + $squares = array_merge($squares, $squareCoord); + } else if ($group->total == 1) { + $points[] = $group->points[0][0]; + $points[] = $group->points[0][1]; + } + } + + if (count($points) > 0) { + array_unshift($points, QR_AREA_POINT); + $this->paths[] = $points; + } + + if (count($squares) > 0) { + array_unshift($squares, QR_AREA_RECT); + $this->paths[] = $squares; + } + + if (count($lshapes) > 0) { + array_unshift($lshapes, QR_AREA_LSHAPE); + $this->paths[] = $lshapes; + } + } + + //---------------------------------------------------------------------- + private function reserveEdgeOnElem($elem, $edgeNo) + { + $this->tab_edges[$elem[QR_AREA_Y]][$elem[QR_AREA_X]][$edgeNo] = true; + } + + //---------------------------------------------------------------------- + private function reserveEdge($px, $py, $edgeNo) + { + $this->tab_edges[$py][$px][$edgeNo] = true; + } + + //---------------------------------------------------------------------- + private function markAdjacentEdges($group) + { + foreach($group->points as $elem) { + if ($this->getOnElem($elem, -1, 0) == $group->id) + $this->reserveEdgeOnElem($elem, QR_AREA_W); + + if ($this->getOnElem($elem, +1, 0) == $group->id) + $this->reserveEdgeOnElem($elem, QR_AREA_E); + + if ($this->getOnElem($elem, 0, -1) == $group->id) + $this->reserveEdgeOnElem($elem, QR_AREA_N); + + if ($this->getOnElem($elem, 0, +1) == $group->id) + $this->reserveEdgeOnElem($elem, QR_AREA_S); + } + } + + //---------------------------------------------------------------------- + private function detectPaths(&$group) + { + $this->markAdjacentEdges($group); + + $elem = $group->points[0]; + $waylist = $this->findPath($group, $elem[QR_AREA_X], $elem[QR_AREA_Y]); + $group->paths[] = array($elem[QR_AREA_X], $elem[QR_AREA_Y], $waylist); + + $tab = array(); + foreach($group->points as $elem) { + + $edgeTab = $this->tab_edges[$elem[QR_AREA_Y]][$elem[QR_AREA_X]]; + + if (!( $edgeTab[QR_AREA_N] + && $edgeTab[QR_AREA_E] + && $edgeTab[QR_AREA_S] + && $edgeTab[QR_AREA_W])) { + + if (!$edgeTab[QR_AREA_S]) { + + $waylistw = $this->findPath($group, $elem[QR_AREA_X], $elem[QR_AREA_Y]+1); + $group->paths[] = array($elem[QR_AREA_X], $elem[QR_AREA_Y]+1, $waylistw); + } + } + } + + } + + //---------------------------------------------------------------------- + private function findPath($group, $sx, $sy) + { + $px = $sx; + $py = $sy; + + $waylist = ''; + $dir = ''; + $lastdir = ''; + + $moves = array( + // magic :) + 0=>'', 1=>'L', 2=>'T', 3=>'L', 4=>'B', 5=>'B', 6=>'B,T', 7=>'B' + ,8=>'R', 9=>'R,L', 10=>'T', 11=>'L',12=>'R',13=>'R',14=>'T',15=>'' + ); + + do + { + $Q = ($this->getAt($px-1, $py-1) == $group->id)?1:0; + $Q += ($this->getAt($px, $py-1) == $group->id)?2:0; + $Q += ($this->getAt($px-1, $py) == $group->id)?4:0; + $Q += ($this->getAt($px, $py) == $group->id)?8:0; + + if ($moves[$Q] == '') + throw new Exception('It should NEVER happened!'); + + $move_expl = explode(',', $moves[$Q]); + $have_way = false; + + $dir = ''; + + while ((count($move_expl) > 0)&&($have_way == false)) { + $way = array_shift($move_expl); + + if (($have_way==false)&&($way=='R')&&($this->tab_edges[$py][$px][QR_AREA_N]==false)) { + $have_way = true; + $dir = $way; + $this->reserveEdge($px, $py, QR_AREA_N); + $px++; + } + + if (($have_way==false)&&($way=='B')&&($this->tab_edges[$py][$px-1][QR_AREA_E]==false)) { + $have_way = true; + $dir = $way; + $this->reserveEdge($px-1, $py, QR_AREA_E); + $py++; + } + + if (($have_way==false)&&($way=='L')&&($this->tab_edges[$py-1][$px-1][QR_AREA_S]==false)) { + $have_way = true; + $dir = $way; + $this->reserveEdge($px-1, $py-1, QR_AREA_S); + $px--; + } + + if (($have_way==false)&&($way=='T')&&($this->tab_edges[$py-1][$px][QR_AREA_W]==false)) { + $have_way = true; + $dir = $way; + $this->reserveEdge($px, $py-1, QR_AREA_W); + $py--; + } + } + + $waylist .= $dir; + + } while (!(($px==$sx)&&($py==$sy))); + + return $waylist; + } + } + + /** @} */ + + + +//---- qrcanvas.php ----------------------------- + + + + +/* + * PHP QR Code encoder + * + * CANVAS output + * + * Based on libqrencode C library distributed under LGPL 2.1 + * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi + * + * PHP QR Code is distributed under LGPL 3 + * Copyright (C) 2010-2013 Dominik Dzienia * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -2833,12 +3709,575 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + /** @addtogroup OutputGroup */ + /** @{ */ + + class QRcanvasOutput extends QRarea { + + public function __construct($source_tab) + { + parent::__construct($source_tab); + } + + //---------------------------------------------------------------------- + public static function encodeNum($num) + { + $addTab = array(0=>'', 1=>'z', 2=>'Z', 3=>'+'); + $map = '0123456789abcdefghijklmnopqrstuvwxyABCDEFGHIJKLMNOPQRSTUVWXY'; + $mapPos = $num % 60; + $mapAdd = (int)($num / 60); + + return $addTab[$mapAdd].$map[$mapPos]; + } + + //---------------------------------------------------------------------- + public static function compact_path(&$pathTab) + { + if (count($pathTab) == 0) { + $pathTab = ''; + } else { + $pathTab = count($pathTab).','.join(',', $pathTab); + } + } + + //---------------------------------------------------------------------- + public static function compact_points(&$pointsTab) + { + if (count($pointsTab) == 0) { + $pointsTab = ''; + } else { + $compacted = ''; + foreach ($pointsTab as $point) + $compacted .= self::encodeNum($point); + $pointsTab = $compacted; + } + } + + //---------------------------------------------------------------------- + public static function compactCanvasCommands($ops) + { + $accumulated = array(); + + $accumulated['SR'] = array(); + $accumulated['WR'] = array(); + $accumulated['SP'] = array(); + $accumulated['WP'] = array(); + $accumulated['SB'] = array(); + $accumulated['WB'] = array(); + $accumulated['SO'] = array(); + + while (count($ops) > 0) { + $color = array_shift($ops); + $opcode = array_shift($ops); + + if (($opcode == 'R') || ($opcode == 'P')) { + + do { + $num = array_shift($ops); + if (is_int($num)) { + $accumulated[$color.$opcode][] = $num; + } else { + array_unshift($ops, $num); + } + + } while ((count($ops) > 0)&&(is_int($num))); + + + } else if ($opcode == 'B') { + + array_shift($ops); + + $px = array_shift($ops); + $py = array_shift($ops); + + array_shift($ops); + + $conftab = array(); + $num = array_shift($ops); + + while ((count($ops) > 0)&&(!($num === 'E'))) { + $conftab[] = $num; + $num = array_shift($ops); + } + + $cc = count($conftab); + $deltas = ''; + + $lastposx = $px; + $lastposy = $py; + + for($pos=0;$pos <$cc; $pos+=2) { + + $dx = $lastposx - $conftab[$pos]; + $dy = $lastposy - $conftab[$pos+1]; + + $lastposx = $conftab[$pos]; + $lastposy = $conftab[$pos+1]; + + if ($dx < 0) { + $deltas .= chr(ord('a')-1-$dx); + } else if ($dx > 0) { + $deltas .= chr(ord('A')-1+$dx); + } else { + $deltas .= '0'; + } + + if ($dy < 0) { + $deltas .= chr(ord('a')-1-$dy); + } else if ($dy > 0) { + $deltas .= chr(ord('A')-1+$dy); + } else { + $deltas .= '0'; + } + + } + + $deltas = strtr($deltas, array( + '00'=>'1', + 'aa'=>'2', + 'aA'=>'3', + 'Aa'=>'4', + 'AA'=>'5', + 'aB'=>'6', + 'Ab'=>'7', + 'bA'=>'8', + 'Ba'=>'9' + )); + + $accumulated[$color.$opcode][] = join(',', array($px, $py, $deltas)); + } else if ($opcode == 'O') { + $px = array_shift($ops); + $py = array_shift($ops); + + $accumulated[$color.$opcode][] = join(',', array($px, $py)); + } + } + + self::compact_points($accumulated['SR']); + self::compact_points($accumulated['WR']); + self::compact_points($accumulated['SP']); + self::compact_points($accumulated['WP']); + + self::compact_path($accumulated['SB']); + self::compact_path($accumulated['WB']); + + if (count($accumulated['SO']) > 0) + $accumulated['SO'] = join(',',$accumulated['SO']); + else $accumulated['SO'] = ''; + + $mapping = array( + 'SO'=>'O', + 'SB'=>'B', + 'WB'=>'b', + 'SR'=>'R', + 'WR'=>'r', + 'SP'=>'P', + 'WP'=>'p' + ); + + $whole = array(); + + foreach($mapping as $key=>$symb) { + if ($accumulated[$key]!='') + $whole[] = $symb.','.$accumulated[$key]; + } + + return join(',', $whole); + } + + + //---------------------------------------------------------------------- + public function getCanvasOps() + { + $ops = array(); + + foreach ($this->paths as $path) { + switch ($path[0]) { + case QR_AREA_PATH: + $pNum = 0; + + foreach($path[1] as $pathDetails) { + if ($pNum == 0) { + $ops[] = 'S'; + } else if ($pNum > 0) { + $ops[] = 'W'; + } + + $ops[] = 'B'; + + $px = array_shift($pathDetails); + $py = array_shift($pathDetails); + + $ops[] = 'M'; + $ops[] = $px; + $ops[] = $py; + + $rle_steps = array_shift($pathDetails); + + $lastOp = ''; + + while(count($rle_steps) > 0) { + + $delta = 1; + + $operator = array_shift($rle_steps); + if (($operator != 'R') && ($operator != 'L') && ($operator != 'T') && ($operator != 'B')) { + $delta = (int)$operator; + $operator = array_shift($rle_steps); + } + + if ($operator == 'R') $px += $delta; + if ($operator == 'L') $px -= $delta; + if ($operator == 'T') $py -= $delta; + if ($operator == 'B') $py += $delta; + + if ($lastOp != 'T') + $ops[] = 'T'; + + $ops[] = $px; + $ops[] = $py; + + $lastOp = 'T'; + } + + $ops[] = 'E'; + + $pNum++; + } + + break; + case QR_AREA_POINT: + + $symb = array_shift($path); + + $ops[] = 'S'; + + $lastOp = ''; + + while(count($path) > 0) { + $px = array_shift($path); + $py = array_shift($path); + + if ($lastOp != 'P') + $ops[] = 'P'; + + $ops[] = $px; + $ops[] = $py; + + $lastOp = 'P'; + } + + break; + + case QR_AREA_RECT: + + $symb = array_shift($path); + + $ops[] = 'S'; + + $lastOp = ''; + + while(count($path) > 0) { + $px = array_shift($path); + $py = array_shift($path); + $ex = array_shift($path); + $ey = array_shift($path); + + if ($lastOp != 'R') + $ops[] = 'R'; + + $ops[] = $px; + $ops[] = $py; + $ops[] = $ex-$px; + $ops[] = $ey-$py; + + $lastOp = 'R'; + } + + break; + + case QR_AREA_LSHAPE: + + $symb = array_shift($path); + + while(count($path) > 0) { + $px = array_shift($path); + $py = array_shift($path); + $mode = (int)array_shift($path); + + $pxd = ($mode % 2)?1:0; + $pyd = ($mode > 1)?1:0; + + $ops[] = 'S'; + $ops[] = 'R'; + $ops[] = $px; + $ops[] = $py; + $ops[] = 2; + $ops[] = 2; + + $ops[] = 'W'; + $ops[] = 'P'; + $ops[] = $px+$pxd; + $ops[] = $py+$pyd; + } + + break; + + case QR_AREA_TRACKER: + + $symb = array_shift($path); + + $px = array_shift($path); + $py = array_shift($path); + + $ops[] = 'S'; + $ops[] = 'O'; + $ops[] = $px; + $ops[] = $py; + + break; + } + } + + return self::compactCanvasCommands($ops); + } + } + + /** @} */ + + + + +//---- qrsvg.php ----------------------------- + + + + +/* + * PHP QR Code encoder + * + * SVG output support + * + * Based on libqrencode C library distributed under LGPL 2.1 + * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi + * + * PHP QR Code is distributed under LGPL 3 + * Copyright (C) 2010-2013 Dominik Dzienia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + /** @addtogroup OutputGroup */ + /** @{ */ + + class QRsvgOutput extends QRarea { + + public function __construct($source_tab) + { + parent::__construct($source_tab); + } + + //---------------------------------------------------------------------- + public function mapX($px) + { + return $px; + } + + //---------------------------------------------------------------------- + public function mapY($py) + { + return $py; + } + + //---------------------------------------------------------------------- + public function getRawSvg() + { + $lib = array(); + $svg = array(); + + $aggregate_paths = array(); + + foreach ($this->paths as $path) { + switch ($path[0]) { + case QR_AREA_PATH: + $pNum = 0; + + foreach($path[1] as $pathDetails) { + + $px = array_shift($pathDetails); + $py = array_shift($pathDetails); + $rle_steps = array_shift($pathDetails); + + $aggregate_add = 'M'.$px.','.$py.' '; + + while(count($rle_steps) > 0) { + + $delta = 1; + + $operator = array_shift($rle_steps); + if (($operator != 'R') && ($operator != 'L') && ($operator != 'T') && ($operator != 'B')) { + $delta = (int)$operator; + $operator = array_shift($rle_steps); + } + + if ($operator == 'R') $aggregate_add .= 'h'.$delta; + if ($operator == 'L') $aggregate_add .= 'h-'.$delta; + if ($operator == 'T') $aggregate_add .= 'v-'.$delta; + if ($operator == 'B') $aggregate_add .= 'v'.$delta; + } + + $aggregate_paths[] = $aggregate_add; + + $pNum++; + } + + break; + case QR_AREA_POINT: + + $symb = array_shift($path); + + while(count($path) > 0) { + $px = array_shift($path); + $py = array_shift($path); + + $aggregate_paths[] = 'M'.$px.','.$py.' v1h1v-1h-1'; + } + + break; + + case QR_AREA_RECT: + + $symb = array_shift($path); + + while(count($path) > 0) { + $px = array_shift($path); + $py = array_shift($path); + $ex = array_shift($path); + $ey = array_shift($path); + + $w = $ex-$px; + $h = $ey-$py; + + $aggregate_paths[] = 'M'.$px.','.$py.' h'.$w.'v'.$h.'h-'.$w.'v-'.$h; + } + + break; + + case QR_AREA_LSHAPE: + + $symb = array_shift($path); + + $l_shapes[0] = 'm1,0h1v2h-2v-1h1z'; + $l_shapes[1] = 'h1v1h1v1h-2z'; + $l_shapes[2] = 'h2v2h-1v-1h-1z'; + $l_shapes[3] = 'h2v1h-1v1h-1z'; + + while(count($path) > 0) { + $px = array_shift($path); + $py = array_shift($path); + $mode = (int)array_shift($path); + + $aggregate_paths[] = 'M'.$px.','.$py.' '.$l_shapes[$mode]; + } + + break; + + case QR_AREA_TRACKER: + + if (!isset($lib['tracker'])) { + $lib['tracker'] = ''; + } + + $symb = array_shift($path); + + $px = array_shift($path); + $py = array_shift($path); + + $svg[] = ''; + + break; + } + } + + $svg[] = ''; + + + + return join("\n", $lib)."\n".join("\n", $svg); + } + } + + /** @} */ + + + + +//---- qrencode.php ----------------------------- + + + + +/* + * PHP QR Code encoder + * + * Main encoder classes. + * + * Based on libqrencode C library distributed under LGPL 2.1 + * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi + * + * PHP QR Code is distributed under LGPL 3 + * Copyright (C) 2010-2013 Dominik Dzienia + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + /** @defgroup CoreGroup Standard API Core + Core encoder classes */ + + /** @addtogroup CoreGroup */ + /** @{ */ + + //########################################################################## + /** + Data block with raw data and it's Error Correction Code data. + */ class QRrsblock { public $dataLength; public $data = array(); public $eccLength; public $ecc = array(); + /** Data block Constructor + @param Integer $dl length of data stream + @param Array $data data stream + @param Integer $el ECC length + @param Array $el ECC stream (modified, by reference) + @param QRrsItem $rs RS encoding item + */ public function __construct($dl, $data, $el, &$ecc, QRrsItem $rs) { $rs->encode_rs_char($data, $ecc); @@ -2851,19 +4290,25 @@ }; //########################################################################## - + /** Raw Code holder. + Contains encoded code data before there are spatialy distributed into frame and masked. + Here goes dividing data into blocks and calculating ECC stream. */ class QRrawcode { - public $version; - public $datacode = array(); - public $ecccode = array(); - public $blocks; - public $rsblocks = array(); //of RSblock - public $count; - public $dataLength; - public $eccLength; - public $b1; + + public $version; ///< __Integer__ code Version + public $datacode = array(); ///< __Array__ data stream + public $ecccode = array(); ///< __Array__ ECC Stream + public $blocks; ///< __Integer__ RS Blocks count + public $rsblocks = array(); ///< __Array__ of RSblock, ECC code blocks + public $count; ///< __Integer__ position of currently processed ECC code + public $dataLength; ///< __Integer__ data stream length + public $eccLength; ///< __Integer__ ECC stream length + public $b1; ///< __Integer__ width of code in pixels, used as a modulo base for column overflow //---------------------------------------------------------------------- + /** Raw Code holder Constructor + @param QRinput $input input stream + */ public function __construct(QRinput $input) { $spec = array(0,0,0,0,0); @@ -2892,6 +4337,9 @@ } //---------------------------------------------------------------------- + /** Initializes Raw Code according to current code speciffication + @param Array $spec code speciffigation, as provided by QRspec + */ public function init(array $spec) { $dl = QRspec::rsDataCodes1($spec); @@ -2935,6 +4383,9 @@ } //---------------------------------------------------------------------- + /** Gets ECC code + @return Integer ECC byte for current object position + */ public function getCode() { $ret = null; @@ -2960,14 +4411,45 @@ } //########################################################################## + /** + __Main class to create QR-code__. + QR Code symbol is a 2D barcode that can be scanned by handy terminals such as a mobile phone with CCD. + The capacity of QR Code is up to 7000 digits or 4000 characters, and has high robustness. + This class supports QR Code model 2, described in JIS (Japanese Industrial Standards) X0510:2004 or ISO/IEC 18004. + Currently the following features are not supported: ECI and FNC1 mode, Micro QR Code, QR Code model 1, Structured mode. + + @abstract Class for generating QR-code images, SVG and HTML5 Canvas + @author Dominik Dzienia + @copyright 2010-2013 Dominik Dzienia and others + @link http://phpqrcode.sourceforge.net + @license http://www.gnu.org/copyleft/lesser.html LGPL + */ + class QRcode { - public $version; - public $width; - public $data; + public $version; ///< __Integer__ QR code version. Size of QRcode is defined as version. Version is from 1 to 40. Version 1 is 21*21 matrix. And 4 modules increases whenever 1 version increases. So version 40 is 177*177 matrix. + public $width; ///< __Integer__ Width of code table. Because code is square shaped - same as height. + public $data; ///< __Array__ Ready, masked code data. + + /** Canvas JS include flag. + If canvas js support library was included, we remember it static in QRcode. + (because file should be included only once) + */ + public static $jscanvasincluded = false; //---------------------------------------------------------------------- + /** + Encode mask + Main function responsible for creating code. + We get empty frame, then fill it with data from input, then select best mask and apply it. + If $mask argument is greater than -1 we assume that user want's that specific mask number (ranging form 0-7) to be used. + Otherwise (when $mask is -1) mask is detected using algorithm depending of global configuration, + + @param QRinput $input data object + @param Integer $mask sugested masking mode + @return QRcode $this (current instance) + */ public function encodeMask(QRinput $input, $mask) { if($input->getVersion() < 0 || $input->getVersion() > QRSPEC_VERSION_MAX) { @@ -2985,7 +4467,7 @@ $width = QRspec::getWidth($version); $frame = QRspec::newFrame($version); - $filler = new FrameFiller($width, $frame); + $filler = new QRframeFiller($width, $frame); if(is_null($filler)) { return NULL; } @@ -3035,23 +4517,37 @@ QRtools::markTime('after_mask'); - $this->version = $version; - $this->width = $width; - $this->data = $masked; + $this->version = $version; + $this->width = $width; + $this->data = $masked; return $this; } //---------------------------------------------------------------------- + /** + Encode input with mask detection. + Shorthand for encodeMask, without specifing particular, static mask number. + + @param QRinput $input data object to be encoded + @return + */ public function encodeInput(QRinput $input) { return $this->encodeMask($input, -1); } //---------------------------------------------------------------------- + /** + Encode string, forcing 8-bit encoding + @param String $string input string + @param Integer $version code version (size of code area) + @param Integer $level ECC level (see: Global Constants -> Levels of Error Correction) + @return QRcode $this (current instance) + */ public function encodeString8bit($string, $version, $level) { - if(string == NULL) { + if($string == NULL) { throw new Exception('empty string!'); return NULL; } @@ -3059,7 +4555,7 @@ $input = new QRinput($version, $level); if($input == NULL) return NULL; - $ret = $input->append($input, QR_MODE_8, strlen($string), str_split($string)); + $ret = $input->append(QR_MODE_8, strlen($string), str_split($string)); if($ret < 0) { unset($input); return NULL; @@ -3068,6 +4564,21 @@ } //---------------------------------------------------------------------- + /** + Encode string, using optimal encodings. + Encode string dynamically adjusting encoding for subsections of string to + minimize resulting code size. For complex string it will split string into + subsections: Numerical, Alphanumerical or 8-bit. + @param String $string input string + @param Integer $version code version (size of code area) + @param String $level ECC level (see: Global Constants -> Levels of Error Correction) + @param Integer $hint __QR_MODE_8__ or __QR_MODE_KANJI__, Because Kanji encoding + is kind of 8 bit encoding we need to hint encoder to use Kanji mode explicite. + (otherwise it may try to encode it as plain 8 bit stream) + @param Boolean $casesensitive hint if given string is case-sensitive, because + if not - encoder may use optimal QR_MODE_AN instead of QR_MODE_8 + @return QRcode $this (current instance) + */ public function encodeString($string, $version, $level, $hint, $casesensitive) { @@ -3087,7 +4598,18 @@ return $this->encodeInput($input); } - //---------------------------------------------------------------------- + //###################################################################### + /** + Creates PNG image containing QR-Code. + Simple helper function to create QR-Code Png image with one static call. + @param String $text text string to encode + @param String $outfile (optional) output file name, if __false__ outputs to browser with required headers + @param Integer $level (optional) error correction level __QR_ECLEVEL_L__, __QR_ECLEVEL_M__, __QR_ECLEVEL_Q__ or __QR_ECLEVEL_H__ + @param Integer $size (optional) pixel size, multiplier for each 'virtual' pixel + @param Integer $margin (optional) code margin (silent zone) in 'virtual' pixels + @param Boolean $saveandprint (optional) if __true__ code is outputed to browser and saved to file, otherwise only saved to file. It is effective only if $outfile is specified. + */ + public static function png($text, $outfile = false, $level = QR_ECLEVEL_L, $size = 3, $margin = 4, $saveandprint=false) { $enc = QRencode::factory($level, $size, $margin); @@ -3095,6 +4617,17 @@ } //---------------------------------------------------------------------- + /** + Creates text (1's & 0's) containing QR-Code. + Simple helper function to create QR-Code text with one static call. + @param String $text text string to encode + @param String $outfile (optional) output file name, when __false__ file is not saved + @param Integer $level (optional) error correction level __QR_ECLEVEL_L__, __QR_ECLEVEL_M__, __QR_ECLEVEL_Q__ or __QR_ECLEVEL_H__ + @param Integer $size (optional) pixel size, multiplier for each 'virtual' pixel + @param Integer $margin (optional) code margin (silent zone) in 'virtual' pixels + @return Array containing line of code with 1 and 0 for every code line + */ + public static function text($text, $outfile = false, $level = QR_ECLEVEL_L, $size = 3, $margin = 4) { $enc = QRencode::factory($level, $size, $margin); @@ -3102,25 +4635,162 @@ } //---------------------------------------------------------------------- + /** + Creates Raw Array containing QR-Code. + Simple helper function to create QR-Code array with one static call. + @param String $text text string to encode + @param Boolean $outfile (optional) not used, shuold be __false__ + @param Integer $level (optional) error correction level __QR_ECLEVEL_L__, __QR_ECLEVEL_M__, __QR_ECLEVEL_Q__ or __QR_ECLEVEL_H__ + @param Integer $size (optional) pixel size, multiplier for each 'virtual' pixel + @param Integer $margin (optional) code margin (silent zone) in 'virtual' pixels + @return Array containing Raw QR code + */ + public static function raw($text, $outfile = false, $level = QR_ECLEVEL_L, $size = 3, $margin = 4) { $enc = QRencode::factory($level, $size, $margin); return $enc->encodeRAW($text, $outfile); } + + //---------------------------------------------------------------------- + /** + Creates Html+JS code to draw QR-Code with HTML5 Canvas. + Simple helper function to create QR-Code array with one static call. + @param String $text text string to encode + @param String $elemId (optional) target Canvas tag id attribute, if __false__ Canvas tag with auto id will be created + @param Integer $level (optional) error correction level __QR_ECLEVEL_L__, __QR_ECLEVEL_M__, __QR_ECLEVEL_Q__ or __QR_ECLEVEL_H__ + @param Integer $width (optional) CANVAS element width (sam as height) + @param Integer $size (optional) pixel size, multiplier for each 'virtual' pixel + @param Integer $margin (optional) code margin (silent zone) in 'virtual' pixels + @param Boolean $autoInclude (optional) if __true__, required qrcanvas.js lib will be included (only once) + @return String containing JavaScript creating the code, Canvas element (when $elemId is __false__) and script tag with required lib (when $autoInclude is __true__ and not yet included) + */ + + public static function canvas($text, $elemId = false, $level = QR_ECLEVEL_L, $width = false, $size = false, $margin = 4, $autoInclude = false) + { + $html = ''; + $extra = ''; + + if ($autoInclude) { + if (!self::$jscanvasincluded) { + self::$jscanvasincluded = true; + echo ''; + } + } + + $enc = QRencode::factory($level, 1, 0); + $tab_src = $enc->encode($text, false); + $area = new QRcanvasOutput($tab_src); + $area->detectGroups(); + $area->detectAreas(); + + if ($elemId === false) { + $elemId = 'qrcode-'.md5(mt_rand(1000,1000000).'.'.mt_rand(1000,1000000).'.'.mt_rand(1000,1000000).'.'.mt_rand(1000,1000000)); + + if ($width == false) { + if (($size !== false) && ($size > 0)) { + $width = ($area->getWidth()+(2*$margin)) * $size; + } else { + $width = ($area->getWidth()+(2*$margin)) * 4; + } + } + + $html .= 'Your browser does not support CANVAS tag! Please upgrade to modern version of FireFox, Opera, Chrome or Safari/Webkit based browser'; + } + + if ($width !== false) { + $extra .= ', '.$width.', '.$width; + } + + if ($margin !== false) { + $extra .= ', '.$margin.', '.$margin; + } + + $html .= ''; + + return $html; + } + + //---------------------------------------------------------------------- + /** + Creates SVG with QR-Code. + Simple helper function to create QR-Code SVG with one static call. + @param String $text text string to encode + @param Boolean $elemId (optional) target SVG tag id attribute, if __false__ SVG tag with auto id will be created + @param String $outfile (optional) output file name, when __false__ file is not saved + @param Integer $level (optional) error correction level __QR_ECLEVEL_L__, __QR_ECLEVEL_M__, __QR_ECLEVEL_Q__ or __QR_ECLEVEL_H__ + @param Integer $width (optional) SVG element width (sam as height) + @param Integer $size (optional) pixel size, multiplier for each 'virtual' pixel + @param Integer $margin (optional) code margin (silent zone) in 'virtual' pixels + @param Boolean $compress (optional) if __true__, compressed SVGZ (instead plaintext SVG) is saved to file + @return String containing SVG tag + */ + + public static function svg($text, $elemId = false, $outFile = false, $level = QR_ECLEVEL_L, $width = false, $size = false, $margin = 4, $compress = false) + { + $enc = QRencode::factory($level, 1, 0); + $tab_src = $enc->encode($text, false); + $area = new QRsvgOutput($tab_src); + $area->detectGroups(); + $area->detectAreas(); + + if ($elemId === false) { + $elemId = 'qrcode-'.md5(mt_rand(1000,1000000).'.'.mt_rand(1000,1000000).'.'.mt_rand(1000,1000000).'.'.mt_rand(1000,1000000)); + + if ($width == false) { + if (($size !== false) && ($size > 0)) { + $width = ($area->getWidth()+(2*$margin)) * $size; + } else { + $width = ($area->getWidth()+(2*$margin)) * 4; + } + } + } + + $svg = ''."\n"; + + $svg .= $area->getRawSvg().''; + + if ($outFile !== false) { + $xmlPreamble = 'width = $width; @@ -3132,18 +4802,27 @@ } //---------------------------------------------------------------------- + /** Sets frame code at given position. + @param Array $at position, map containing __x__ and __y__ coordinates + @param Integer $val value to set + */ public function setFrameAt($at, $val) { $this->frame[$at['y']][$at['x']] = chr($val); } //---------------------------------------------------------------------- + /** Gets frame code from given position. + @param Array $at position, map containing __x__ and __y__ coordinates + @return Integer value at requested position + */ public function getFrameAt($at) { return ord($this->frame[$at['y']][$at['x']]); } //---------------------------------------------------------------------- + /** Proceed to next code point. */ public function next() { do { @@ -3200,22 +4879,29 @@ } ; //########################################################################## - + /** QR Code encoder. + Encoder is used by QRCode to create simple static code generators. */ class QRencode { - public $casesensitive = true; - public $eightbit = false; + public $casesensitive = true; ///< __Boolean__ does input stream id case sensitive, if not encoder may use more optimal charsets + public $eightbit = false; ///< __Boolean__ does input stream is 8 bit - public $version = 0; - public $size = 3; - public $margin = 4; + public $version = 0; ///< __Integer__ code version (total size) if __0__ - will be auto-detected + public $size = 3; ///< __Integer__ pixel zoom factor, multiplier to map virtual code pixels to image output pixels + public $margin = 4; ///< __Integer__ margin (silent zone) size, in code pixels - public $structured = 0; // not supported yet + public $structured = 0; ///< Structured QR codes. Not supported. - public $level = QR_ECLEVEL_L; - public $hint = QR_MODE_8; + public $level = QR_ECLEVEL_L; ///< __Integer__ error correction level __QR_ECLEVEL_L__, __QR_ECLEVEL_M__, __QR_ECLEVEL_Q__ or __QR_ECLEVEL_H__ + public $hint = QR_MODE_8; ///< __Integer__ encoding hint, __QR_MODE_8__ or __QR_MODE_KANJI__, Because Kanji encoding is kind of 8 bit encoding we need to hint encoder to use Kanji mode explicite. (otherwise it may try to encode it as plain 8 bit stream) //---------------------------------------------------------------------- + /** Encoder instances factory. + @param Integer $level error correction level __QR_ECLEVEL_L__, __QR_ECLEVEL_M__, __QR_ECLEVEL_Q__ or __QR_ECLEVEL_H__ + @param Integer $size pixel zoom factor, multiplier to map virtual code pixels to image output pixels + @param Integer $margin margin (silent zone) size, in code pixels + @return builded QRencode instance + */ public static function factory($level = QR_ECLEVEL_L, $size = 3, $margin = 4) { $enc = new QRencode(); @@ -3251,7 +4937,12 @@ } //---------------------------------------------------------------------- - public function encodeRAW($intext, $outfile = false) + /** Encodes input into Raw code table. + @param String $intext input text + @param Boolean $notused (optional, not used) placeholder for similar outfile parameter + @return __Array__ Raw code frame + */ + public function encodeRAW($intext, $notused = false) { $code = new QRcode(); @@ -3265,6 +4956,11 @@ } //---------------------------------------------------------------------- + /** Encodes input into binary code table. + @param String $intext input text + @param String $outfile (optional) output file to save code table, if __false__ file will be not saved + @return __Array__ binary code frame + */ public function encode($intext, $outfile = false) { $code = new QRcode(); @@ -3277,15 +4973,21 @@ QRtools::markTime('after_encode'); + $binarized = QRtools::binarize($code->data); if ($outfile!== false) { - file_put_contents($outfile, join("\n", QRtools::binarize($code->data))); - } else { - return QRtools::binarize($code->data); + file_put_contents($outfile, join("\n", $binarized)); } + + return $binarized; } //---------------------------------------------------------------------- - public function encodePNG($intext, $outfile = false,$saveandprint=false) + /** Encodes input into PNG image. + @param String $intext input text + @param String $outfile (optional) output file name, if __false__ outputs to browser with required headers + @param Boolean $saveandprint (optional) if __true__ code is outputed to browser and saved to file, otherwise only saved to file. It is effective only if $outfile is specified. + */ + public function encodePNG($intext, $outfile = false, $saveandprint=false) { try { @@ -3308,5 +5010,6 @@ } } } - - + + /** @}*/ + diff --git a/resources/celerity/map.php b/resources/celerity/map.php index d0ae1e8f0e..0911be5e57 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,10 +9,10 @@ return array( 'names' => array( 'conpherence.pkg.css' => '2f25eb4f', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => '3471f5d3', + 'core.pkg.css' => 'ac619266', 'core.pkg.js' => '2eeda9e0', 'dark-console.pkg.js' => '187792c2', - 'differential.pkg.css' => '6d3700f0', + 'differential.pkg.css' => 'fda9518d', 'differential.pkg.js' => '46fcb3af', 'diffusion.pkg.css' => '354279ea', 'diffusion.pkg.js' => '78c9885d', @@ -63,7 +63,7 @@ return array( 'rsrc/css/application/diff/diff-tree-view.css' => 'e2d3e222', 'rsrc/css/application/diff/inline-comment-summary.css' => '81eb368d', 'rsrc/css/application/differential/add-comment.css' => '7e5900d9', - 'rsrc/css/application/differential/changeset-view.css' => '1b0476bc', + 'rsrc/css/application/differential/changeset-view.css' => '979e688c', 'rsrc/css/application/differential/core.css' => '7300a73e', 'rsrc/css/application/differential/phui-inline-comment.css' => '9863a85e', 'rsrc/css/application/differential/revision-comment.css' => '7dbc8d1d', @@ -137,7 +137,7 @@ return array( 'rsrc/css/phui/object-item/phui-oi-simple-ui.css' => '9b03a61f', 'rsrc/css/phui/phui-action-list.css' => '1b0085b2', 'rsrc/css/phui/phui-action-panel.css' => '6c386cbf', - 'rsrc/css/phui/phui-badge.css' => '666e25ad', + 'rsrc/css/phui/phui-badge.css' => 'd12f6f6c', 'rsrc/css/phui/phui-basic-nav-view.css' => 'a5693cf0', 'rsrc/css/phui/phui-big-info-view.css' => '362ad37b', 'rsrc/css/phui/phui-box.css' => '5ed3b8cb', @@ -158,7 +158,7 @@ return array( 'rsrc/css/phui/phui-form.css' => '1f177cb7', 'rsrc/css/phui/phui-formation-view.css' => 'd2dec8ed', 'rsrc/css/phui/phui-head-thing.css' => 'd7f293df', - 'rsrc/css/phui/phui-header-view.css' => '36c86a58', + 'rsrc/css/phui/phui-header-view.css' => '4cd25427', 'rsrc/css/phui/phui-hovercard.css' => '39fd2e14', 'rsrc/css/phui/phui-icon-set-selector.css' => '19e0253b', 'rsrc/css/phui/phui-icon.css' => '084ac612', @@ -187,7 +187,7 @@ return array( 'rsrc/css/sprite-login.css' => '07052ee0', 'rsrc/css/sprite-tokens.css' => 'f1896dc5', 'rsrc/css/syntax/syntax-default.css' => 'c0307dc6', - 'rsrc/externals/d3/d3.min.js' => '9d068042', + 'rsrc/externals/d3/d3.min.js' => 'e97b4b78', 'rsrc/externals/font/fontawesome/fontawesome-webfont.eot' => '23f8c698', 'rsrc/externals/font/fontawesome/fontawesome-webfont.ttf' => '70983df0', 'rsrc/externals/font/fontawesome/fontawesome-webfont.woff' => 'cd02f93b', @@ -396,7 +396,7 @@ return array( 'rsrc/js/application/diffusion/behavior-pull-lastmodified.js' => 'c715c123', 'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => '6a85bc5a', 'rsrc/js/application/drydock/drydock-live-operation-status.js' => '47a0728b', - 'rsrc/js/application/fact/Chart.js' => '52e3ff03', + 'rsrc/js/application/fact/Chart.js' => '351abd1c', 'rsrc/js/application/fact/ChartCurtainView.js' => '86954222', 'rsrc/js/application/fact/ChartFunctionLabel.js' => '81de1dab', 'rsrc/js/application/files/behavior-document-engine.js' => '243d6c22', @@ -558,9 +558,9 @@ return array( 'conpherence-participant-pane-css' => '69e0058a', 'conpherence-thread-manager' => 'aec8e38c', 'conpherence-transaction-css' => '3a3f5e7e', - 'd3' => '9d068042', + 'd3' => 'e97b4b78', 'diff-tree-view-css' => 'e2d3e222', - 'differential-changeset-view-css' => '1b0476bc', + 'differential-changeset-view-css' => '979e688c', 'differential-core-view-css' => '7300a73e', 'differential-revision-add-comment-css' => '7e5900d9', 'differential-revision-comment-css' => '7dbc8d1d', @@ -702,7 +702,7 @@ return array( 'javelin-behavior-user-menu' => '60cd9241', 'javelin-behavior-view-placeholder' => 'a9942052', 'javelin-behavior-workflow' => '9623adc1', - 'javelin-chart' => '52e3ff03', + 'javelin-chart' => '351abd1c', 'javelin-chart-curtain-view' => '86954222', 'javelin-chart-function-label' => '81de1dab', 'javelin-color' => '78f811c9', @@ -822,7 +822,7 @@ return array( 'phrequent-css' => 'bd79cc67', 'phriction-document-css' => '03380da0', 'phui-action-panel-css' => '6c386cbf', - 'phui-badge-view-css' => '666e25ad', + 'phui-badge-view-css' => 'd12f6f6c', 'phui-basic-nav-view-css' => 'a5693cf0', 'phui-big-info-view-css' => '362ad37b', 'phui-box-css' => '5ed3b8cb', @@ -851,7 +851,7 @@ return array( 'phui-form-view-css' => '57edecb7', 'phui-formation-view-css' => 'd2dec8ed', 'phui-head-thing-view-css' => 'd7f293df', - 'phui-header-view-css' => '36c86a58', + 'phui-header-view-css' => '4cd25427', 'phui-hovercard' => '6199f752', 'phui-hovercard-list' => 'de4b4919', 'phui-hovercard-view-css' => '39fd2e14', @@ -1056,9 +1056,6 @@ return array( 'javelin-util', 'phabricator-keyboard-shortcut-manager', ), - '1b0476bc' => array( - 'phui-inline-comment-view-css', - ), '1b6acc2a' => array( 'javelin-magical-init', 'javelin-util', @@ -1233,6 +1230,12 @@ return array( 'aphront-typeahead-control-css', 'phui-tag-view-css', ), + '351abd1c' => array( + 'phui-chart-css', + 'd3', + 'javelin-chart-curtain-view', + 'javelin-chart-function-label', + ), '3829a3cf' => array( 'javelin-behavior', 'javelin-uri', @@ -1419,12 +1422,6 @@ return array( 'javelin-dom', 'javelin-fx', ), - '52e3ff03' => array( - 'phui-chart-css', - 'd3', - 'javelin-chart-curtain-view', - 'javelin-chart-function-label', - ), '541f81c3' => array( 'javelin-install', ), @@ -1777,6 +1774,9 @@ return array( 'javelin-stratcom', 'phabricator-notification', ), + '979e688c' => array( + 'phui-inline-comment-view-css', + ), '98ef467f' => array( 'javelin-behavior', 'javelin-dom', diff --git a/resources/sql/autopatches/20230917.fileattachment.01.delete.sql b/resources/sql/autopatches/20230917.fileattachment.01.delete.sql new file mode 100644 index 0000000000..74adf25f52 --- /dev/null +++ b/resources/sql/autopatches/20230917.fileattachment.01.delete.sql @@ -0,0 +1,6 @@ +USE {$NAMESPACE}_file; + DELETE FROM file_attachment + WHERE NOT EXISTS + (SELECT * + FROM file + WHERE phid=file_attachment.filePHID) diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 17b1e837a6..5c7e655cd1 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -436,6 +436,7 @@ phutil_register_library_map(array( 'ConpherenceViewController' => 'applications/conpherence/controller/ConpherenceViewController.php', 'CountdownEditConduitAPIMethod' => 'applications/countdown/conduit/CountdownEditConduitAPIMethod.php', 'CountdownSearchConduitAPIMethod' => 'applications/countdown/conduit/CountdownSearchConduitAPIMethod.php', + 'CowsayReferenceController' => 'applications/reference/src/controller/CowsayReferenceController.php', 'DarkConsoleController' => 'applications/console/controller/DarkConsoleController.php', 'DarkConsoleCore' => 'applications/console/core/DarkConsoleCore.php', 'DarkConsoleDataController' => 'applications/console/controller/DarkConsoleDataController.php', @@ -1304,6 +1305,7 @@ phutil_register_library_map(array( 'FeedStoryNotificationGarbageCollector' => 'applications/notification/garbagecollector/FeedStoryNotificationGarbageCollector.php', 'FerretConfigurableSearchFunction' => 'applications/search/ferret/function/FerretConfigurableSearchFunction.php', 'FerretSearchFunction' => 'applications/search/ferret/function/FerretSearchFunction.php', + 'FigletReferenceController' => 'applications/reference/src/controller/FigletReferenceController.php', 'FileAllocateConduitAPIMethod' => 'applications/files/conduit/FileAllocateConduitAPIMethod.php', 'FileConduitAPIMethod' => 'applications/files/conduit/FileConduitAPIMethod.php', 'FileCreateMailReceiver' => 'applications/files/mail/FileCreateMailReceiver.php', @@ -1802,6 +1804,7 @@ phutil_register_library_map(array( 'ManiphestEditConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestEditConduitAPIMethod.php', 'ManiphestEditEngine' => 'applications/maniphest/editor/ManiphestEditEngine.php', 'ManiphestEmailCommand' => 'applications/maniphest/command/ManiphestEmailCommand.php', + 'ManiphestFlagCustomField' => 'applications/maniphest/field/ManiphestFlagCustomField.php', 'ManiphestGetTaskTransactionsConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestGetTaskTransactionsConduitAPIMethod.php', 'ManiphestHovercardEngineExtension' => 'applications/maniphest/engineextension/ManiphestHovercardEngineExtension.php', 'ManiphestInfoConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestInfoConduitAPIMethod.php', @@ -3450,6 +3453,7 @@ phutil_register_library_map(array( 'PhabricatorFileAES256StorageFormat' => 'applications/files/format/PhabricatorFileAES256StorageFormat.php', 'PhabricatorFileAltTextTransaction' => 'applications/files/xaction/PhabricatorFileAltTextTransaction.php', 'PhabricatorFileAttachment' => 'applications/files/storage/PhabricatorFileAttachment.php', + 'PhabricatorFileAttachmentDestructionEngineExtension' => 'applications/files/engineextension/PhabricatorFileAttachmentDestructionEngineExtension.php', 'PhabricatorFileAttachmentQuery' => 'applications/files/query/PhabricatorFileAttachmentQuery.php', 'PhabricatorFileBundleLoader' => 'applications/files/query/PhabricatorFileBundleLoader.php', 'PhabricatorFileChunk' => 'applications/files/storage/PhabricatorFileChunk.php', @@ -4490,6 +4494,7 @@ phutil_register_library_map(array( 'PhabricatorProjectTransactionType' => 'applications/project/xaction/PhabricatorProjectTransactionType.php', 'PhabricatorProjectTrigger' => 'applications/project/storage/PhabricatorProjectTrigger.php', 'PhabricatorProjectTriggerAddProjectsRule' => 'applications/project/trigger/PhabricatorProjectTriggerAddProjectsRule.php', + 'PhabricatorProjectTriggerAddSubscribersRule' => 'applications/project/trigger/PhabricatorProjectTriggerAddSubscribersRule.php', 'PhabricatorProjectTriggerController' => 'applications/project/controller/trigger/PhabricatorProjectTriggerController.php', 'PhabricatorProjectTriggerCorruptionException' => 'applications/project/exception/PhabricatorProjectTriggerCorruptionException.php', 'PhabricatorProjectTriggerEditController' => 'applications/project/controller/trigger/PhabricatorProjectTriggerEditController.php', @@ -4504,6 +4509,7 @@ phutil_register_library_map(array( 'PhabricatorProjectTriggerPlaySoundRule' => 'applications/project/trigger/PhabricatorProjectTriggerPlaySoundRule.php', 'PhabricatorProjectTriggerQuery' => 'applications/project/query/PhabricatorProjectTriggerQuery.php', 'PhabricatorProjectTriggerRemoveProjectsRule' => 'applications/project/trigger/PhabricatorProjectTriggerRemoveProjectsRule.php', + 'PhabricatorProjectTriggerRemoveSubscribersRule' => 'applications/project/trigger/PhabricatorProjectTriggerRemoveSubscribersRule.php', 'PhabricatorProjectTriggerRule' => 'applications/project/trigger/PhabricatorProjectTriggerRule.php', 'PhabricatorProjectTriggerRuleRecord' => 'applications/project/trigger/PhabricatorProjectTriggerRuleRecord.php', 'PhabricatorProjectTriggerRulesetTransaction' => 'applications/project/xaction/trigger/PhabricatorProjectTriggerRulesetTransaction.php', @@ -5075,6 +5081,7 @@ phutil_register_library_map(array( 'PhabricatorTypeaheadDatasourceTestCase' => 'applications/typeahead/datasource/__tests__/PhabricatorTypeaheadDatasourceTestCase.php', 'PhabricatorTypeaheadFunctionHelpController' => 'applications/typeahead/controller/PhabricatorTypeaheadFunctionHelpController.php', 'PhabricatorTypeaheadInvalidTokenException' => 'applications/typeahead/exception/PhabricatorTypeaheadInvalidTokenException.php', + 'PhabricatorTypeaheadLoginRequiredException' => 'applications/typeahead/exception/PhabricatorTypeaheadLoginRequiredException.php', 'PhabricatorTypeaheadModularDatasourceController' => 'applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php', 'PhabricatorTypeaheadMonogramDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadMonogramDatasource.php', 'PhabricatorTypeaheadProxyDatasource' => 'applications/typeahead/datasource/PhabricatorTypeaheadProxyDatasource.php', @@ -5388,6 +5395,8 @@ phutil_register_library_map(array( 'PholioTransactionView' => 'applications/pholio/view/PholioTransactionView.php', 'PholioUploadedImageView' => 'applications/pholio/view/PholioUploadedImageView.php', 'PhorgeCodeWarningSetupCheck' => 'applications/config/check/PhorgeCodeWarningSetupCheck.php', + 'PhorgeFlagFlaggedObjectCustomField' => 'applications/flag/customfield/PhorgeFlagFlaggedObjectCustomField.php', + 'PhorgeFlagFlaggedObjectFieldStorage' => 'applications/flag/customfield/PhorgeFlagFlaggedObjectFieldStorage.php', 'PhorgeSystemDeprecationWarningListener' => 'applications/system/events/PhorgeSystemDeprecationWarningListener.php', 'PhortuneAccount' => 'applications/phortune/storage/PhortuneAccount.php', 'PhortuneAccountAddManagerController' => 'applications/phortune/controller/account/PhortuneAccountAddManagerController.php', @@ -5609,6 +5618,7 @@ phutil_register_library_map(array( 'PhrictionDocumentDatasource' => 'applications/phriction/typeahead/PhrictionDocumentDatasource.php', 'PhrictionDocumentDeleteTransaction' => 'applications/phriction/xaction/PhrictionDocumentDeleteTransaction.php', 'PhrictionDocumentDraftTransaction' => 'applications/phriction/xaction/PhrictionDocumentDraftTransaction.php', + 'PhrictionDocumentEditConduitAPIMethod' => 'applications/phriction/conduit/PhrictionDocumentEditConduitAPIMethod.php', 'PhrictionDocumentEditEngine' => 'applications/phriction/editor/PhrictionDocumentEditEngine.php', 'PhrictionDocumentEditTransaction' => 'applications/phriction/xaction/PhrictionDocumentEditTransaction.php', 'PhrictionDocumentFerretEngine' => 'applications/phriction/search/PhrictionDocumentFerretEngine.php', @@ -5790,6 +5800,8 @@ phutil_register_library_map(array( 'PhutilTranslatedHTMLTestCase' => 'infrastructure/markup/__tests__/PhutilTranslatedHTMLTestCase.php', 'PhutilTwitchAuthAdapter' => 'applications/auth/adapter/PhutilTwitchAuthAdapter.php', 'PhutilTwitterAuthAdapter' => 'applications/auth/adapter/PhutilTwitterAuthAdapter.php', + 'PhutilURIHelper' => 'infrastructure/parser/PhutilURIHelper.php', + 'PhutilURIHelperTestCase' => 'infrastructure/parser/__tests__/PhutilURIHelperTestCase.php', 'PhutilWordPressAuthAdapter' => 'applications/auth/adapter/PhutilWordPressAuthAdapter.php', 'PhutilXHPASTSyntaxHighlighter' => 'infrastructure/markup/syntax/highlighter/PhutilXHPASTSyntaxHighlighter.php', 'PhutilXHPASTSyntaxHighlighterFuture' => 'infrastructure/markup/syntax/highlighter/xhpast/PhutilXHPASTSyntaxHighlighterFuture.php', @@ -5871,7 +5883,11 @@ phutil_register_library_map(array( 'ProjectSearchConduitAPIMethod' => 'applications/project/conduit/ProjectSearchConduitAPIMethod.php', 'QueryFormattingTestCase' => 'infrastructure/storage/__tests__/QueryFormattingTestCase.php', 'QueryFuture' => 'infrastructure/storage/future/QueryFuture.php', + 'ReferenceApplication' => 'applications/reference/src/application/ReferenceApplication.php', + 'ReferenceController' => 'applications/reference/src/controller/ReferenceController.php', 'RemarkupProcessConduitAPIMethod' => 'applications/remarkup/conduit/RemarkupProcessConduitAPIMethod.php', + 'RemarkupReferenceController' => 'applications/reference/src/controller/RemarkupReferenceController.php', + 'RemarkupSyntaxDocumentationProvider' => 'infrastructure/markup/interface/RemarkupSyntaxDocumentationProvider.php', 'RemarkupValue' => 'applications/remarkup/RemarkupValue.php', 'RepositoryConduitAPIMethod' => 'applications/repository/conduit/RepositoryConduitAPIMethod.php', 'RepositoryQueryConduitAPIMethod' => 'applications/repository/conduit/RepositoryQueryConduitAPIMethod.php', @@ -6437,6 +6453,7 @@ phutil_register_library_map(array( 'ConpherenceViewController' => 'ConpherenceController', 'CountdownEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod', 'CountdownSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', + 'CowsayReferenceController' => 'ReferenceController', 'DarkConsoleController' => 'PhabricatorController', 'DarkConsoleCore' => 'Phobject', 'DarkConsoleDataController' => 'PhabricatorController', @@ -7397,6 +7414,7 @@ phutil_register_library_map(array( 'FeedStoryNotificationGarbageCollector' => 'PhabricatorGarbageCollector', 'FerretConfigurableSearchFunction' => 'FerretSearchFunction', 'FerretSearchFunction' => 'Phobject', + 'FigletReferenceController' => 'ReferenceController', 'FileAllocateConduitAPIMethod' => 'FileConduitAPIMethod', 'FileConduitAPIMethod' => 'ConduitAPIMethod', 'FileCreateMailReceiver' => 'PhabricatorApplicationMailReceiver', @@ -8008,6 +8026,7 @@ phutil_register_library_map(array( 'ManiphestEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod', 'ManiphestEditEngine' => 'PhabricatorEditEngine', 'ManiphestEmailCommand' => 'MetaMTAEmailTransactionCommand', + 'ManiphestFlagCustomField' => 'ManiphestCustomField', 'ManiphestGetTaskTransactionsConduitAPIMethod' => 'ManiphestConduitAPIMethod', 'ManiphestHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension', 'ManiphestInfoConduitAPIMethod' => 'ManiphestConduitAPIMethod', @@ -9906,6 +9925,7 @@ phutil_register_library_map(array( 'PhabricatorPolicyInterface', 'PhabricatorExtendedPolicyInterface', ), + 'PhabricatorFileAttachmentDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension', 'PhabricatorFileAttachmentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorFileBundleLoader' => 'Phobject', 'PhabricatorFileChunk' => array( @@ -11125,6 +11145,7 @@ phutil_register_library_map(array( 'PhabricatorDestructibleInterface', ), 'PhabricatorProjectTriggerAddProjectsRule' => 'PhabricatorProjectTriggerRule', + 'PhabricatorProjectTriggerAddSubscribersRule' => 'PhabricatorProjectTriggerRule', 'PhabricatorProjectTriggerController' => 'PhabricatorProjectController', 'PhabricatorProjectTriggerCorruptionException' => 'Exception', 'PhabricatorProjectTriggerEditController' => 'PhabricatorProjectTriggerController', @@ -11139,6 +11160,7 @@ phutil_register_library_map(array( 'PhabricatorProjectTriggerPlaySoundRule' => 'PhabricatorProjectTriggerRule', 'PhabricatorProjectTriggerQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorProjectTriggerRemoveProjectsRule' => 'PhabricatorProjectTriggerRule', + 'PhabricatorProjectTriggerRemoveSubscribersRule' => 'PhabricatorProjectTriggerRule', 'PhabricatorProjectTriggerRule' => 'Phobject', 'PhabricatorProjectTriggerRuleRecord' => 'Phobject', 'PhabricatorProjectTriggerRulesetTransaction' => 'PhabricatorProjectTriggerTransactionType', @@ -11201,12 +11223,18 @@ phutil_register_library_map(array( 'PhabricatorRegistrationProfile' => 'Phobject', 'PhabricatorRemarkupCachePurger' => 'PhabricatorCachePurger', 'PhabricatorRemarkupControl' => 'AphrontFormTextAreaControl', - 'PhabricatorRemarkupCowsayBlockInterpreter' => 'PhutilRemarkupBlockInterpreter', + 'PhabricatorRemarkupCowsayBlockInterpreter' => array( + 'PhutilRemarkupBlockInterpreter', + 'RemarkupSyntaxDocumentationProvider', + ), 'PhabricatorRemarkupCustomBlockRule' => 'PhutilRemarkupBlockRule', 'PhabricatorRemarkupCustomInlineRule' => 'PhutilRemarkupRule', 'PhabricatorRemarkupDocumentEngine' => 'PhabricatorDocumentEngine', 'PhabricatorRemarkupEditField' => 'PhabricatorEditField', - 'PhabricatorRemarkupFigletBlockInterpreter' => 'PhutilRemarkupBlockInterpreter', + 'PhabricatorRemarkupFigletBlockInterpreter' => array( + 'PhutilRemarkupBlockInterpreter', + 'RemarkupSyntaxDocumentationProvider', + ), 'PhabricatorRemarkupHyperlinkEngineExtension' => 'PhutilRemarkupHyperlinkEngineExtension', 'PhabricatorRemarkupUIExample' => 'PhabricatorUIExample', 'PhabricatorRemoveEmailUserLogType' => 'PhabricatorUserLogType', @@ -11811,6 +11839,7 @@ phutil_register_library_map(array( 'PhabricatorTypeaheadDatasourceTestCase' => 'PhabricatorTestCase', 'PhabricatorTypeaheadFunctionHelpController' => 'PhabricatorTypeaheadDatasourceController', 'PhabricatorTypeaheadInvalidTokenException' => 'Exception', + 'PhabricatorTypeaheadLoginRequiredException' => 'Exception', 'PhabricatorTypeaheadModularDatasourceController' => 'PhabricatorTypeaheadDatasourceController', 'PhabricatorTypeaheadMonogramDatasource' => 'PhabricatorTypeaheadDatasource', 'PhabricatorTypeaheadProxyDatasource' => 'PhabricatorTypeaheadCompositeDatasource', @@ -12212,6 +12241,8 @@ phutil_register_library_map(array( 'PholioTransactionView' => 'PhabricatorApplicationTransactionView', 'PholioUploadedImageView' => 'AphrontView', 'PhorgeCodeWarningSetupCheck' => 'PhabricatorSetupCheck', + 'PhorgeFlagFlaggedObjectCustomField' => 'PhabricatorCustomField', + 'PhorgeFlagFlaggedObjectFieldStorage' => 'Phobject', 'PhorgeSystemDeprecationWarningListener' => 'PhabricatorEventListener', 'PhortuneAccount' => array( 'PhortuneDAO', @@ -12498,6 +12529,7 @@ phutil_register_library_map(array( 'PhrictionDocumentDatasource' => 'PhabricatorTypeaheadDatasource', 'PhrictionDocumentDeleteTransaction' => 'PhrictionDocumentVersionTransaction', 'PhrictionDocumentDraftTransaction' => 'PhrictionDocumentEditTransaction', + 'PhrictionDocumentEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod', 'PhrictionDocumentEditEngine' => 'PhabricatorEditEngine', 'PhrictionDocumentEditTransaction' => 'PhrictionDocumentVersionTransaction', 'PhrictionDocumentFerretEngine' => 'PhabricatorFerretEngine', @@ -12677,6 +12709,8 @@ phutil_register_library_map(array( 'PhutilTranslatedHTMLTestCase' => 'PhutilTestCase', 'PhutilTwitchAuthAdapter' => 'PhutilOAuthAuthAdapter', 'PhutilTwitterAuthAdapter' => 'PhutilOAuth1AuthAdapter', + 'PhutilURIHelper' => 'Phobject', + 'PhutilURIHelperTestCase' => 'PhabricatorTestCase', 'PhutilWordPressAuthAdapter' => 'PhutilOAuthAuthAdapter', 'PhutilXHPASTSyntaxHighlighter' => 'Phobject', 'PhutilXHPASTSyntaxHighlighterFuture' => 'FutureProxy', @@ -12779,7 +12813,10 @@ phutil_register_library_map(array( 'ProjectSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'QueryFormattingTestCase' => 'PhabricatorTestCase', 'QueryFuture' => 'Future', + 'ReferenceApplication' => 'PhabricatorApplication', + 'ReferenceController' => 'PhabricatorController', 'RemarkupProcessConduitAPIMethod' => 'ConduitAPIMethod', + 'RemarkupReferenceController' => 'ReferenceController', 'RemarkupValue' => 'Phobject', 'RepositoryConduitAPIMethod' => 'ConduitAPIMethod', 'RepositoryQueryConduitAPIMethod' => 'RepositoryConduitAPIMethod', diff --git a/src/aphront/AphrontRequest.php b/src/aphront/AphrontRequest.php index 2561e397b6..8ce2f3e4af 100644 --- a/src/aphront/AphrontRequest.php +++ b/src/aphront/AphrontRequest.php @@ -56,8 +56,8 @@ final class AphrontRequest extends Phobject { * Applications like Paste, Diffusion, and Harbormaster use "$12-14" in the * URI to allow users to link to particular lines. * - * @param string URI data key to pull line range information from. - * @param int|null Maximum length of the range. + * @param string $key URI data key to pull line range information from. + * @param int|null $limit Maximum length of the range. * @return null|pair Null, or beginning and end of the range. */ public function getURILineRange($key, $limit) { @@ -537,8 +537,8 @@ final class AphrontRequest extends Phobject { * * To set a temporary cookie, see @{method:setTemporaryCookie}. * - * @param string Cookie name. - * @param string Cookie value. + * @param string $name Cookie name. + * @param string $value Cookie value. * @return this * @task cookie */ @@ -553,8 +553,8 @@ final class AphrontRequest extends Phobject { * * To set a durable cookie, see @{method:setCookie}. * - * @param string Cookie name. - * @param string Cookie value. + * @param string $name Cookie name. + * @param string $value Cookie value. * @return this * @task cookie */ @@ -566,9 +566,9 @@ final class AphrontRequest extends Phobject { /** * Set a cookie with a given expiration policy. * - * @param string Cookie name. - * @param string Cookie value. - * @param int Epoch timestamp for cookie expiration. + * @param string $name Cookie name. + * @param string $value Cookie value. + * @param int $expire Epoch timestamp for cookie expiration. * @return this * @task cookie */ @@ -748,7 +748,7 @@ final class AphrontRequest extends Phobject { * into a list of key-value pairs suitable for submitting via HTTP request * (with arrays flattened). * - * @param dict Data to flatten. + * @param dict $data Data to flatten. * @return dict Flat data suitable for inclusion in an HTTP * request. */ @@ -778,9 +778,11 @@ final class AphrontRequest extends Phobject { * and looks up the appropriate value in `$_SERVER` (in this case, * `"HTTP_ACCEPT_ENCODING"`). * - * @param string Canonical header name, like `"Accept-Encoding"`. - * @param wild Default value to return if header is not present. - * @param array? Read this instead of `$_SERVER`. + * @param string $name Canonical header name, like + `"Accept-Encoding"`. + * @param wild? $default Default value to return if header is not + present. + * @param array? $data Read this instead of `$_SERVER`. * @return string|wild Header value if present, or `$default` if not. */ public static function getHTTPHeader($name, $default = null, $data = null) { @@ -844,7 +846,7 @@ final class AphrontRequest extends Phobject { * This is not a general-purpose proxying method; it is a specialized * method with niche applications and severe security implications. * - * @param string URI identifying the host we are proxying the request to. + * @param string URI $uri identifying the host we are proxying the request to. * @return HTTPSFuture New proxy future. * * @phutil-external-symbol class PhabricatorStartup diff --git a/src/aphront/configuration/AphrontApplicationConfiguration.php b/src/aphront/configuration/AphrontApplicationConfiguration.php index 3bdd2c00bc..af05993e2b 100644 --- a/src/aphront/configuration/AphrontApplicationConfiguration.php +++ b/src/aphront/configuration/AphrontApplicationConfiguration.php @@ -510,8 +510,8 @@ final class AphrontApplicationConfiguration * Map a specific path to the corresponding controller. For a description * of routing, see @{method:buildController}. * - * @param list List of routing maps. - * @param string Path to route. + * @param list $maps List of routing maps. + * @param string $path Path to route. * @return pair Controller and dictionary of request * parameters. * @task routing @@ -523,6 +523,7 @@ final class AphrontApplicationConfiguration return array($result->getController(), $result->getURIData()); } } + return null; } private function buildSiteForRequest(AphrontRequest $request) { @@ -561,7 +562,7 @@ final class AphrontApplicationConfiguration /** * Tests if a response is of a valid type. * - * @param wild Supposedly valid response. + * @param wild $response Supposedly valid response. * @return bool True if the object is of a valid type. * @task response */ @@ -582,8 +583,9 @@ final class AphrontApplicationConfiguration * Verifies that the return value from an @{class:AphrontController} is * of an allowed type. * - * @param AphrontController Controller which returned the response. - * @param wild Supposedly valid response. + * @param AphrontController $controller Controller which returned the + * response. + * @param wild $response Supposedly valid response. * @return void * @task response */ @@ -611,9 +613,9 @@ final class AphrontApplicationConfiguration * Verifies that the return value from an * @{class:AphrontResponseProducerInterface} is of an allowed type. * - * @param AphrontResponseProducerInterface Object which produced + * @param AphrontResponseProducerInterface $producer Object which produced * this response. - * @param wild Supposedly valid response. + * @param wild $response Supposedly valid response. * @return void * @task response */ @@ -641,9 +643,9 @@ final class AphrontApplicationConfiguration * Verifies that the return value from an * @{class:AphrontRequestExceptionHandler} is of an allowed type. * - * @param AphrontRequestExceptionHandler Object which produced this + * @param AphrontRequestExceptionHandler $handler Object which produced this * response. - * @param wild Supposedly valid response. + * @param wild $response Supposedly valid response. * @return void * @task response */ @@ -677,9 +679,9 @@ final class AphrontApplicationConfiguration * If a controller returns a response producer, invoke it now and produce * the real response. * - * @param AphrontRequest Request being handled. - * @param AphrontResponse|AphrontResponseProducerInterface Response, or - * response producer. + * @param AphrontRequest $request Request being handled. + * @param AphrontResponse|AphrontResponseProducerInterface $response + * Response, or response producer. * @return AphrontResponse Response after any required production. * @task response */ @@ -737,7 +739,7 @@ final class AphrontApplicationConfiguration * This method delegates exception handling to available subclasses of * @{class:AphrontRequestExceptionHandler}. * - * @param Throwable Exception which needs to be handled. + * @param Throwable $throwable Exception which needs to be handled. * @return wild Response or response producer, or null if no available * handler can produce a response. * @task exception diff --git a/src/aphront/httpparametertype/AphrontHTTPParameterType.php b/src/aphront/httpparametertype/AphrontHTTPParameterType.php index 78a62a663c..a31101a9fc 100644 --- a/src/aphront/httpparametertype/AphrontHTTPParameterType.php +++ b/src/aphront/httpparametertype/AphrontHTTPParameterType.php @@ -31,7 +31,7 @@ abstract class AphrontHTTPParameterType extends Phobject { * example, a type might lookup usernames or project names. These types need * to use the current viewer to execute queries. * - * @param PhabricatorUser Current viewer. + * @param PhabricatorUser $viewer Current viewer. * @return this * @task read */ @@ -58,8 +58,8 @@ abstract class AphrontHTTPParameterType extends Phobject { /** * Test if a value is present in a request. * - * @param AphrontRequest The incoming request. - * @param string The key to examine. + * @param AphrontRequest $request The incoming request. + * @param string $key The key to examine. * @return bool True if a readable value is present in the request. * @task read */ @@ -74,8 +74,8 @@ abstract class AphrontHTTPParameterType extends Phobject { * If the value is not present, a default value is returned (usually `null`). * Use @{method:getExists} to test if a value is present. * - * @param AphrontRequest The incoming request. - * @param string The key to examine. + * @param AphrontRequest $request The incoming request. + * @param string $key The key to examine. * @return wild Value, or default if value is not present. * @task read */ @@ -165,9 +165,9 @@ abstract class AphrontHTTPParameterType extends Phobject { * existence check that a simpler "list of strings" type has, and can just * call the simpler type to reuse its behavior. * - * @param AphrontHTTPParameterType The other type. - * @param AphrontRequest Incoming request. - * @param string Key to examine. + * @param AphrontHTTPParameterType $type The other type. + * @param AphrontRequest $request Incoming request. + * @param string $key Key to examine. * @return bool True if the parameter exists. * @task util */ @@ -189,9 +189,9 @@ abstract class AphrontHTTPParameterType extends Phobject { * type. For example, a "list of users" type may start by running the same * basic parsing that a simpler "list of strings" type does. * - * @param AphrontHTTPParameterType The other type. - * @param AphrontRequest Incoming request. - * @param string Key to examine. + * @param AphrontHTTPParameterType $type The other type. + * @param AphrontRequest $request Incoming request. + * @param string $key Key to examine. * @return wild Parsed value. * @task util */ @@ -233,8 +233,8 @@ abstract class AphrontHTTPParameterType extends Phobject { * To call another type's behavior in order to perform this check, use * @{method:getExistsWithType}. * - * @param AphrontRequest The incoming request. - * @param string The key to examine. + * @param AphrontRequest $request The incoming request. + * @param string $key The key to examine. * @return bool True if a readable value is present in the request. * @task impl */ @@ -253,8 +253,8 @@ abstract class AphrontHTTPParameterType extends Phobject { * To call another type's behavior in order to parse a value, use * @{method:getValueWithType}. * - * @param AphrontRequest The incoming request. - * @param string The key to examine. + * @param AphrontRequest $request The incoming request. + * @param string $key The key to examine. * @return wild Parsed value. * @task impl */ diff --git a/src/aphront/response/AphrontFileResponse.php b/src/aphront/response/AphrontFileResponse.php index 6508ad6020..522b111a04 100644 --- a/src/aphront/response/AphrontFileResponse.php +++ b/src/aphront/response/AphrontFileResponse.php @@ -30,7 +30,7 @@ final class AphrontFileResponse extends AphrontResponse { /** * Set a download filename * - * @param $download string + * @param string $download * @return self */ public function setDownload($download) { diff --git a/src/aphront/response/AphrontRedirectResponse.php b/src/aphront/response/AphrontRedirectResponse.php index 390ad193c9..5494d3e0e3 100644 --- a/src/aphront/response/AphrontRedirectResponse.php +++ b/src/aphront/response/AphrontRedirectResponse.php @@ -117,8 +117,9 @@ class AphrontRedirectResponse extends AphrontResponse { * ambiguity. For example, Chrome interprets "Location: /\evil.com" to mean * "perform a protocol-relative redirect to evil.com". * - * @param string URI to redirect to. - * @param bool True if this URI identifies a remote resource. + * @param string $uri URI to redirect to. + * @param bool $is_external True if this URI identifies a remote + * resource. * @return string URI for use in a "Location:" header. */ public static function getURIForRedirect($uri, $is_external) { diff --git a/src/aphront/sink/AphrontHTTPSink.php b/src/aphront/sink/AphrontHTTPSink.php index 9e43e4a687..f2a2f50860 100644 --- a/src/aphront/sink/AphrontHTTPSink.php +++ b/src/aphront/sink/AphrontHTTPSink.php @@ -28,7 +28,8 @@ abstract class AphrontHTTPSink extends Phobject { /** * Write an HTTP status code to the output. * - * @param int Numeric HTTP status code. + * @param int $code Numeric HTTP status code. + * @param string? $message * @return void */ final public function writeHTTPStatus($code, $message = '') { @@ -44,7 +45,7 @@ abstract class AphrontHTTPSink extends Phobject { /** * Write HTTP headers to the output. * - * @param list List of pairs. + * @param list $headers List of pairs. * @return void */ final public function writeHeaders(array $headers) { @@ -89,7 +90,7 @@ abstract class AphrontHTTPSink extends Phobject { /** * Write HTTP body data to the output. * - * @param string Body data. + * @param string $data Body data. * @return void */ final public function writeData($data) { @@ -100,7 +101,7 @@ abstract class AphrontHTTPSink extends Phobject { /** * Write an entire @{class:AphrontResponse} to the output. * - * @param AphrontResponse The response object to write. + * @param AphrontResponse $response The response object to write. * @return void */ final public function writeResponse(AphrontResponse $response) { diff --git a/src/aphront/site/AphrontRoutingMap.php b/src/aphront/site/AphrontRoutingMap.php index bda98429f9..687aae93bb 100644 --- a/src/aphront/site/AphrontRoutingMap.php +++ b/src/aphront/site/AphrontRoutingMap.php @@ -50,7 +50,7 @@ final class AphrontRoutingMap extends Phobject { /** * Find the route matching a path, if one exists. * - * @param string Path to route. + * @param string $path Path to route. * @return AphrontRoutingResult|null Routing result, if path matches map. * @task routing */ @@ -84,9 +84,9 @@ final class AphrontRoutingMap extends Phobject { /** * Test a sub-map to see if any routes match a path. * - * @param string Path to route. - * @param string Pattern from the map. - * @param string Value from the map. + * @param string $route Pattern from the map. + * @param string $value Value from the map. + * @param string $path Path to route. * @return dict|null Match details, if path matches sub-map. * @task routing */ diff --git a/src/aphront/writeguard/AphrontWriteGuard.php b/src/aphront/writeguard/AphrontWriteGuard.php index 589a0db37b..6a3d053cf1 100644 --- a/src/aphront/writeguard/AphrontWriteGuard.php +++ b/src/aphront/writeguard/AphrontWriteGuard.php @@ -57,7 +57,7 @@ final class AphrontWriteGuard extends Phobject { * the request, or abort the request (e.g., by throwing an exception) if a * valid token isn't present. * - * @param callable CSRF callback. + * @param $callback Callable CSRF callback. * @return this * @task manage */ diff --git a/src/applications/almanac/query/AlmanacDeviceSearchEngine.php b/src/applications/almanac/query/AlmanacDeviceSearchEngine.php index 86633ac870..b4f7836720 100644 --- a/src/applications/almanac/query/AlmanacDeviceSearchEngine.php +++ b/src/applications/almanac/query/AlmanacDeviceSearchEngine.php @@ -137,4 +137,30 @@ final class AlmanacDeviceSearchEngine return $result; } + protected function getNewUserBody() { + $see_network = id(new PHUIButtonView()) + ->setTag('a') + ->setText(pht('See Networks')) + ->setHref('/almanac/network/'); + + $create_button = id(new PHUIButtonView()) + ->setTag('a') + ->setText(pht('Create a Device')) + ->setHref('/almanac/device/edit/') + ->setIcon('fa-plus') + ->setColor(PHUIButtonView::GREEN); + + $app_name = pht('Devices'); + $view = id(new PHUIBigInfoView()) + ->setIcon('fa-server') + ->setTitle(pht('Welcome to %s', $app_name)) + ->setDescription( + pht( + 'Use Almanac devices to catalogue your build hosts '. + 'and their SSH ports your network, and more.')) + ->addAction($see_network) + ->addAction($create_button); + + return $view; + } } diff --git a/src/applications/almanac/query/AlmanacNetworkSearchEngine.php b/src/applications/almanac/query/AlmanacNetworkSearchEngine.php index 5938fd44c7..7bb8b85116 100644 --- a/src/applications/almanac/query/AlmanacNetworkSearchEngine.php +++ b/src/applications/almanac/query/AlmanacNetworkSearchEngine.php @@ -87,4 +87,25 @@ final class AlmanacNetworkSearchEngine return $result; } + + protected function getNewUserBody() { + $create_button = id(new PHUIButtonView()) + ->setTag('a') + ->setText(pht('Create a Network')) + ->setHref('/almanac/network/edit/') + ->setIcon('fa-plus') + ->setColor(PHUIButtonView::GREEN); + + $app_name = pht('Networks'); + $view = id(new PHUIBigInfoView()) + ->setIcon('fa-globe') + ->setTitle(pht('Welcome to %s', $app_name)) + ->setDescription( + pht( + 'Use Almanac networks to catalogue private and public '. + 'computer networks.')) + ->addAction($create_button); + + return $view; + } } diff --git a/src/applications/almanac/query/AlmanacServiceSearchEngine.php b/src/applications/almanac/query/AlmanacServiceSearchEngine.php index f9786a87d2..46a9ebfbad 100644 --- a/src/applications/almanac/query/AlmanacServiceSearchEngine.php +++ b/src/applications/almanac/query/AlmanacServiceSearchEngine.php @@ -115,4 +115,32 @@ final class AlmanacServiceSearchEngine return $result; } + + protected function getNewUserBody() { + $see_devices = id(new PHUIButtonView()) + ->setTag('a') + ->setText(pht('See Devices')) + ->setHref('/almanac/device/'); + + $create_button = id(new PHUIButtonView()) + ->setTag('a') + ->setText(pht('Create a Service')) + ->setHref('/almanac/service/edit/') + ->setIcon('fa-plus') + ->setColor(PHUIButtonView::GREEN); + + + $app_name = pht('Services'); + $view = id(new PHUIBigInfoView()) + ->setIcon('fa-plug') + ->setTitle(pht('Welcome to %s', $app_name)) + ->setDescription( + pht( + 'Services describe pools of devices, and '. + 'they are available to Drydock for CI/CD, and more.')) + ->addAction($see_devices) + ->addAction($create_button); + + return $view; + } } diff --git a/src/applications/auth/adapter/PhutilBitbucketAuthAdapter.php b/src/applications/auth/adapter/PhutilBitbucketAuthAdapter.php index 1384548d5e..baf9d5407e 100644 --- a/src/applications/auth/adapter/PhutilBitbucketAuthAdapter.php +++ b/src/applications/auth/adapter/PhutilBitbucketAuthAdapter.php @@ -14,7 +14,7 @@ final class PhutilBitbucketAuthAdapter extends PhutilOAuth1AuthAdapter { public function getAccountURI() { $name = $this->getAccountID(); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return 'https://bitbucket.org/'.$name; } return null; diff --git a/src/applications/auth/adapter/PhutilGitHubAuthAdapter.php b/src/applications/auth/adapter/PhutilGitHubAuthAdapter.php index a9d4c849d5..5d85a2ac21 100644 --- a/src/applications/auth/adapter/PhutilGitHubAuthAdapter.php +++ b/src/applications/auth/adapter/PhutilGitHubAuthAdapter.php @@ -31,7 +31,7 @@ final class PhutilGitHubAuthAdapter extends PhutilOAuthAuthAdapter { public function getAccountURI() { $name = $this->getAccountName(); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return 'https://github.com/'.$name; } return null; diff --git a/src/applications/auth/adapter/PhutilTwitterAuthAdapter.php b/src/applications/auth/adapter/PhutilTwitterAuthAdapter.php index 6f738c75f6..02a1f59a67 100644 --- a/src/applications/auth/adapter/PhutilTwitterAuthAdapter.php +++ b/src/applications/auth/adapter/PhutilTwitterAuthAdapter.php @@ -17,7 +17,7 @@ final class PhutilTwitterAuthAdapter extends PhutilOAuth1AuthAdapter { public function getAccountURI() { $name = $this->getAccountName(); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return 'https://twitter.com/'.$name; } return null; diff --git a/src/applications/auth/constants/PhabricatorCommonPasswords.php b/src/applications/auth/constants/PhabricatorCommonPasswords.php index 1313257553..e74a255b09 100644 --- a/src/applications/auth/constants/PhabricatorCommonPasswords.php +++ b/src/applications/auth/constants/PhabricatorCommonPasswords.php @@ -16,7 +16,7 @@ final class PhabricatorCommonPasswords extends Phobject { /** * Check if a password is extremely common. * - * @param string Password to test. + * @param string $password Password to test. * @return bool True if the password is pathologically weak. * * @task common diff --git a/src/applications/auth/constants/PhabricatorCookies.php b/src/applications/auth/constants/PhabricatorCookies.php index 9dc9d823b2..607240c97b 100644 --- a/src/applications/auth/constants/PhabricatorCookies.php +++ b/src/applications/auth/constants/PhabricatorCookies.php @@ -77,7 +77,7 @@ final class PhabricatorCookies extends Phobject { * Set the client ID cookie. This is a random cookie used like a CSRF value * during authentication workflows. * - * @param AphrontRequest Request to modify. + * @param AphrontRequest $request Request to modify. * @return void * @task clientid */ @@ -105,10 +105,10 @@ final class PhabricatorCookies extends Phobject { * written, to avoid writing over a real URI with a bunch of "humans.txt" * stuff. See T3793 for discussion. * - * @param AphrontRequest Request to write to. - * @param string URI to write. - * @param bool Write this cookie even if we have a fresh - * cookie already. + * @param AphrontRequest $request Request to write to. + * @param string $next_uri URI to write. + * @param bool? $force Write this cookie even if we have a + * fresh cookie already. * @return void * * @task next @@ -139,7 +139,7 @@ final class PhabricatorCookies extends Phobject { /** * Read the URI out of the Next URI cookie. * - * @param AphrontRequest Request to examine. + * @param AphrontRequest $request Request to examine. * @return string|null Next URI cookie's URI value. * * @task next @@ -155,7 +155,7 @@ final class PhabricatorCookies extends Phobject { /** * Parse a Next URI cookie into its components. * - * @param string Raw cookie value. + * @param string $cookie Raw cookie value. * @return list List of timestamp and URI. * * @task next diff --git a/src/applications/auth/controller/PhabricatorAuthController.php b/src/applications/auth/controller/PhabricatorAuthController.php index 8bba90a1e6..bb95c2ec72 100644 --- a/src/applications/auth/controller/PhabricatorAuthController.php +++ b/src/applications/auth/controller/PhabricatorAuthController.php @@ -44,8 +44,9 @@ abstract class PhabricatorAuthController extends PhabricatorController { * the user's cookies are set. However, event listeners can intercept this * event and do something else if they prefer. * - * @param PhabricatorUser User to log the viewer in as. - * @param bool True to issue a full session immediately, bypassing MFA. + * @param PhabricatorUser $user User to log the viewer in as. + * @param bool? $force_full_session True to issue a full session immediately, + * bypassing MFA. * @return AphrontResponse Response which continues the login process. */ protected function loginUser( diff --git a/src/applications/auth/controller/PhabricatorAuthStartController.php b/src/applications/auth/controller/PhabricatorAuthStartController.php index 76c7eb9df5..57e294f6dc 100644 --- a/src/applications/auth/controller/PhabricatorAuthStartController.php +++ b/src/applications/auth/controller/PhabricatorAuthStartController.php @@ -31,7 +31,7 @@ final class PhabricatorAuthStartController $session_token = $request->getCookie(PhabricatorCookies::COOKIE_SESSION); $did_clear = $request->getStr('cleared'); - if (strlen($session_token)) { + if (phutil_nonempty_string($session_token)) { $kind = PhabricatorAuthSessionEngine::getSessionKindFromToken( $session_token); switch ($kind) { diff --git a/src/applications/auth/controller/PhabricatorAuthUnlinkController.php b/src/applications/auth/controller/PhabricatorAuthUnlinkController.php index a4843a7ccd..af6732a796 100644 --- a/src/applications/auth/controller/PhabricatorAuthUnlinkController.php +++ b/src/applications/auth/controller/PhabricatorAuthUnlinkController.php @@ -102,7 +102,7 @@ final class PhabricatorAuthUnlinkController ->addHiddenInput('confirmations', implode(',', $confirmations)) ->appendParagraph( pht( - 'This is the only external login account linked to your Phabicator '. + 'This is the only external login account linked to your '. 'account. If you remove it, you may no longer be able to log in.')) ->appendParagraph( pht( diff --git a/src/applications/auth/engine/PhabricatorAuthSessionEngine.php b/src/applications/auth/engine/PhabricatorAuthSessionEngine.php index 251c8284ef..30d85b6fac 100644 --- a/src/applications/auth/engine/PhabricatorAuthSessionEngine.php +++ b/src/applications/auth/engine/PhabricatorAuthSessionEngine.php @@ -75,7 +75,7 @@ final class PhabricatorAuthSessionEngine extends Phobject { * Get the session kind (e.g., anonymous, user, external account) from a * session token. Returns a `KIND_` constant. * - * @param string Session token. + * @param string $session_token Session token. * @return const Session kind constant. */ public static function getSessionKindFromToken($session_token) { @@ -109,8 +109,8 @@ final class PhabricatorAuthSessionEngine extends Phobject { * loading context. This prevents use of a Conduit sesssion as a Web * session, for example. * - * @param const The type of session to load. - * @param string The session token. + * @param const $session_type The type of session to load. + * @param string $session_token The session token. * @return PhabricatorUser|null * @task use */ @@ -250,11 +250,12 @@ final class PhabricatorAuthSessionEngine extends Phobject { * You can configure the maximum number of concurrent sessions for various * session types in the Phabricator configuration. * - * @param const Session type constant (see + * @param const $session_type Session type constant (see * @{class:PhabricatorAuthSession}). - * @param phid|null Identity to establish a session for, usually a user - * PHID. With `null`, generates an anonymous session. - * @param bool True to issue a partial session. + * @param phid|null $identity_phid Identity to establish a session for, + * usually a user PHID. With `null`, generates an + * anonymous session. + * @param bool $partial True to issue a partial session. * @return string Newly generated session key. */ public function establishSession($session_type, $identity_phid, $partial) { @@ -325,9 +326,9 @@ final class PhabricatorAuthSessionEngine extends Phobject { * This is used when users change passwords, linked accounts, or add * multifactor authentication. * - * @param PhabricatorUser User whose sessions should be terminated. - * @param string|null Optionally, one session to keep. Normally, the current - * login session. + * @param PhabricatorUser $user User whose sessions should be terminated. + * @param string|null? $except_session Optionally, one session to keep. + * Normally, the current login session. * * @return void */ @@ -388,9 +389,11 @@ final class PhabricatorAuthSessionEngine extends Phobject { * does not upgrade the user's session as a side effect. This method is * appropriate for one-time checks. * - * @param PhabricatorUser User whose session needs to be in high security. - * @param AphrontRequest Current request. - * @param string URI to return the user to if they cancel. + * @param PhabricatorUser $viewer User whose session needs to be in high + * security. + * @param AphrontRequest $request Current request. + * @param string $cancel_uri URI to return the user to if they + * cancel. * @return PhabricatorAuthHighSecurityToken Security token. * @task hisec */ @@ -420,12 +423,14 @@ final class PhabricatorAuthSessionEngine extends Phobject { * take multiple high security actions. To perform a one-time check instead, * use @{method:requireHighSecurityToken}. * - * @param PhabricatorUser User whose session needs to be in high security. - * @param AphrontRequest Current request. - * @param string URI to return the user to if they cancel. - * @param bool True to jump partial sessions directly into high - * security instead of just upgrading them to full - * sessions. + * @param PhabricatorUser $viewer User whose session needs to be in high + * security. + * @param AphrontRequest $request Current request. + * @param string $cancel_uri URI to return the user to if they + * cancel. + * @param bool? $jump_into_hisec True to jump partial sessions + * directly into high security instead of just + * upgrading them to full sessions. * @return PhabricatorAuthHighSecurityToken Security token. * @task hisec */ @@ -736,8 +741,8 @@ final class PhabricatorAuthSessionEngine extends Phobject { /** * Issue a high security token for a session, if authorized. * - * @param PhabricatorAuthSession Session to issue a token for. - * @param bool Force token issue. + * @param PhabricatorAuthSession $session Session to issue a token for. + * @param bool? $force Force token issue. * @return PhabricatorAuthHighSecurityToken|null Token, if authorized. * @task hisec */ @@ -756,8 +761,10 @@ final class PhabricatorAuthSessionEngine extends Phobject { /** * Render a form for providing relevant multi-factor credentials. * - * @param PhabricatorUser Viewing user. - * @param AphrontRequest Current request. + * @param array $factors + * @param array $validation_results + * @param PhabricatorUser $viewer Viewing user. + * @param AphrontRequest $request Current request. * @return AphrontFormView Renderable form. * @task hisec */ @@ -810,8 +817,9 @@ final class PhabricatorAuthSessionEngine extends Phobject { * * Kicks a session out of high security and logs the exit. * - * @param PhabricatorUser Acting user. - * @param PhabricatorAuthSession Session to return to normal security. + * @param PhabricatorUser $viewer Acting user. + * @param PhabricatorAuthSession $session Session to return to normal + * security. * @return void * @task hisec */ @@ -843,7 +851,7 @@ final class PhabricatorAuthSessionEngine extends Phobject { /** * Upgrade a partial session to a full session. * - * @param PhabricatorAuthSession Session to upgrade. + * @param PhabricatorUser $viewer Viewer whose session should upgrade. * @return void * @task partial */ @@ -885,8 +893,8 @@ final class PhabricatorAuthSessionEngine extends Phobject { /** * Upgrade a session to have all legalpad documents signed. * - * @param PhabricatorUser User whose session should upgrade. - * @param array LegalpadDocument objects + * @param PhabricatorUser $viewer User whose session should upgrade. + * @param array $docs LegalpadDocument objects * @return void * @task partial */ @@ -934,13 +942,14 @@ final class PhabricatorAuthSessionEngine extends Phobject { * These URIs are used for password recovery and to regain access to accounts * which users have been locked out of. * - * @param PhabricatorUser User to generate a URI for. - * @param PhabricatorUserEmail Optionally, email to verify when + * @param PhabricatorUser $user User to generate a URI for. + * @param PhabricatorUserEmail? $email Optionally, email to verify when * link is used. - * @param string Optional context string for the URI. This is purely cosmetic - * and used only to customize workflow and error messages. - * @param bool True to generate a URI which forces an immediate upgrade to - * a full session, bypassing MFA and other login checks. + * @param string? $type Optional context string for the URI. This is purely + * cosmetic and used only to customize workflow and error messages. + * @param bool? $force_full_session True to generate a URI which forces an + * immediate upgrade to a full session, bypassing MFA and other login + * checks. * @return string Login URI. * @task onetime */ @@ -984,10 +993,10 @@ final class PhabricatorAuthSessionEngine extends Phobject { /** * Load the temporary token associated with a given one-time login key. * - * @param PhabricatorUser User to load the token for. - * @param PhabricatorUserEmail Optionally, email to verify when + * @param PhabricatorUser $user User to load the token for. + * @param PhabricatorUserEmail? $email Optionally, email to verify when * link is used. - * @param string Key user is presenting as a valid one-time login key. + * @param string? $key Key user is presenting as a valid one-time login key. * @return PhabricatorAuthTemporaryToken|null Token, if one exists. * @task onetime */ @@ -1012,10 +1021,10 @@ final class PhabricatorAuthSessionEngine extends Phobject { /** * Hash a one-time login key for storage as a temporary token. * - * @param PhabricatorUser User this key is for. - * @param PhabricatorUserEmail Optionally, email to verify when + * @param PhabricatorUser $user User this key is for. + * @param PhabricatorUserEmail? $email Optionally, email to verify when * link is used. - * @param string The one time login key. + * @param string? $key The one time login key. * @return string Hash of the key. * task onetime */ diff --git a/src/applications/auth/password/PhabricatorAuthPasswordHashInterface.php b/src/applications/auth/password/PhabricatorAuthPasswordHashInterface.php index af20db3ed4..f5317bef6f 100644 --- a/src/applications/auth/password/PhabricatorAuthPasswordHashInterface.php +++ b/src/applications/auth/password/PhabricatorAuthPasswordHashInterface.php @@ -14,8 +14,8 @@ interface PhabricatorAuthPasswordHashInterface { * as their password or picking other passwords which are trivially similar * to an account or object identifier. * - * @param PhabricatorUser The user selecting the password. - * @param PhabricatorAuthPasswordEngine The password engine updating a + * @param PhabricatorUser $viewer The user selecting the password. + * @param PhabricatorAuthPasswordEngine $engine The password engine updating a * password. * @return list Blocklist of nonsecret identifiers which the password * should not be similar to. diff --git a/src/applications/auth/provider/PhabricatorAuthProvider.php b/src/applications/auth/provider/PhabricatorAuthProvider.php index 735dd42b4b..4b90daa2aa 100644 --- a/src/applications/auth/provider/PhabricatorAuthProvider.php +++ b/src/applications/auth/provider/PhabricatorAuthProvider.php @@ -460,9 +460,9 @@ abstract class PhabricatorAuthProvider extends Phobject { * - `uri`: URI the button should take the user to when clicked. * - `method`: Optional HTTP method the button should use, defaults to GET. * - * @param AphrontRequest HTTP request. - * @param string Request mode string. - * @param map Additional parameters, see above. + * @param AphrontRequest $request HTTP request. + * @param string $mode Request mode string. + * @param map? $attributes Additional parameters, see above. * @return wild Log in button. */ protected function renderStandardLoginButton( diff --git a/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php b/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php index 36a83f3678..3975e8da79 100644 --- a/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php +++ b/src/applications/auth/provider/PhabricatorLDAPAuthProvider.php @@ -142,10 +142,10 @@ final class PhabricatorLDAPAuthProvider extends PhabricatorAuthProvider { $username = $request->getStr('ldap_username'); $password = $request->getStr('ldap_password'); - $has_password = strlen($password); + $has_password = phutil_nonempty_string($password); $password = new PhutilOpaqueEnvelope($password); - if (!strlen($username) || !$has_password) { + if (!phutil_nonempty_string($username) || !$has_password) { $response = $controller->buildProviderPageResponse( $this, $this->renderLoginForm($request, 'login')); @@ -154,7 +154,7 @@ final class PhabricatorLDAPAuthProvider extends PhabricatorAuthProvider { if ($request->isFormPost()) { try { - if (strlen($username) && $has_password) { + if (phutil_nonempty_string($username) && $has_password) { $adapter = $this->getAdapter(); $adapter->setLoginUsername($username); $adapter->setLoginPassword($password); diff --git a/src/applications/auth/view/PhabricatorAuthAccountView.php b/src/applications/auth/view/PhabricatorAuthAccountView.php index f903f45d26..82ac7c6152 100644 --- a/src/applications/auth/view/PhabricatorAuthAccountView.php +++ b/src/applications/auth/view/PhabricatorAuthAccountView.php @@ -63,7 +63,7 @@ final class PhabricatorAuthAccountView extends AphrontView { )); $account_uri = $account->getAccountURI(); - if (strlen($account_uri)) { + if (phutil_nonempty_string($account_uri)) { // Make sure we don't link a "javascript:" URI if a user somehow // managed to get one here. diff --git a/src/applications/badges/controller/PhabricatorBadgesAwardController.php b/src/applications/badges/controller/PhabricatorBadgesAwardController.php index 63af70b791..1a31584a51 100644 --- a/src/applications/badges/controller/PhabricatorBadgesAwardController.php +++ b/src/applications/badges/controller/PhabricatorBadgesAwardController.php @@ -6,6 +6,8 @@ final class PhabricatorBadgesAwardController public function handleRequest(AphrontRequest $request) { $viewer = $request->getViewer(); $id = $request->getURIData('id'); + $errors = array(); + $e_badge = true; $user = id(new PhabricatorPeopleQuery()) ->setViewer($viewer) @@ -19,37 +21,41 @@ final class PhabricatorBadgesAwardController if ($request->isFormPost()) { $badge_phids = $request->getArr('badgePHIDs'); - $badges = id(new PhabricatorBadgesQuery()) - ->setViewer($viewer) - ->withPHIDs($badge_phids) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_EDIT, - PhabricatorPolicyCapability::CAN_VIEW, - )) - ->execute(); - if (!$badges) { - return new Aphront404Response(); + + if (empty($badge_phids)) { + $errors[] = pht('Badge name is required.'); + $e_badge = pht('Required'); } - $award_phids = array($user->getPHID()); + if (!$errors) { + $badges = id(new PhabricatorBadgesQuery()) + ->setViewer($viewer) + ->withPHIDs($badge_phids) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_EDIT, + PhabricatorPolicyCapability::CAN_VIEW, + )) + ->execute(); + $award_phids = array($user->getPHID()); - foreach ($badges as $badge) { - $xactions = array(); - $xactions[] = id(new PhabricatorBadgesTransaction()) - ->setTransactionType( - PhabricatorBadgesBadgeAwardTransaction::TRANSACTIONTYPE) - ->setNewValue($award_phids); + foreach ($badges as $badge) { + $xactions = array(); + $xactions[] = id(new PhabricatorBadgesTransaction()) + ->setTransactionType( + PhabricatorBadgesBadgeAwardTransaction::TRANSACTIONTYPE) + ->setNewValue($award_phids); - $editor = id(new PhabricatorBadgesEditor()) - ->setActor($viewer) - ->setContentSourceFromRequest($request) - ->setContinueOnNoEffect(true) - ->setContinueOnMissingFields(true) - ->applyTransactions($badge, $xactions); + $editor = id(new PhabricatorBadgesEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true) + ->applyTransactions($badge, $xactions); + } + + return id(new AphrontRedirectResponse()) + ->setURI($view_uri); } - - return id(new AphrontRedirectResponse()) - ->setURI($view_uri); } $form = id(new AphrontFormView()) @@ -58,6 +64,7 @@ final class PhabricatorBadgesAwardController id(new AphrontFormTokenizerControl()) ->setLabel(pht('Badge')) ->setName('badgePHIDs') + ->setError($e_badge) ->setDatasource( id(new PhabricatorBadgesDatasource()) ->setParameters( diff --git a/src/applications/badges/controller/PhabricatorBadgesEditRecipientsController.php b/src/applications/badges/controller/PhabricatorBadgesEditRecipientsController.php index ce437f1a5d..f25fd93542 100644 --- a/src/applications/badges/controller/PhabricatorBadgesEditRecipientsController.php +++ b/src/applications/badges/controller/PhabricatorBadgesEditRecipientsController.php @@ -7,6 +7,8 @@ final class PhabricatorBadgesEditRecipientsController $viewer = $request->getViewer(); $id = $request->getURIData('id'); $xactions = array(); + $errors = array(); + $e_recipient = true; $badge = id(new PhabricatorBadgesQuery()) ->setViewer($viewer) @@ -29,46 +31,41 @@ final class PhabricatorBadgesEditRecipientsController $add_recipients = $request->getArr('phids'); if ($add_recipients) { foreach ($add_recipients as $phid) { - $award_phids[] = $phid; + $award_phids[$phid] = $phid; } + } else { + $errors[] = pht('Recipient name is required.'); + $e_recipient = pht('Required'); } - $xactions[] = id(new PhabricatorBadgesTransaction()) - ->setTransactionType( - PhabricatorBadgesBadgeAwardTransaction::TRANSACTIONTYPE) - ->setNewValue($award_phids); + if (!$errors) { + $xactions[] = id(new PhabricatorBadgesTransaction()) + ->setTransactionType( + PhabricatorBadgesBadgeAwardTransaction::TRANSACTIONTYPE) + ->setNewValue($award_phids); - $editor = id(new PhabricatorBadgesEditor()) - ->setActor($viewer) - ->setContentSourceFromRequest($request) - ->setContinueOnNoEffect(true) - ->setContinueOnMissingFields(true) - ->applyTransactions($badge, $xactions); + $editor = id(new PhabricatorBadgesEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true) + ->applyTransactions($badge, $xactions); - return id(new AphrontRedirectResponse()) - ->setURI($view_uri); + return id(new AphrontRedirectResponse()) + ->setURI($view_uri); + } } - $can_edit = PhabricatorPolicyFilter::hasCapability( - $viewer, - $badge, - PhabricatorPolicyCapability::CAN_EDIT); - - $form_box = null; - $title = pht('Add Recipient'); - if ($can_edit) { - $header_name = pht('Edit Recipients'); - - $form = new AphrontFormView(); - $form - ->setUser($viewer) - ->setFullWidth(true) - ->appendControl( - id(new AphrontFormTokenizerControl()) - ->setName('phids') - ->setLabel(pht('Recipients')) - ->setDatasource(new PhabricatorPeopleDatasource())); - } + $form = new AphrontFormView(); + $form + ->setUser($viewer) + ->setFullWidth(true) + ->appendControl( + id(new AphrontFormTokenizerControl()) + ->setName('phids') + ->setLabel(pht('Recipients')) + ->setError($e_recipient) + ->setDatasource(new PhabricatorPeopleDatasource())); $dialog = id(new AphrontDialogView()) ->setUser($viewer) diff --git a/src/applications/base/PhabricatorApplication.php b/src/applications/base/PhabricatorApplication.php index 67294db263..4433306e78 100644 --- a/src/applications/base/PhabricatorApplication.php +++ b/src/applications/base/PhabricatorApplication.php @@ -141,7 +141,7 @@ abstract class PhabricatorApplication * * Users who have not yet set preferences see a default list of applications. * - * @param PhabricatorUser User viewing the pinned application list. + * @param PhabricatorUser $viewer User viewing the pinned application list. * @return bool True if this application should be pinned by default. */ public function isPinnedByDefault(PhabricatorUser $viewer) { @@ -330,9 +330,9 @@ abstract class PhabricatorApplication /** * Build items for the main menu. * - * @param PhabricatorUser The viewing user. - * @param AphrontController The current controller. May be null for special - * pages like 404, exception handlers, etc. + * @param PhabricatorUser $user The viewing user. + * @param AphrontController? $controller The current controller. May be null + * for special pages like 404, exception handlers, etc. * @return list List of menu items. * @task ui */ @@ -409,7 +409,7 @@ abstract class PhabricatorApplication * To check if an application is installed //and// available to a particular * viewer, user @{method:isClassInstalledForViewer}. * - * @param string Application class name. + * @param string $class Application class name. * @return bool True if the class is installed. * @task meta */ @@ -425,8 +425,8 @@ abstract class PhabricatorApplication * To check if an application is installed at all, use * @{method:isClassInstalled}. * - * @param string Application class name. - * @param PhabricatorUser Viewing user. + * @param string $class Application class name. + * @param PhabricatorUser $viewer Viewing user. * @return bool True if the class is installed for the viewer. * @task meta */ diff --git a/src/applications/base/controller/PhabricatorController.php b/src/applications/base/controller/PhabricatorController.php index d1b1fcfc8e..3f04e5a62a 100644 --- a/src/applications/base/controller/PhabricatorController.php +++ b/src/applications/base/controller/PhabricatorController.php @@ -627,25 +627,4 @@ abstract class PhabricatorController extends AphrontController { return $this->delegateToController($controller); } - -/* -( Deprecated )--------------------------------------------------------- */ - - - /** - * DEPRECATED. Use @{method:newPage}. - */ - public function buildStandardPageView() { - return $this->newPage(); - } - - - /** - * DEPRECATED. Use @{method:newPage}. - */ - public function buildStandardPageResponse($view, array $data) { - $page = $this->buildStandardPageView(); - $page->appendChild($view); - return $page->produceAphrontResponse(); - } - } diff --git a/src/applications/cache/PhabricatorCaches.php b/src/applications/cache/PhabricatorCaches.php index 9c22bde672..8e45741fe6 100644 --- a/src/applications/cache/PhabricatorCaches.php +++ b/src/applications/cache/PhabricatorCaches.php @@ -416,7 +416,7 @@ final class PhabricatorCaches extends Phobject { * * Data can later be inflated with @{method:inflateData}. * - * @param string String to attempt to deflate. + * @param string $value String to attempt to deflate. * @return string|null Deflated string, or null if it was not deflated. * @task compress */ @@ -447,7 +447,7 @@ final class PhabricatorCaches extends Phobject { /** * Inflate data previously deflated by @{method:maybeDeflateData}. * - * @param string Deflated data, from @{method:maybeDeflateData}. + * @param string $value Deflated data, from @{method:maybeDeflateData}. * @return string Original, uncompressed data. * @task compress */ diff --git a/src/applications/calendar/import/PhabricatorCalendarImportEngine.php b/src/applications/calendar/import/PhabricatorCalendarImportEngine.php index 626757c61d..33809f1516 100644 --- a/src/applications/calendar/import/PhabricatorCalendarImportEngine.php +++ b/src/applications/calendar/import/PhabricatorCalendarImportEngine.php @@ -235,14 +235,14 @@ abstract class PhabricatorCalendarImportEngine // We avoid disclosing email addresses to be consistent with the rest // of the product. $name = $attendee->getName(); - if (preg_match('/@/', $name)) { + if (phutil_nonempty_string($name) && preg_match('/@/', $name)) { $name = new PhutilEmailAddress($name); $name = $name->getDisplayName(); } // If we don't have a name or the name still looks like it's an // email address, give them a dummy placeholder name. - if (!strlen($name) || preg_match('/@/', $name)) { + if (!phutil_nonempty_string($name) || preg_match('/@/', $name)) { $name = pht('Private User %d', $private_index); $private_index++; } diff --git a/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php b/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php index 3a6e931515..d553177e4f 100644 --- a/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php +++ b/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php @@ -525,6 +525,10 @@ final class PhabricatorCalendarEventSearchEngine } } + /** + * @param PhabricatorSavedQuery $saved + * @return AphrontFormDateControlValue + */ private function getQueryDateFrom(PhabricatorSavedQuery $saved) { if ($this->calendarYear && $this->calendarMonth) { $viewer = $this->requireViewer(); diff --git a/src/applications/calendar/typeahead/PhabricatorCalendarInviteeViewerFunctionDatasource.php b/src/applications/calendar/typeahead/PhabricatorCalendarInviteeViewerFunctionDatasource.php index d653c9bdea..ef8cb76dc1 100644 --- a/src/applications/calendar/typeahead/PhabricatorCalendarInviteeViewerFunctionDatasource.php +++ b/src/applications/calendar/typeahead/PhabricatorCalendarInviteeViewerFunctionDatasource.php @@ -28,8 +28,12 @@ final class PhabricatorCalendarInviteeViewerFunctionDatasource ); } + protected function isFunctionWithLoginRequired($function) { + return true; + } + public function loadResults() { - if ($this->getViewer()->getPHID()) { + if ($this->getViewer()->isLoggedIn()) { $results = array($this->renderViewerFunctionToken()); } else { $results = array(); @@ -39,7 +43,7 @@ final class PhabricatorCalendarInviteeViewerFunctionDatasource } protected function canEvaluateFunction($function) { - if (!$this->getViewer()->getPHID()) { + if (!$this->getViewer()->isLoggedIn()) { return false; } diff --git a/src/applications/celerity/CelerityResourceMap.php b/src/applications/celerity/CelerityResourceMap.php index e53b656aaf..58036d9016 100644 --- a/src/applications/celerity/CelerityResourceMap.php +++ b/src/applications/celerity/CelerityResourceMap.php @@ -160,7 +160,7 @@ final class CelerityResourceMap extends Phobject { /** * Get the epoch timestamp of the last modification time of a symbol. * - * @param string Resource symbol to lookup. + * @param string $name Resource symbol to lookup. * @return int Epoch timestamp of last resource modification. */ public function getModifiedTimeForName($name) { @@ -186,7 +186,7 @@ final class CelerityResourceMap extends Phobject { * Return the absolute URI for the resource associated with a symbol. This * method is fairly low-level and ignores packaging. * - * @param string Resource symbol to lookup. + * @param string $symbol Resource symbol to lookup. * @return string|null Resource URI, or null if the symbol is unknown. */ public function getURIForSymbol($symbol) { @@ -199,7 +199,7 @@ final class CelerityResourceMap extends Phobject { * Return the absolute URI for the resource associated with a resource name. * This method is fairly low-level and ignores packaging. * - * @param string Resource name to lookup. + * @param string $name Resource name to lookup. * @return string|null Resource URI, or null if the name is unknown. */ public function getURIForName($name) { @@ -217,7 +217,7 @@ final class CelerityResourceMap extends Phobject { * Return the absolute URI for a resource, identified by hash. * This method is fairly low-level and ignores packaging. * - * @param string Resource hash to lookup. + * @param string $hash Resource hash to lookup. * @return string|null Resource URI, or null if the hash is unknown. */ private function getURIForHash($hash) { @@ -231,7 +231,7 @@ final class CelerityResourceMap extends Phobject { /** * Return the resource symbols required by a named resource. * - * @param string Resource name to lookup. + * @param string $name Resource name to lookup. * @return list|null List of required symbols, or null if the name * is unknown. */ @@ -247,7 +247,7 @@ final class CelerityResourceMap extends Phobject { /** * Return the resource name for a given symbol. * - * @param string Resource symbol to lookup. + * @param string $symbol Resource symbol to lookup. * @return string|null Resource name, or null if the symbol is unknown. */ public function getResourceNameForSymbol($symbol) { diff --git a/src/applications/celerity/CelerityResourceMapGenerator.php b/src/applications/celerity/CelerityResourceMapGenerator.php index 9280c9ecc2..d54aa572ed 100644 --- a/src/applications/celerity/CelerityResourceMapGenerator.php +++ b/src/applications/celerity/CelerityResourceMapGenerator.php @@ -146,7 +146,8 @@ EOFILE; * Find binary resources (like PNG and SWF) and return information about * them. * - * @param CelerityPhysicalResources Resource map to find binary resources for. + * @param CelerityPhysicalResources $resources Resource map to find binary + * resources for. * @return map> Resource information map. */ private function rebuildBinaryResources( @@ -170,8 +171,10 @@ EOFILE; /** * Find text resources (like JS and CSS) and return information about them. * - * @param CelerityPhysicalResources Resource map to find text resources for. - * @param CelerityResourceTransformer Configured resource transformer. + * @param CelerityPhysicalResources $resources Resource map to find text + * resources for. + * @param CelerityResourceTransformer $xformer Configured resource + * transformer. * @return map> Resource information map. */ private function rebuildTextResources( @@ -211,8 +214,8 @@ EOFILE; * Parse the `@provides` and `@requires` symbols out of a text resource, like * JS or CSS. * - * @param string Resource name. - * @param string Resource data. + * @param string $name Resource name. + * @param string $data Resource data. * @return pair|null> The `@provides` symbol and * the list of `@requires` symbols. If the resource is not part of the * dependency graph, both are null. @@ -254,8 +257,8 @@ EOFILE; * Check for dependency cycles in the resource graph. Raises an exception if * a cycle is detected. * - * @param map> Map of `@provides` symbols to their - * `@requires` symbols. + * @param map> $nodes Map of `@provides` symbols to + * their `@requires` symbols. * @return void */ private function detectGraphCycles(array $nodes) { @@ -278,9 +281,9 @@ EOFILE; /** * Build package specifications for a given resource source. * - * @param CelerityPhysicalResources Resource source to rebuild. - * @param map Map of `@provides` to hashes. - * @param map Map of hashes to resource names. + * @param CelerityPhysicalResources $resources Resource source to rebuild. + * @param map $symbol_map Map of `@provides` to hashes. + * @param map $reverse_map Map of hashes to resource names. * @return map> Package information maps. */ private function rebuildPackages( diff --git a/src/applications/celerity/CelerityResourceTransformer.php b/src/applications/celerity/CelerityResourceTransformer.php index 6d86a8806a..8c49a83df7 100644 --- a/src/applications/celerity/CelerityResourceTransformer.php +++ b/src/applications/celerity/CelerityResourceTransformer.php @@ -228,7 +228,8 @@ final class CelerityResourceTransformer extends Phobject { * small enough. Otherwise, this method will return `null` and we'll end up * using a normal URI instead. * - * @param string Resource name to attempt to generate a data URI for. + * @param string $resource_name Resource name to attempt to generate a data + * URI for. * @return string|null Data URI, or null if we declined to generate one. */ private function generateDataURI($resource_name) { diff --git a/src/applications/celerity/api.php b/src/applications/celerity/api.php index 4340b57df0..98802b4598 100644 --- a/src/applications/celerity/api.php +++ b/src/applications/celerity/api.php @@ -9,8 +9,9 @@ * * For more information, see @{article:Adding New CSS and JS}. * - * @param string Name of the celerity module to include. This is whatever you - * annotated as "@provides" in the file. + * @param string $symbol Name of the celerity module to include. This is + * whatever you annotated as "@provides" in the file. + * @param string? $source_name * @return void */ function require_celerity_resource($symbol, $source_name = 'phabricator') { @@ -40,7 +41,8 @@ function celerity_generate_unique_node_id() { /** * Get the versioned URI for a raw resource, like an image. * - * @param string Path to the raw image. + * @param string $resource Path to the raw image. + * @param string? $source * @return string Versioned path to the image, if one is available. */ function celerity_get_resource_uri($resource, $source = 'phabricator') { diff --git a/src/applications/celerity/controller/CelerityResourceController.php b/src/applications/celerity/controller/CelerityResourceController.php index 547c881df8..26d8d05701 100644 --- a/src/applications/celerity/controller/CelerityResourceController.php +++ b/src/applications/celerity/controller/CelerityResourceController.php @@ -59,6 +59,7 @@ abstract class CelerityResourceController extends PhabricatorController { } $cache = null; + $cache_key = null; $data = null; if ($is_cacheable && $is_locally_cacheable && !$dev_mode) { $cache = PhabricatorCaches::getImmutableCache(); @@ -98,7 +99,7 @@ abstract class CelerityResourceController extends PhabricatorController { $data = $xformer->transformResource($path, $data); } - if ($cache) { + if ($cache && $cache_key !== null) { $cache->setKey($cache_key, $data); } } @@ -193,7 +194,7 @@ abstract class CelerityResourceController extends PhabricatorController { * are cached, while other types of resources (which are large, and cheap * to process) are not. * - * @param string Resource type. + * @param string $type Resource type. * @return bool True to enable caching. */ private function isLocallyCacheableResourceType($type) { diff --git a/src/applications/celerity/management/CelerityManagementMapWorkflow.php b/src/applications/celerity/management/CelerityManagementMapWorkflow.php index e838de58f4..22fdebd686 100644 --- a/src/applications/celerity/management/CelerityManagementMapWorkflow.php +++ b/src/applications/celerity/management/CelerityManagementMapWorkflow.php @@ -32,7 +32,7 @@ final class CelerityManagementMapWorkflow /** * Rebuild the resource map for a resource source. * - * @param CelerityPhysicalResources Resource source to rebuild. + * @param $resources CelerityPhysicalResources Resource source to rebuild. * @return void */ private function rebuildResources(CelerityPhysicalResources $resources) { diff --git a/src/applications/conduit/controller/PhabricatorConduitAPIController.php b/src/applications/conduit/controller/PhabricatorConduitAPIController.php index 994f282f44..8eb19a2dc9 100644 --- a/src/applications/conduit/controller/PhabricatorConduitAPIController.php +++ b/src/applications/conduit/controller/PhabricatorConduitAPIController.php @@ -159,8 +159,9 @@ final class PhabricatorConduitAPIController /** * Authenticate the client making the request to a Phabricator user account. * - * @param ConduitAPIRequest Request being executed. - * @param dict Request metadata. + * @param ConduitAPIRequest $api_request Request being executed. + * @param dict $metadata Request metadata. + * @param wild $method * @return null|pair Null to indicate successful authentication, or * an error code and error message pair. */ diff --git a/src/applications/conduit/protocol/exception/ConduitException.php b/src/applications/conduit/protocol/exception/ConduitException.php index c30931de04..d604de65ef 100644 --- a/src/applications/conduit/protocol/exception/ConduitException.php +++ b/src/applications/conduit/protocol/exception/ConduitException.php @@ -12,7 +12,7 @@ class ConduitException extends Exception { * will be used instead. This is useful to provide specific information about * an exception (e.g., which values were wrong in an invalid request). * - * @param string Detailed error description. + * @param string $error_description Detailed error description. * @return this */ final public function setErrorDescription($error_description) { diff --git a/src/applications/config/controller/PhabricatorConfigConsoleController.php b/src/applications/config/controller/PhabricatorConfigConsoleController.php index e545019666..ad81e28be7 100644 --- a/src/applications/config/controller/PhabricatorConfigConsoleController.php +++ b/src/applications/config/controller/PhabricatorConfigConsoleController.php @@ -60,7 +60,7 @@ final class PhabricatorConfigConsoleController ->setBackground(PHUIObjectBoxView::WHITE_CONFIG) ->setObjectList($menu); - $versions = $this->newLibraryVersionTable($viewer); + $versions = $this->newLibraryVersionTable(); $binary_versions = $this->newBinaryVersionTable(); $launcher_view = id(new PHUILauncherView()) diff --git a/src/applications/config/custom/PhabricatorCustomLogoConfigType.php b/src/applications/config/custom/PhabricatorCustomLogoConfigType.php index cc20768119..6443864bc9 100644 --- a/src/applications/config/custom/PhabricatorCustomLogoConfigType.php +++ b/src/applications/config/custom/PhabricatorCustomLogoConfigType.php @@ -13,6 +13,45 @@ final class PhabricatorCustomLogoConfigType return idx($logo, 'wordmarkText'); } + /** + * Return the full URI of the Phorge logo + * @param PhabricatorUser $viewer Current viewer + * @return string Full URI of the Phorge logo + */ + public static function getLogoURI(PhabricatorUser $viewer) { + $logo_uri = null; + + $custom_header = self::getLogoImagePHID(); + if ($custom_header) { + $cache = PhabricatorCaches::getImmutableCache(); + $cache_key_logo = 'ui.custom-header.logo-phid.v3.'.$custom_header; + $logo_uri = $cache->getKey($cache_key_logo); + + if (!$logo_uri) { + // NOTE: If the file policy has been changed to be restrictive, we'll + // miss here and just show the default logo. The cache will fill later + // when someone who can see the file loads the page. This might be a + // little spooky, see T11982. + $files = id(new PhabricatorFileQuery()) + ->setViewer($viewer) + ->withPHIDs(array($custom_header)) + ->execute(); + $file = head($files); + if ($file) { + $logo_uri = $file->getViewURI(); + $cache->setKey($cache_key_logo, $logo_uri); + } + } + } + + if (!$logo_uri) { + $logo_uri = + celerity_get_resource_uri('/rsrc/image/logo/project-logo.png'); + } + + return $logo_uri; + } + public function validateOption(PhabricatorConfigOption $option, $value) { if (!is_array($value)) { throw new Exception( diff --git a/src/applications/config/issue/PhabricatorSetupIssue.php b/src/applications/config/issue/PhabricatorSetupIssue.php index cadedfc7da..3b6503113d 100644 --- a/src/applications/config/issue/PhabricatorSetupIssue.php +++ b/src/applications/config/issue/PhabricatorSetupIssue.php @@ -145,8 +145,8 @@ final class PhabricatorSetupIssue extends Phobject { * we alter `memory_limit` during startup, so if the original value is not * provided it will look like it is always set to `-1`. * - * @param string PHP configuration option to provide a value for. - * @param string Explicit value to show in the UI. + * @param string $php_config PHP configuration option to provide a value for. + * @param string $value Explicit value to show in the UI. * @return this */ public function addPHPConfigOriginalValue($php_config, $value) { diff --git a/src/applications/config/json/PhabricatorConfigJSON.php b/src/applications/config/json/PhabricatorConfigJSON.php index 0433e4a1dc..bbd84fc2bc 100644 --- a/src/applications/config/json/PhabricatorConfigJSON.php +++ b/src/applications/config/json/PhabricatorConfigJSON.php @@ -4,7 +4,8 @@ final class PhabricatorConfigJSON extends Phobject { /** * Properly format a JSON value. * - * @param wild Any value, but should be a raw value, not a string of JSON. + * @param wild $value Any value, but should be a raw value, not a string of + * JSON. * @return string */ public static function prettyPrintJSON($value) { diff --git a/src/applications/config/option/PhabricatorAWSConfigOptions.php b/src/applications/config/option/PhabricatorAWSConfigOptions.php index a7a3a1e2ea..d610e1641c 100644 --- a/src/applications/config/option/PhabricatorAWSConfigOptions.php +++ b/src/applications/config/option/PhabricatorAWSConfigOptions.php @@ -8,7 +8,7 @@ final class PhabricatorAWSConfigOptions } public function getDescription() { - return pht('Configure integration with AWS (EC2, SES, S3, etc).'); + return pht('Configure integration with AWS (EC2, SES, S3, etc.).'); } public function getIcon() { diff --git a/src/applications/config/option/PhabricatorApplicationConfigOptions.php b/src/applications/config/option/PhabricatorApplicationConfigOptions.php index 49cd9eb17e..e8aea20ba7 100644 --- a/src/applications/config/option/PhabricatorApplicationConfigOptions.php +++ b/src/applications/config/option/PhabricatorApplicationConfigOptions.php @@ -65,8 +65,8 @@ abstract class PhabricatorApplicationConfigOptions extends Phobject { * or other context. For example, this is used to show workspace IDs when * configuring `asana.workspace-id`. * - * @param PhabricatorConfigOption Option being rendered. - * @param AphrontRequest Active request. + * @param PhabricatorConfigOption $option Option being rendered. + * @param AphrontRequest $request Active request. * @return wild Additional contextual description * information. */ diff --git a/src/applications/conpherence/controller/ConpherenceUpdateController.php b/src/applications/conpherence/controller/ConpherenceUpdateController.php index 6d838e72bc..1b2ae3ab62 100644 --- a/src/applications/conpherence/controller/ConpherenceUpdateController.php +++ b/src/applications/conpherence/controller/ConpherenceUpdateController.php @@ -79,6 +79,30 @@ final class ConpherenceUpdateController $user, $conpherence, $message); + + $xaction_comment = PhabricatorTransactions::findOneByType( + $xactions, + PhabricatorTransactions::TYPE_COMMENT); + + $text_metadata = $request->getStr('text_metadata'); + if ($text_metadata) { + $text_metadata = phutil_json_decode($text_metadata); + $attached_file_phids = idx( + $text_metadata, + 'attachedFilePHIDs', + array()); + + if ($attached_file_phids) { + $metadata_object = array( + 'remarkup.control' => array( + 'attachedFilePHIDs' => $attached_file_phids, + ), + ); + + $xaction_comment->setMetadata($metadata_object); + } + } + $delete_draft = true; } else { $action = ConpherenceUpdateActions::LOAD; diff --git a/src/applications/conpherence/view/ConpherenceDurableColumnView.php b/src/applications/conpherence/view/ConpherenceDurableColumnView.php index 83c40aa32e..69778c71ab 100644 --- a/src/applications/conpherence/view/ConpherenceDurableColumnView.php +++ b/src/applications/conpherence/view/ConpherenceDurableColumnView.php @@ -472,23 +472,4 @@ final class ConpherenceDurableColumnView extends AphrontTagView { )); } - private function buildStatusText() { - return null; - } - - private function buildSendButton() { - $conpherence = $this->getSelectedConpherence(); - if (!$conpherence) { - return null; - } - - return javelin_tag( - 'button', - array( - 'class' => 'grey', - 'sigil' => 'conpherence-send-message', - ), - pht('Send')); - } - } diff --git a/src/applications/daemon/controller/PhabricatorDaemonController.php b/src/applications/daemon/controller/PhabricatorDaemonController.php index 05c850d399..bbf259b000 100644 --- a/src/applications/daemon/controller/PhabricatorDaemonController.php +++ b/src/applications/daemon/controller/PhabricatorDaemonController.php @@ -8,7 +8,7 @@ abstract class PhabricatorDaemonController } public function buildApplicationMenu() { - return $this->buildSideNavView(true)->getMenu(); + return $this->buildSideNavView()->getMenu(); } protected function buildSideNavView() { diff --git a/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelListController.php b/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelListController.php index 1eab148261..c9cefca707 100644 --- a/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelListController.php +++ b/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelListController.php @@ -48,23 +48,4 @@ final class PhabricatorDashboardPanelListController return $crumbs; } - protected function getNewUserBody() { - $create_button = id(new PHUIButtonView()) - ->setTag('a') - ->setText(pht('Create a Panel')) - ->setHref('/dashboard/panel/edit/') - ->setColor(PHUIButtonView::GREEN); - - $icon = $this->getApplication()->getIcon(); - $app_name = $this->getApplication()->getName(); - $view = id(new PHUIBigInfoView()) - ->setIcon($icon) - ->setTitle(pht('Welcome to %s', $app_name)) - ->setDescription( - pht('Build individual panels to display on your homepage dashboard.')) - ->addAction($create_button); - - return $view; - } - } diff --git a/src/applications/dashboard/editfield/PhabricatorDashboardQueryPanelQueryEditField.php b/src/applications/dashboard/editfield/PhabricatorDashboardQueryPanelQueryEditField.php index 58192c0eee..3be70dc952 100644 --- a/src/applications/dashboard/editfield/PhabricatorDashboardQueryPanelQueryEditField.php +++ b/src/applications/dashboard/editfield/PhabricatorDashboardQueryPanelQueryEditField.php @@ -38,7 +38,7 @@ final class PhabricatorDashboardQueryPanelQueryEditField } } - if (strlen($value) && !$seen) { + if (phutil_nonempty_string($value) && !$seen) { $name = pht('Custom Query ("%s")', $value); } else { $name = pht('(None)'); @@ -56,7 +56,7 @@ final class PhabricatorDashboardQueryPanelQueryEditField 'queryID' => $control_id, 'options' => $queries, 'value' => array( - 'key' => strlen($value) ? $value : null, + 'key' => phutil_nonempty_string($value) ? $value : null, 'name' => $name, ), )); diff --git a/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php b/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php index 3b9c3ad192..655d3be51b 100644 --- a/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php +++ b/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php @@ -427,7 +427,7 @@ final class PhabricatorDashboardPanelRenderingEngine extends Phobject { * panel. Generally, all of this stuff is ridiculous and we just want to * shut it down. * - * @param PhabricatorDashboardPanel Panel being rendered. + * @param PhabricatorDashboardPanel $panel Panel being rendered. * @return void */ private function detectRenderingCycle(PhabricatorDashboardPanel $panel) { diff --git a/src/applications/dashboard/engine/PhabricatorDashboardPortalProfileMenuEngine.php b/src/applications/dashboard/engine/PhabricatorDashboardPortalProfileMenuEngine.php index 9f8afc74dc..8265b306b1 100644 --- a/src/applications/dashboard/engine/PhabricatorDashboardPortalProfileMenuEngine.php +++ b/src/applications/dashboard/engine/PhabricatorDashboardPortalProfileMenuEngine.php @@ -41,7 +41,7 @@ final class PhabricatorDashboardPortalProfileMenuEngine pht('New Portal'), pht('Use "Edit Menu" to add menu items to this portal.')); } else { - return $this->newEmptyValue( + return $this->newNoContentView( pht('No Portal Content'), pht( 'None of the visible menu items in this portal can render any '. diff --git a/src/applications/dashboard/menuitem/PhabricatorDashboardPortalMenuItem.php b/src/applications/dashboard/menuitem/PhabricatorDashboardPortalMenuItem.php index 6d47a4c219..9c5aa8ddc2 100644 --- a/src/applications/dashboard/menuitem/PhabricatorDashboardPortalMenuItem.php +++ b/src/applications/dashboard/menuitem/PhabricatorDashboardPortalMenuItem.php @@ -31,7 +31,7 @@ final class PhabricatorDashboardPortalMenuItem PhabricatorProfileMenuItemConfiguration $config) { $name = $config->getMenuItemProperty('name'); - if (strlen($name)) { + if (phutil_nonempty_string($name)) { return $name; } diff --git a/src/applications/dashboard/query/PhabricatorDashboardPanelSearchEngine.php b/src/applications/dashboard/query/PhabricatorDashboardPanelSearchEngine.php index be59345aaa..c4b30652b9 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardPanelSearchEngine.php +++ b/src/applications/dashboard/query/PhabricatorDashboardPanelSearchEngine.php @@ -147,4 +147,22 @@ final class PhabricatorDashboardPanelSearchEngine return $result; } + protected function getNewUserBody() { + $create_button = id(new PHUIButtonView()) + ->setTag('a') + ->setText(pht('Create a Panel')) + ->setHref('/dashboard/panel/edit/') + ->setColor(PHUIButtonView::GREEN); + + $app_name = pht('Panels'); + $view = id(new PHUIBigInfoView()) + ->setIcon('fa-line-chart') + ->setTitle(pht('Welcome to %s', $app_name)) + ->setDescription( + pht('Build individual panels to display on your homepage dashboard.')) + ->addAction($create_button); + + return $view; + } + } diff --git a/src/applications/differential/constants/DifferentialReviewerStatus.php b/src/applications/differential/constants/DifferentialReviewerStatus.php index 203365efc6..c0b508f464 100644 --- a/src/applications/differential/constants/DifferentialReviewerStatus.php +++ b/src/applications/differential/constants/DifferentialReviewerStatus.php @@ -19,7 +19,7 @@ final class DifferentialReviewerStatus extends Phobject { * will attempt to update you to both "commented" and "accepted". We want * "accepted" to win, because it's the stronger of the two. * - * @param const Reviewer status constant. + * @param const $constant Reviewer status constant. * @return int Relative strength (higher is stronger). */ public static function getStatusStrength($constant) { diff --git a/src/applications/differential/customfield/DifferentialRepositoryField.php b/src/applications/differential/customfield/DifferentialRepositoryField.php index 1d403d160a..9cdc29fcb3 100644 --- a/src/applications/differential/customfield/DifferentialRepositoryField.php +++ b/src/applications/differential/customfield/DifferentialRepositoryField.php @@ -63,4 +63,16 @@ final class DifferentialRepositoryField $repository->getMonogram().' '.$repository->getName()); } + public function shouldAppearInListView() { + return true; + } + + public function renderOnListItem(PHUIObjectItemView $view) { + if ($this->getValue()) { + $handle = $this->getViewer()->renderHandle($this->getValue()); + $view->addByLine(pht('Repository: %s', $handle)); + } + } + + } diff --git a/src/applications/differential/parser/DifferentialChangesetParser.php b/src/applications/differential/parser/DifferentialChangesetParser.php index 5b39269bdd..3548e02258 100644 --- a/src/applications/differential/parser/DifferentialChangesetParser.php +++ b/src/applications/differential/parser/DifferentialChangesetParser.php @@ -240,12 +240,12 @@ final class DifferentialChangesetParser extends Phobject { * the left and right halves of the displayed diff to be correctly mapped to * storage changesets. * - * @param id The Differential Changeset ID that comments added to the right - * side of the visible diff should be attached to. - * @param bool If true, attach new comments to the right side of the storage - * changeset. Note that this may be false, if the left side of - * some storage changeset is being shown as the right side of - * a display diff. + * @param id $id The Differential Changeset ID that comments added to the + * right side of the visible diff should be attached to. + * @param bool $is_new If true, attach new comments to the right side of the + * storage changeset. Note that this may be false, if the left + * side of some storage changeset is being shown as the right + * side of a display diff. * @return this */ public function setRightSideCommentMapping($id, $is_new) { @@ -295,7 +295,8 @@ final class DifferentialChangesetParser extends Phobject { * By default, there is no render cache key and parsers do not use the cache. * This is appropriate for rarely-viewed changesets. * - * @param string Key for identifying this changeset in the render cache. + * @param string $key Key for identifying this changeset in the render + * cache. * @return this */ public function setRenderCacheKey($key) { @@ -1238,7 +1239,7 @@ final class DifferentialChangesetParser extends Phobject { * taking into consideration which halves of which changesets will actually * be shown. * - * @param PhabricatorInlineComment Comment to test for visibility. + * @param PhabricatorInlineComment $comment Comment to test for visibility. * @return bool True if the comment is visible on the rendered diff. */ private function isCommentVisibleOnRenderedDiff( @@ -1266,7 +1267,7 @@ final class DifferentialChangesetParser extends Phobject { * Note that the comment must appear somewhere on the rendered changeset, as * per isCommentVisibleOnRenderedDiff(). * - * @param PhabricatorInlineComment Comment to test for display + * @param PhabricatorInlineComment $comment Comment to test for display * location. * @return bool True for right, false for left. */ @@ -1298,8 +1299,8 @@ final class DifferentialChangesetParser extends Phobject { * list($start, $end, $mask) = $parsed; * $parser->render($start, $end, $mask); * - * @param string Range specification, indicating the range of the diff that - * should be rendered. + * @param string $spec Range specification, indicating the range of the diff + * that should be rendered. * @return tuple List of suitable for passing to * @{method:render}. */ diff --git a/src/applications/differential/parser/DifferentialLineAdjustmentMap.php b/src/applications/differential/parser/DifferentialLineAdjustmentMap.php index 2b4033c798..6c9bfad98b 100644 --- a/src/applications/differential/parser/DifferentialLineAdjustmentMap.php +++ b/src/applications/differential/parser/DifferentialLineAdjustmentMap.php @@ -58,8 +58,8 @@ final class DifferentialLineAdjustmentMap extends Phobject { /** * Map a line across a change, or a series of changes. * - * @param int Line to map - * @param bool True to map it as the end of a range. + * @param int $line Line to map + * @param bool $is_end True to map it as the end of a range. * @return wild Spooky magic. */ public function mapLine($line, $is_end) { diff --git a/src/applications/differential/query/DifferentialRevisionQuery.php b/src/applications/differential/query/DifferentialRevisionQuery.php index d6b249cac5..87ebafd97f 100644 --- a/src/applications/differential/query/DifferentialRevisionQuery.php +++ b/src/applications/differential/query/DifferentialRevisionQuery.php @@ -45,7 +45,7 @@ final class DifferentialRevisionQuery /** * Find revisions affecting one or more items in a list of paths. * - * @param list List of file paths. + * @param list $paths List of file paths. * @return this * @task config */ @@ -59,7 +59,7 @@ final class DifferentialRevisionQuery * this function will clear anything set by previous calls to * @{method:withAuthors}. * - * @param array List of PHIDs of authors + * @param array $author_phids List of PHIDs of authors * @return this * @task config */ @@ -72,7 +72,7 @@ final class DifferentialRevisionQuery * Filter results to revisions which CC one of the listed people. Calling this * function will clear anything set by previous calls to @{method:withCCs}. * - * @param array List of PHIDs of subscribers. + * @param array $cc_phids List of PHIDs of subscribers. * @return this * @task config */ @@ -86,7 +86,7 @@ final class DifferentialRevisionQuery * reviewers. Calling this function will clear anything set by previous calls * to @{method:withReviewers}. * - * @param array List of PHIDs of reviewers + * @param array $reviewer_phids List of PHIDs of reviewers * @return this * @task config */ @@ -124,7 +124,7 @@ final class DifferentialRevisionQuery * Calling this function will clear anything set by previous calls to * @{method:withCommitHashes}. * - * @param array List of pairs * @return this @@ -149,7 +149,7 @@ final class DifferentialRevisionQuery /** * Filter results to revisions on given branches. * - * @param list List of branch names. + * @param list $branches List of branch names. * @return this * @task config */ @@ -162,7 +162,7 @@ final class DifferentialRevisionQuery /** * Filter results to only return revisions whose ids are in the given set. * - * @param array List of revision ids + * @param array $ids List of revision ids * @return this * @task config */ @@ -175,7 +175,7 @@ final class DifferentialRevisionQuery /** * Filter results to only return revisions whose PHIDs are in the given set. * - * @param array List of revision PHIDs + * @param array $phids List of revision PHIDs * @return this * @task config */ @@ -189,7 +189,7 @@ final class DifferentialRevisionQuery * Given a set of users, filter results to return only revisions they are * responsible for (i.e., they are either authors or reviewers). * - * @param array List of user PHIDs. + * @param array $responsible_phids List of user PHIDs. * @return this * @task config */ @@ -221,7 +221,7 @@ final class DifferentialRevisionQuery * Set whether or not the query should load the active diff for each * revision. * - * @param bool True to load and attach diffs. + * @param bool $need_active_diffs True to load and attach diffs. * @return this * @task config */ @@ -235,7 +235,7 @@ final class DifferentialRevisionQuery * Set whether or not the query should load the associated commit PHIDs for * each revision. * - * @param bool True to load and attach diffs. + * @param bool $need_commit_phids True to load and attach diffs. * @return this * @task config */ @@ -249,7 +249,7 @@ final class DifferentialRevisionQuery * Set whether or not the query should load associated diff IDs for each * revision. * - * @param bool True to load and attach diff IDs. + * @param bool $need_diff_ids True to load and attach diff IDs. * @return this * @task config */ @@ -263,7 +263,7 @@ final class DifferentialRevisionQuery * Set whether or not the query should load associated commit hashes for each * revision. * - * @param bool True to load and attach commit hashes. + * @param bool $need_hashes True to load and attach commit hashes. * @return this * @task config */ @@ -276,7 +276,7 @@ final class DifferentialRevisionQuery /** * Set whether or not the query should load associated reviewers. * - * @param bool True to load and attach reviewers. + * @param bool $need_reviewers True to load and attach reviewers. * @return this * @task config */ @@ -291,7 +291,7 @@ final class DifferentialRevisionQuery * reviewer. In particular, they have authority to act on behalf of projects * they are a member of. * - * @param bool True to load and attach authority. + * @param bool $need_reviewer_authority True to load and attach authority. * @return this * @task config */ diff --git a/src/applications/differential/query/DifferentialRevisionSearchEngine.php b/src/applications/differential/query/DifferentialRevisionSearchEngine.php index b69b764bb8..9e18ba8e24 100644 --- a/src/applications/differential/query/DifferentialRevisionSearchEngine.php +++ b/src/applications/differential/query/DifferentialRevisionSearchEngine.php @@ -199,6 +199,10 @@ final class DifferentialRevisionSearchEngine $unlanded = $this->loadUnlandedDependencies($revisions); + $custom_field_lists = $this->loadCustomFields( + $revisions, + PhabricatorCustomField::ROLE_LIST); + $views = array(); if ($bucket) { $bucket->setViewer($viewer); @@ -231,6 +235,7 @@ final class DifferentialRevisionSearchEngine foreach ($views as $view) { $view->setUnlandedDependencies($unlanded); + $view->setCustomFieldLists($custom_field_lists); } if (count($views) == 1) { diff --git a/src/applications/differential/render/DifferentialChangesetHTMLRenderer.php b/src/applications/differential/render/DifferentialChangesetHTMLRenderer.php index 7612f9e876..f6832c4fc9 100644 --- a/src/applications/differential/render/DifferentialChangesetHTMLRenderer.php +++ b/src/applications/differential/render/DifferentialChangesetHTMLRenderer.php @@ -491,9 +491,10 @@ abstract class DifferentialChangesetHTMLRenderer /** * Build links which users can click to show more context in a changeset. * - * @param int Beginning of the line range to build links for. - * @param int Length of the line range to build links for. - * @param int Total number of lines in the changeset. + * @param int $top Beginning of the line range to build links for. + * @param int $len Length of the line range to build links for. + * @param int $changeset_length Total number of lines in the changeset. + * @param bool? $is_blocks * @return markup Rendered links. */ protected function renderShowContextLinks( @@ -580,9 +581,9 @@ abstract class DifferentialChangesetHTMLRenderer * * See @{method:renderShowContextLinks}. * - * @param bool Does this link show all context when clicked? - * @param string Range specification for lines to show. - * @param string Text of the link. + * @param bool $is_all Does this link show all context when clicked? + * @param string $range Range specification for lines to show. + * @param string $text Text of the link. * @return markup Rendered link. */ private function renderShowContextLink($is_all, $range, $text) { diff --git a/src/applications/differential/render/DifferentialChangesetRenderer.php b/src/applications/differential/render/DifferentialChangesetRenderer.php index 711d7d574b..676a6bcf05 100644 --- a/src/applications/differential/render/DifferentialChangesetRenderer.php +++ b/src/applications/differential/render/DifferentialChangesetRenderer.php @@ -442,8 +442,8 @@ abstract class DifferentialChangesetRenderer extends Phobject { * when a file is not changed. * - `"none"`: Don't show the link (e.g., text not available). * - * @param string Message explaining why the diff is hidden. - * @param string|null Force mode, see above. + * @param string $message Message explaining why the diff is hidden. + * @param string|null $force Force mode, see above. * @return string Shield markup. */ abstract public function renderShield($message, $force = 'default'); diff --git a/src/applications/differential/storage/DifferentialChangeset.php b/src/applications/differential/storage/DifferentialChangeset.php index d92b27e574..7b20c22a34 100644 --- a/src/applications/differential/storage/DifferentialChangeset.php +++ b/src/applications/differential/storage/DifferentialChangeset.php @@ -213,7 +213,7 @@ final class DifferentialChangeset * Test if this changeset and some other changeset put the affected file in * the same state. * - * @param DifferentialChangeset Changeset to compare against. + * @param DifferentialChangeset $other Changeset to compare against. * @return bool True if the two changesets have the same effect. */ public function hasSameEffectAs(DifferentialChangeset $other) { diff --git a/src/applications/differential/typeahead/DifferentialResponsibleViewerFunctionDatasource.php b/src/applications/differential/typeahead/DifferentialResponsibleViewerFunctionDatasource.php index 05ae39ec2e..4d735b0c94 100644 --- a/src/applications/differential/typeahead/DifferentialResponsibleViewerFunctionDatasource.php +++ b/src/applications/differential/typeahead/DifferentialResponsibleViewerFunctionDatasource.php @@ -28,8 +28,12 @@ final class DifferentialResponsibleViewerFunctionDatasource ); } + protected function isFunctionWithLoginRequired($function) { + return true; + } + public function loadResults() { - if ($this->getViewer()->getPHID()) { + if ($this->getViewer()->isLoggedIn()) { $results = array($this->renderViewerFunctionToken()); } else { $results = array(); @@ -39,7 +43,7 @@ final class DifferentialResponsibleViewerFunctionDatasource } protected function canEvaluateFunction($function) { - if (!$this->getViewer()->getPHID()) { + if (!$this->getViewer()->isLoggedIn()) { return false; } diff --git a/src/applications/differential/view/DifferentialChangesetDetailView.php b/src/applications/differential/view/DifferentialChangesetDetailView.php index 9d04d081ac..8dfe71f2d3 100644 --- a/src/applications/differential/view/DifferentialChangesetDetailView.php +++ b/src/applications/differential/view/DifferentialChangesetDetailView.php @@ -233,6 +233,22 @@ final class DifferentialChangesetDetailView extends AphrontView { $show_directory_uri = phutil_string_cast($show_directory_uri); } + Javelin::initBehavior('phabricator-clipboard-copy'); + $path_copy_button = id(new PHUIButtonView()) + ->setTag('a') + ->setColor(PHUIButtonView::GREY) + ->setIcon('fa-clipboard') + ->setHref('#') + ->addSigil('clipboard-copy') + ->addSigil('has-tooltip') + ->setMetadata( + array( + 'tip' => pht('Copy file path'), + 'text' => $display_filename, + 'successMessage' => pht('File path copied.'), + 'errorMessage' => pht('Copy of file path failed.'), + )); + return javelin_tag( 'div', array( @@ -278,14 +294,23 @@ final class DifferentialChangesetDetailView extends AphrontView { 'sigil' => 'changeset-header', ), array( - $icon, + javelin_tag( + 'span', + array( + 'class' => 'differential-changeset-path-copy-button', + 'meta' => array( + 'selectID' => $display_filename, + 'once' => true, + ), + ), + $path_copy_button), javelin_tag( 'span', array( 'class' => 'differential-changeset-path-name', 'sigil' => 'changeset-header-path-name', ), - $display_filename), + $display_filename), )), javelin_tag( 'div', diff --git a/src/applications/differential/view/DifferentialRevisionListView.php b/src/applications/differential/view/DifferentialRevisionListView.php index ab0abcc386..018f640c6c 100644 --- a/src/applications/differential/view/DifferentialRevisionListView.php +++ b/src/applications/differential/view/DifferentialRevisionListView.php @@ -11,6 +11,7 @@ final class DifferentialRevisionListView extends AphrontView { private $noBox; private $background = null; private $unlandedDependencies = array(); + private $customFieldLists = array(); public function setUnlandedDependencies(array $unlanded_dependencies) { $this->unlandedDependencies = $unlanded_dependencies; @@ -47,6 +48,11 @@ final class DifferentialRevisionListView extends AphrontView { return $this; } + public function setCustomFieldLists(array $lists) { + $this->customFieldLists = $lists; + return $this; + } + public function render() { $viewer = $this->getViewer(); @@ -181,6 +187,12 @@ final class DifferentialRevisionListView extends AphrontView { "{$icon} {$color}", $revision->getStatusDisplayName()); + $field_list = idx($this->customFieldLists, $revision->getPHID()); + if ($field_list) { + $field_list + ->addFieldsToListViewItem($revision, $viewer, $item); + } + $list->addItem($item); } diff --git a/src/applications/diffusion/conduit/DiffusionQueryPathsConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionQueryPathsConduitAPIMethod.php index 98cc006419..e511492fc4 100644 --- a/src/applications/diffusion/conduit/DiffusionQueryPathsConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionQueryPathsConduitAPIMethod.php @@ -27,7 +27,7 @@ final class DiffusionQueryPathsConduitAPIMethod protected function getResult(ConduitAPIRequest $request) { $results = parent::getResult($request); - $offset = $request->getValue('offset'); + $offset = $request->getValue('offset', 0); return array_slice($results, $offset); } diff --git a/src/applications/diffusion/conduit/DiffusionSearchQueryConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionSearchQueryConduitAPIMethod.php index ba4c824061..5f40aa06bb 100644 --- a/src/applications/diffusion/conduit/DiffusionSearchQueryConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionSearchQueryConduitAPIMethod.php @@ -40,7 +40,7 @@ final class DiffusionSearchQueryConduitAPIMethod throw $ex; } - $offset = $request->getValue('offset'); + $offset = $request->getValue('offset', 0); $results = array_slice($results, $offset); return $results; diff --git a/src/applications/diffusion/controller/DiffusionCommitController.php b/src/applications/diffusion/controller/DiffusionCommitController.php index ce3ef41c9e..1e2ec10847 100644 --- a/src/applications/diffusion/controller/DiffusionCommitController.php +++ b/src/applications/diffusion/controller/DiffusionCommitController.php @@ -624,6 +624,13 @@ final class DiffusionCommitController extends DiffusionController { $author_view = $commit->newCommitAuthorView($viewer); if ($author_view) { $author_date = $data->getAuthorEpoch(); + + // Add support to Subversion commits that only have one date, + // since in SVN you cannot prepare local commits to push them later. + if ($author_date === null) { + $author_date = $commit->getEpoch(); + } + $author_date = phabricator_datetime($author_date, $viewer); $provenance_list->addItem( diff --git a/src/applications/diffusion/data/DiffusionBrowseResultSet.php b/src/applications/diffusion/data/DiffusionBrowseResultSet.php index 22d9e08251..f112ab49ed 100644 --- a/src/applications/diffusion/data/DiffusionBrowseResultSet.php +++ b/src/applications/diffusion/data/DiffusionBrowseResultSet.php @@ -123,7 +123,7 @@ final class DiffusionBrowseResultSet extends Phobject { * When a directory contains several README files, this function scores them * so the caller can select a preferred file. See @{method:getReadmePath}. * - * @param string Local README path, like "README.txt". + * @param string $path Local README path, like "README.txt". * @return int Priority score, with higher being more preferred. */ public static function getReadmePriority($path) { diff --git a/src/applications/diffusion/data/DiffusionGitBranch.php b/src/applications/diffusion/data/DiffusionGitBranch.php index 7fe8f5c6c0..75c17b67c1 100644 --- a/src/applications/diffusion/data/DiffusionGitBranch.php +++ b/src/applications/diffusion/data/DiffusionGitBranch.php @@ -19,8 +19,9 @@ final class DiffusionGitBranch extends Phobject { * 'master' => '99a9c082f9a1b68c7264e26b9e552484a5ae5f25', * ); * - * @param string stdout of git branch command. - * @param string Filter branches to those on a specific remote. + * @param string $stdout stdout of git branch command. + * @param string? $only_this_remote Filter branches to those on a specific + * remote. * @return map Map of 'branch' or 'remote/branch' to hash at HEAD. */ public static function parseRemoteBranchOutput( diff --git a/src/applications/diffusion/protocol/DiffusionMercurialCommandEngine.php b/src/applications/diffusion/protocol/DiffusionMercurialCommandEngine.php index dc898f81d4..ee70ce217d 100644 --- a/src/applications/diffusion/protocol/DiffusionMercurialCommandEngine.php +++ b/src/applications/diffusion/protocol/DiffusionMercurialCommandEngine.php @@ -58,7 +58,7 @@ final class DiffusionMercurialCommandEngine * Sanitize output of an `hg` command invoked with the `--debug` flag to make * it usable. * - * @param string Output from `hg --debug ...` + * @param string $stdout Output from `hg --debug ...` * @return string Usable output. */ public static function filterMercurialDebugOutput($stdout) { diff --git a/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php b/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php index dfc86e5f56..0b2f40c2d6 100644 --- a/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php +++ b/src/applications/diffusion/protocol/DiffusionRepositoryClusterEngine.php @@ -586,7 +586,7 @@ final class DiffusionRepositoryClusterEngine extends Phobject { 'for %s seconds(s). This repository will be frozen.', $this->clusterWriteOwner, $device->getName(), - $this->getDisplayName(), + $repository->getDisplayName(), new PhutilNumber($duration))); } diff --git a/src/applications/diffusion/query/DiffusionCommitQuery.php b/src/applications/diffusion/query/DiffusionCommitQuery.php index e1bec46441..18b2dbacde 100644 --- a/src/applications/diffusion/query/DiffusionCommitQuery.php +++ b/src/applications/diffusion/query/DiffusionCommitQuery.php @@ -252,7 +252,7 @@ final class DiffusionCommitQuery $table->getTableName()); } - if (!$subqueries) { + if (!$subqueries && $empty_exception) { throw $empty_exception; } diff --git a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelCommitQuery.php b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelCommitQuery.php index 8b9233f128..0b5df04a41 100644 --- a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelCommitQuery.php +++ b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelCommitQuery.php @@ -126,11 +126,11 @@ final class DiffusionLowLevelCommitQuery $head = $parts[6]; $tail = $parts[7]; - if (strlen($head) && strlen($tail)) { + if (phutil_nonempty_string($head) && phutil_nonempty_string($tail)) { $body = $head."\n\n".$tail; - } else if (strlen($head)) { + } else if (phutil_nonempty_string($head)) { $body = $head; - } else if (strlen($tail)) { + } else if (phutil_nonempty_string($tail)) { $body = $tail; } else { $body = ''; diff --git a/src/applications/diffusion/query/pathid/DiffusionPathIDQuery.php b/src/applications/diffusion/query/pathid/DiffusionPathIDQuery.php index d994410034..69bb89502a 100644 --- a/src/applications/diffusion/query/pathid/DiffusionPathIDQuery.php +++ b/src/applications/diffusion/query/pathid/DiffusionPathIDQuery.php @@ -42,7 +42,7 @@ final class DiffusionPathIDQuery extends Phobject { /** * Convert a path to the canonical, absolute representation used by Diffusion. * - * @param string Some repository path. + * @param string $path Some repository path. * @return string Canonicalized Diffusion path. * @task pathutil */ @@ -62,7 +62,7 @@ final class DiffusionPathIDQuery extends Phobject { * Return the canonical parent directory for a path. Note, returns "/" when * passed "/". * - * @param string Some repository path. + * @param string $path Some repository path. * @return string That path's canonical parent directory. * @task pathutil */ @@ -80,7 +80,7 @@ final class DiffusionPathIDQuery extends Phobject { * Generate a list of parents for a repository path. The path itself is * included. * - * @param string Some repository path. + * @param string $path Some repository path. * @return list List of canonical paths between the path and the root. * @task pathutil */ diff --git a/src/applications/diffusion/query/rawdiff/DiffusionGitRawDiffQuery.php b/src/applications/diffusion/query/rawdiff/DiffusionGitRawDiffQuery.php index 9c894437ef..b05e96e592 100644 --- a/src/applications/diffusion/query/rawdiff/DiffusionGitRawDiffQuery.php +++ b/src/applications/diffusion/query/rawdiff/DiffusionGitRawDiffQuery.php @@ -35,7 +35,7 @@ final class DiffusionGitRawDiffQuery extends DiffusionRawDiffQuery { } $path = $drequest->getPath(); - if (!strlen($path)) { + if (!phutil_nonempty_string($path)) { $path = '.'; } diff --git a/src/applications/diffusion/request/DiffusionRequest.php b/src/applications/diffusion/request/DiffusionRequest.php index c301b89b28..d9bd30afea 100644 --- a/src/applications/diffusion/request/DiffusionRequest.php +++ b/src/applications/diffusion/request/DiffusionRequest.php @@ -56,7 +56,7 @@ abstract class DiffusionRequest extends Phobject { * - `commit` Optional, commit identifier. * - `line` Optional, line range. * - * @param map See documentation. + * @param map $data See documentation. * @return DiffusionRequest New request object. * @task new */ @@ -133,8 +133,9 @@ abstract class DiffusionRequest extends Phobject { /** * Internal. Use @{method:newFromDictionary}, not this method. * - * @param string Repository identifier. - * @param PhabricatorUser Viewing user. + * @param string $identifier Repository identifier. + * @param PhabricatorUser $viewer Viewing user. + * @param bool? $need_edit * @return DiffusionRequest New request object. * @task new */ @@ -170,7 +171,7 @@ abstract class DiffusionRequest extends Phobject { /** * Internal. Use @{method:newFromDictionary}, not this method. * - * @param PhabricatorRepository Repository object. + * @param PhabricatorRepository $repository Repository object. * @return DiffusionRequest New request object. * @task new */ @@ -201,7 +202,7 @@ abstract class DiffusionRequest extends Phobject { /** * Internal. Use @{method:newFromDictionary}, not this method. * - * @param map Map of parsed data. + * @param map $data Map of parsed data. * @return void * @task new */ @@ -294,7 +295,7 @@ abstract class DiffusionRequest extends Phobject { /** * Modify the request to move the symbolic commit elsewhere. * - * @param string New symbolic commit. + * @param string $symbol New symbolic commit. * @return this */ public function updateSymbolicCommit($symbol) { @@ -464,8 +465,8 @@ abstract class DiffusionRequest extends Phobject { * * Parse the request URI into components. * - * @param string URI blob. - * @param bool True if this VCS supports branches. + * @param string $blob URI blob. + * @param bool $supports_branches True if this VCS supports branches. * @return map Parsed URI. * * @task uri @@ -535,7 +536,7 @@ abstract class DiffusionRequest extends Phobject { /** * Check that the working copy of the repository is present and readable. * - * @param string Path to the working copy. + * @param string $path Path to the working copy. */ protected function validateWorkingCopy($path) { if (!is_readable(dirname($path))) { diff --git a/src/applications/diffusion/view/DiffusionReadmeView.php b/src/applications/diffusion/view/DiffusionReadmeView.php index 2f65f5730f..72b7a94f5b 100644 --- a/src/applications/diffusion/view/DiffusionReadmeView.php +++ b/src/applications/diffusion/view/DiffusionReadmeView.php @@ -26,7 +26,7 @@ final class DiffusionReadmeView extends DiffusionView { /** * Get the markup language a README should be interpreted as. * - * @param string Local README path, like "README.txt". + * @param string $path Local README path, like "README.txt". * @return string Best markup interpreter (like "remarkup") for this file. */ private function getReadmeLanguage($path) { diff --git a/src/applications/diviner/controller/DivinerAtomController.php b/src/applications/diviner/controller/DivinerAtomController.php index 60eba689e4..100fe0f354 100644 --- a/src/applications/diviner/controller/DivinerAtomController.php +++ b/src/applications/diviner/controller/DivinerAtomController.php @@ -437,7 +437,7 @@ final class DivinerAtomController extends DivinerController { $tasks = $symbol->getAtom()->getDocblockMetaValue('task'); if (!is_array($tasks)) { - if (strlen($tasks)) { + if (phutil_nonempty_string($tasks)) { $tasks = array($tasks); } else { $tasks = array(); @@ -446,7 +446,11 @@ final class DivinerAtomController extends DivinerController { if ($tasks) { foreach ($tasks as $task) { - list($name, $title) = explode(' ', $task, 2); + if (strpos($task, ' ') !== false) { + list($name, $title) = explode(' ', $task, 2); + } else { + list($name, $title) = array($task, ''); + } $name = trim($name); $title = trim($title); diff --git a/src/applications/diviner/controller/DivinerFindController.php b/src/applications/diviner/controller/DivinerFindController.php index 2e3b121706..d6ddd420fd 100644 --- a/src/applications/diviner/controller/DivinerFindController.php +++ b/src/applications/diviner/controller/DivinerFindController.php @@ -37,7 +37,7 @@ final class DivinerFindController extends DivinerController { } $type = $request->getStr('type'); - if (strlen($type)) { + if (phutil_nonempty_string($type)) { $query->withTypes(array($type)); } diff --git a/src/applications/diviner/query/DivinerAtomQuery.php b/src/applications/diviner/query/DivinerAtomQuery.php index 1ca7aad984..278b0a4234 100644 --- a/src/applications/diviner/query/DivinerAtomQuery.php +++ b/src/applications/diviner/query/DivinerAtomQuery.php @@ -93,7 +93,7 @@ final class DivinerAtomQuery extends PhabricatorCursorPagedPolicyAwareQuery { * have been generated incorrectly by accident. In these cases, we can * restore the original data. * - * @param bool + * @param bool $ghosts * @return this */ public function withGhosts($ghosts) { @@ -437,9 +437,10 @@ final class DivinerAtomQuery extends PhabricatorCursorPagedPolicyAwareQuery { * children. When recursing, also walk up the tree and collect children of * atoms they extend. * - * @param list List of symbols to collect child hashes of. - * @param bool True to collect children of extended atoms, - * as well. + * @param list $symbols List of symbols to collect child + * hashes of. + * @param bool $recurse_up True to collect children of + * extended atoms, as well. * @return map Hashes of atoms' children. */ private function getAllChildHashes(array $symbols, $recurse_up) { @@ -469,9 +470,12 @@ final class DivinerAtomQuery extends PhabricatorCursorPagedPolicyAwareQuery { * Attach child atoms to existing atoms. In recursive mode, also attach child * atoms to atoms that these atoms extend. * - * @param list List of symbols to attach children to. - * @param map Map of symbols, keyed by node hash. - * @param bool True to attach children to extended atoms, as well. + * @param list $symbols List of symbols to attach children + * to. + * @param map $children Map of symbols, keyed by + * node hash. + * @param bool $recurse_up True to attach children to extended atoms, as + * well. * @return void */ private function attachAllChildren( diff --git a/src/applications/doorkeeper/engine/DoorkeeperFeedStoryPublisher.php b/src/applications/doorkeeper/engine/DoorkeeperFeedStoryPublisher.php index 46ca3f3ac9..ddd45ce802 100644 --- a/src/applications/doorkeeper/engine/DoorkeeperFeedStoryPublisher.php +++ b/src/applications/doorkeeper/engine/DoorkeeperFeedStoryPublisher.php @@ -31,7 +31,8 @@ abstract class DoorkeeperFeedStoryPublisher extends Phobject { * JIRA issues which can have several linked revisions), it's generally * more useful not to assume context. * - * @param bool True to assume object context when rendering. + * @param bool $render_with_implied_context True to assume object context + * when rendering. * @return this * @task config */ diff --git a/src/applications/doorkeeper/storage/DoorkeeperExternalObject.php b/src/applications/doorkeeper/storage/DoorkeeperExternalObject.php index a4ce41adac..a6d4704a90 100644 --- a/src/applications/doorkeeper/storage/DoorkeeperExternalObject.php +++ b/src/applications/doorkeeper/storage/DoorkeeperExternalObject.php @@ -60,11 +60,7 @@ final class DoorkeeperExternalObject extends DoorkeeperDAO } public function getObjectKey() { - $key = parent::getObjectKey(); - if ($key === null) { - $key = $this->getRef()->getObjectKey(); - } - return $key; + return $this->getRef()->getObjectKey(); } public function getRef() { diff --git a/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php b/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php index 27ba6e623f..bfa039d39f 100644 --- a/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php +++ b/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php @@ -63,9 +63,10 @@ abstract class DrydockBlueprintImplementation extends Phobject { * This method should not acquire locks or expect anything to be locked. This * is a coarse compatibility check between a lease and a resource. * - * @param DrydockBlueprint Concrete blueprint to allocate for. - * @param DrydockResource Candidate resource to allocate the lease on. - * @param DrydockLease Pending lease that wants to allocate here. + * @param DrydockBlueprint $blueprint Concrete blueprint to allocate for. + * @param DrydockResource $resource Candidate resource to allocate the lease + * on. + * @param DrydockLease $lease Pending lease that wants to allocate here. * @return bool True if the resource and lease are compatible. * @task lease */ @@ -81,9 +82,9 @@ abstract class DrydockBlueprintImplementation extends Phobject { * * If acquisition fails, throw an exception. * - * @param DrydockBlueprint Blueprint which built the resource. - * @param DrydockResource Resource to acquire a lease on. - * @param DrydockLease Requested lease. + * @param DrydockBlueprint $blueprint Blueprint which built the resource. + * @param DrydockResource $resource Resource to acquire a lease on. + * @param DrydockLease $lease Requested lease. * @return void * @task lease */ @@ -111,9 +112,9 @@ abstract class DrydockBlueprintImplementation extends Phobject { * This callback is primarily useful for automatically releasing resources * once all leases are released. * - * @param DrydockBlueprint Blueprint which built the resource. - * @param DrydockResource Resource a lease was released on. - * @param DrydockLease Recently released lease. + * @param DrydockBlueprint $blueprint Blueprint which built the resource. + * @param DrydockResource $resource Resource a lease was released on. + * @param DrydockLease $lease Recently released lease. * @return void * @task lease */ @@ -128,9 +129,9 @@ abstract class DrydockBlueprintImplementation extends Phobject { * * If a lease creates temporary state while held, destroy it here. * - * @param DrydockBlueprint Blueprint which built the resource. - * @param DrydockResource Resource the lease is acquired on. - * @param DrydockLease The lease being destroyed. + * @param DrydockBlueprint $blueprint Blueprint which built the resource. + * @param DrydockResource $resource Resource the lease is acquired on. + * @param DrydockLease $lease The lease being destroyed. * @return void * @task lease */ @@ -156,11 +157,11 @@ abstract class DrydockBlueprintImplementation extends Phobject { * has a flexible pool of expensive resources and you want to pack leases * onto them as tightly as possible. * - * @param DrydockBlueprint The blueprint for an existing resource being - * acquired. - * @param DrydockResource The resource being acquired, which we may want to - * build a supplemental resource for. - * @param DrydockLease The current lease performing acquisition. + * @param DrydockBlueprint $blueprint The blueprint for an existing resource + * being acquired. + * @param DrydockResource $resource The resource being acquired, which we may + * want to build a supplemental resource for. + * @param DrydockLease $lease The current lease performing acquisition. * @return bool True to prefer allocating a supplemental resource. * * @task lease @@ -190,7 +191,7 @@ abstract class DrydockBlueprintImplementation extends Phobject { * really exists, only if some blueprint may conceivably exist which could * plausibly be able to build a suitable resource. * - * @param DrydockLease Requested lease. + * @param DrydockLease $lease Requested lease. * @return bool True if some concrete blueprint of this implementation's * type might ever be able to build a resource for the lease. * @task resource @@ -211,9 +212,9 @@ abstract class DrydockBlueprintImplementation extends Phobject { * if the blueprint as configured may, at some time, be able to build a * suitable resource. * - * @param DrydockBlueprint Blueprint which may be asked to allocate a - * resource. - * @param DrydockLease Requested lease. + * @param DrydockBlueprint $blueprint Blueprint which may be asked to + * allocate a resource. + * @param DrydockLease $lease Requested lease. * @return bool True if this blueprint can eventually build a suitable * resource for the lease, as currently configured. * @task resource @@ -240,9 +241,9 @@ abstract class DrydockBlueprintImplementation extends Phobject { * eaten up free capacity by the time it actually tries to build a resource. * This is normal and the allocator will recover from it. * - * @param DrydockBlueprint The blueprint which may be asked to allocate a - * resource. - * @param DrydockLease Requested lease. + * @param DrydockBlueprint $blueprint The blueprint which may be asked to + * allocate a resource. + * @param DrydockLease $lease Requested lease. * @return bool True if this blueprint appears likely to be able to allocate * a suitable resource. * @task resource @@ -260,8 +261,9 @@ abstract class DrydockBlueprintImplementation extends Phobject { * called. Blueprints are entirely responsible for any lock handling they * need to perform. * - * @param DrydockBlueprint The blueprint which should allocate a resource. - * @param DrydockLease Requested lease. + * @param DrydockBlueprint $blueprint The blueprint which should allocate a + * resource. + * @param DrydockLease $lease Requested lease. * @return DrydockResource Allocated resource. * @task resource */ @@ -287,8 +289,8 @@ abstract class DrydockBlueprintImplementation extends Phobject { * here. For example, you might shut down a virtual host or destroy a working * copy on disk. * - * @param DrydockBlueprint Blueprint which built the resource. - * @param DrydockResource Resource being destroyed. + * @param DrydockBlueprint $blueprint Blueprint which built the resource. + * @param DrydockResource $resource Resource being destroyed. * @return void * @task resource */ @@ -300,8 +302,8 @@ abstract class DrydockBlueprintImplementation extends Phobject { /** * Get a human readable name for a resource. * - * @param DrydockBlueprint Blueprint which built the resource. - * @param DrydockResource Resource to get the name of. + * @param DrydockBlueprint $blueprint Blueprint which built the resource. + * @param DrydockResource $resource Resource to get the name of. * @return string Human-readable resource name. * @task resource */ @@ -338,7 +340,7 @@ abstract class DrydockBlueprintImplementation extends Phobject { * build a resource which can satisfy the lease. They may not be able to * build that resource right now. * - * @param DrydockLease Requested lease. + * @param DrydockLease $lease Requested lease. * @return list List of qualifying blueprint * implementations. */ @@ -429,7 +431,7 @@ abstract class DrydockBlueprintImplementation extends Phobject { /** * Get the effective concurrent resource limit for this blueprint. * - * @param DrydockBlueprint Blueprint to get the limit for. + * @param DrydockBlueprint $blueprint Blueprint to get the limit for. * @return int|null Limit, or `null` for no limit. */ protected function getConcurrentResourceLimit(DrydockBlueprint $blueprint) { @@ -500,7 +502,7 @@ abstract class DrydockBlueprintImplementation extends Phobject { /** * Apply standard limits on resource allocation rate. * - * @param DrydockBlueprint The blueprint requesting an allocation. + * @param DrydockBlueprint $blueprint The blueprint requesting an allocation. * @return bool True if further allocations should be limited. */ protected function shouldLimitAllocatingPoolSize( diff --git a/src/applications/drydock/query/DrydockBlueprintSearchEngine.php b/src/applications/drydock/query/DrydockBlueprintSearchEngine.php index 244afcb986..f556f90146 100644 --- a/src/applications/drydock/query/DrydockBlueprintSearchEngine.php +++ b/src/applications/drydock/query/DrydockBlueprintSearchEngine.php @@ -138,4 +138,32 @@ final class DrydockBlueprintSearchEngine return $result; } + protected function getNewUserBody() { + $see_almanac_button = id(new PHUIButtonView()) + ->setTag('a') + ->setText(pht('See Almanac services')) + ->setHref('/almanac/service/'); + + $create_button = id(new PHUIButtonView()) + ->setTag('a') + ->setText(pht('Create a Blueprint')) + ->setHref('/drydock/blueprint/edit/') + ->setIcon('fa-plus') + ->setColor(PHUIButtonView::GREEN); + + $app_name = pht('Blueprints'); + $view = id(new PHUIBigInfoView()) + ->setIcon('fa-map-o') + ->setTitle(pht('Welcome to %s', $app_name)) + ->setDescription( + pht( + 'Blueprints allow to lease fresh working copies of repositories, '. + 'on your Drydock devices, when needed by CI/CD workflows, and more. '. + 'Blueprints lease services defined in your Almanac.')) + ->addAction($see_almanac_button) + ->addAction($create_button); + + return $view; + } + } diff --git a/src/applications/drydock/storage/DrydockAuthorization.php b/src/applications/drydock/storage/DrydockAuthorization.php index 32e694918c..71f8c36cf8 100644 --- a/src/applications/drydock/storage/DrydockAuthorization.php +++ b/src/applications/drydock/storage/DrydockAuthorization.php @@ -103,10 +103,10 @@ final class DrydockAuthorization extends DrydockDAO * Apply external authorization effects after a user changes the value of a * blueprint selector control an object. * - * @param PhabricatorUser User applying the change. - * @param phid Object PHID change is being applied to. - * @param list Old blueprint PHIDs. - * @param list New blueprint PHIDs. + * @param PhabricatorUser $viewer User applying the change. + * @param phid $object_phid Object PHID change is being applied to. + * @param list $old Old blueprint PHIDs. + * @param list $new New blueprint PHIDs. * @return void */ public static function applyAuthorizationChanges( diff --git a/src/applications/drydock/storage/DrydockResource.php b/src/applications/drydock/storage/DrydockResource.php index 38e6660f7e..f7f29c9bc1 100644 --- a/src/applications/drydock/storage/DrydockResource.php +++ b/src/applications/drydock/storage/DrydockResource.php @@ -69,7 +69,7 @@ final class DrydockResource extends DrydockDAO } public function getCapability($key, $default = null) { - return idx($this->capbilities, $key, $default); + return idx($this->capabilities, $key, $default); } public function getInterface(DrydockLease $lease, $type) { diff --git a/src/applications/drydock/storage/DrydockSlotLock.php b/src/applications/drydock/storage/DrydockSlotLock.php index 7a9bce8b3f..3ba5163342 100644 --- a/src/applications/drydock/storage/DrydockSlotLock.php +++ b/src/applications/drydock/storage/DrydockSlotLock.php @@ -43,7 +43,7 @@ final class DrydockSlotLock extends DrydockDAO { /** * Load all locks held by a particular owner. * - * @param phid Owner PHID. + * @param phid $owner_phid Owner PHID. * @return list All held locks. * @task info */ @@ -57,7 +57,7 @@ final class DrydockSlotLock extends DrydockDAO { /** * Test if a lock is currently free. * - * @param string Lock key to test. + * @param string $lock Lock key to test. * @return bool True if the lock is currently free. * @task info */ @@ -69,7 +69,7 @@ final class DrydockSlotLock extends DrydockDAO { /** * Test if a list of locks are all currently free. * - * @param list List of lock keys to test. + * @param list $locks List of lock keys to test. * @return bool True if all locks are currently free. * @task info */ @@ -82,7 +82,7 @@ final class DrydockSlotLock extends DrydockDAO { /** * Load named locks. * - * @param list List of lock keys to load. + * @param list $locks List of lock keys to load. * @return list List of held locks. * @task info */ @@ -114,8 +114,8 @@ final class DrydockSlotLock extends DrydockDAO { * This method either acquires all the locks or throws an exception (usually * because one or more locks are held). * - * @param phid Lock owner PHID. - * @param list List of locks to acquire. + * @param phid $owner_phid Lock owner PHID. + * @param list $locks List of locks to acquire. * @return void * @task locks */ @@ -158,7 +158,7 @@ final class DrydockSlotLock extends DrydockDAO { /** * Release all locks held by an owner. * - * @param phid Lock owner PHID. + * @param phid $owner_phid Lock owner PHID. * @return void * @task locks */ diff --git a/src/applications/drydock/worker/DrydockLeaseUpdateWorker.php b/src/applications/drydock/worker/DrydockLeaseUpdateWorker.php index 0402276696..54a44660ec 100644 --- a/src/applications/drydock/worker/DrydockLeaseUpdateWorker.php +++ b/src/applications/drydock/worker/DrydockLeaseUpdateWorker.php @@ -168,7 +168,7 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker { * Find or build a resource which can satisfy a given lease request, then * acquire the lease. * - * @param DrydockLease Requested lease. + * @param DrydockLease $lease Requested lease. * @return void * @task allocator */ @@ -573,7 +573,7 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker { * Get all the concrete @{class:DrydockBlueprint}s which can possibly * build a resource to satisfy a lease. * - * @param DrydockLease Requested lease. + * @param DrydockLease $lease Requested lease. * @return list List of qualifying blueprints. * @task allocator */ @@ -645,9 +645,9 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker { * Load a list of all resources which a given lease can possibly be * allocated against. * - * @param list Blueprints which may produce suitable - * resources. - * @param DrydockLease Requested lease. + * @param list $blueprints Blueprints which may produce + * suitable resources. + * @param DrydockLease $lease Requested lease. * @return list Resources which may be able to allocate * the lease. * @task allocator @@ -675,8 +675,8 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker { /** * Remove resources which can not be acquired by a given lease from a list. * - * @param list Candidate resources. - * @param DrydockLease Acquiring lease. + * @param list $resources Candidate resources. + * @param DrydockLease $lease Acquiring lease. * @return list Resources which the lease may be able to * acquire. * @task allocator @@ -703,9 +703,9 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker { * Remove blueprints which are too heavily allocated to build a resource for * a lease from a list of blueprints. * - * @param list List of blueprints. - * @return list List with blueprints that can not allocate - * a resource for the lease right now removed. + * @param list $blueprints List of blueprints. + * @return list $lease List with blueprints that can not + * allocate a resource for the lease right now removed. * @task allocator */ private function removeOverallocatedBlueprints( @@ -731,8 +731,8 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker { * Rank blueprints by suitability for building a new resource for a * particular lease. * - * @param list List of blueprints. - * @param DrydockLease Requested lease. + * @param list $blueprints List of blueprints. + * @param DrydockLease $lease Requested lease. * @return list Ranked list of blueprints. * @task allocator */ @@ -750,8 +750,8 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker { /** * Rank resources by suitability for allocating a particular lease. * - * @param list List of resources. - * @param DrydockLease Requested lease. + * @param list $resources List of resources. + * @param DrydockLease $lease Requested lease. * @return list Ranked list of resources. * @task allocator */ @@ -769,8 +769,9 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker { /** * Perform an actual resource allocation with a particular blueprint. * - * @param DrydockBlueprint The blueprint to allocate a resource from. - * @param DrydockLease Requested lease. + * @param DrydockBlueprint $blueprint The blueprint to allocate a resource + * from. + * @param DrydockLease $lease Requested lease. * @return DrydockResource Allocated resource. * @task allocator */ @@ -815,9 +816,10 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker { * Check that the resource a blueprint allocated is roughly the sort of * object we expect. * - * @param DrydockBlueprint Blueprint which built the resource. - * @param wild Thing which the blueprint claims is a valid resource. - * @param DrydockLease Lease the resource was allocated for. + * @param DrydockBlueprint $blueprint Blueprint which built the resource. + * @param wild $resource Thing which the blueprint claims is a valid + * resource. + * @param DrydockLease $lease Lease the resource was allocated for. * @return void * @task allocator */ @@ -899,8 +901,8 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker { /** * Perform an actual lease acquisition on a particular resource. * - * @param DrydockResource Resource to acquire a lease on. - * @param DrydockLease Lease to acquire. + * @param DrydockResource $resource Resource to acquire a lease on. + * @param DrydockLease $lease Lease to acquire. * @return void * @task acquire */ @@ -931,9 +933,9 @@ final class DrydockLeaseUpdateWorker extends DrydockWorker { /** * Make sure that a lease was really acquired properly. * - * @param DrydockBlueprint Blueprint which created the resource. - * @param DrydockResource Resource which was acquired. - * @param DrydockLease The lease which was supposedly acquired. + * @param DrydockBlueprint $blueprint Blueprint which created the resource. + * @param DrydockResource $resource Resource which was acquired. + * @param DrydockLease $lease The lease which was supposedly acquired. * @return void * @task acquire */ diff --git a/src/applications/drydock/worker/DrydockResourceUpdateWorker.php b/src/applications/drydock/worker/DrydockResourceUpdateWorker.php index 14ef8e4936..6316116c53 100644 --- a/src/applications/drydock/worker/DrydockResourceUpdateWorker.php +++ b/src/applications/drydock/worker/DrydockResourceUpdateWorker.php @@ -38,7 +38,7 @@ final class DrydockResourceUpdateWorker extends DrydockWorker { /** * Update a resource, handling exceptions thrown during the update. * - * @param DrydockReosource Resource to update. + * @param DrydockResource $resource Resource to update. * @return void * @task update */ @@ -58,7 +58,7 @@ final class DrydockResourceUpdateWorker extends DrydockWorker { /** * Update a resource. * - * @param DrydockResource Resource to update. + * @param DrydockResource $resource Resource to update. * @return void * @task update */ @@ -89,8 +89,8 @@ final class DrydockResourceUpdateWorker extends DrydockWorker { /** * Convert a temporary exception into a yield. * - * @param DrydockResource Resource to yield. - * @param Exception Temporary exception worker encountered. + * @param DrydockResource $resource Resource to yield. + * @param Exception $ex Temporary exception worker encountered. * @task update */ private function yieldResource(DrydockResource $resource, Exception $ex) { diff --git a/src/applications/fact/extract/PhabricatorFactUpdateIterator.php b/src/applications/fact/extract/PhabricatorFactUpdateIterator.php index c0d229084f..910c3ffc7a 100644 --- a/src/applications/fact/extract/PhabricatorFactUpdateIterator.php +++ b/src/applications/fact/extract/PhabricatorFactUpdateIterator.php @@ -33,6 +33,7 @@ final class PhabricatorFactUpdateIterator extends PhutilBufferedIterator { } } + #[\ReturnTypeWillChange] public function key() { return $this->getCursorFromObject($this->current()); } diff --git a/src/applications/feed/PhabricatorFeedStoryPublisher.php b/src/applications/feed/PhabricatorFeedStoryPublisher.php index 70d9a2f61c..81c3dceef0 100644 --- a/src/applications/feed/PhabricatorFeedStoryPublisher.php +++ b/src/applications/feed/PhabricatorFeedStoryPublisher.php @@ -214,7 +214,7 @@ final class PhabricatorFeedStoryPublisher extends Phobject { /** * Remove PHIDs who should not receive notifications from a subscriber list. * - * @param list List of potential subscribers. + * @param list $phids List of potential subscribers. * @return list List of actual subscribers. */ private function filterSubscribedPHIDs(array $phids) { diff --git a/src/applications/feed/story/PhabricatorFeedStory.php b/src/applications/feed/story/PhabricatorFeedStory.php index e0c65c7dc2..2d0ed8836b 100644 --- a/src/applications/feed/story/PhabricatorFeedStory.php +++ b/src/applications/feed/story/PhabricatorFeedStory.php @@ -32,8 +32,9 @@ abstract class PhabricatorFeedStory * construct appropriate @{class:PhabricatorFeedStory} wrappers for each * data row. * - * @param list List of @{class:PhabricatorFeedStoryData} rows from the - * database. + * @param list $rows List of @{class:PhabricatorFeedStoryData} rows + * from the database. + * @param PhabricatorUser $viewer * @return list List of @{class:PhabricatorFeedStory} * objects. * @task load diff --git a/src/applications/files/PhabricatorImageTransformer.php b/src/applications/files/PhabricatorImageTransformer.php index 1075969a42..0ca475e90c 100644 --- a/src/applications/files/PhabricatorImageTransformer.php +++ b/src/applications/files/PhabricatorImageTransformer.php @@ -20,8 +20,8 @@ final class PhabricatorImageTransformer extends Phobject { * Phabricator can not encode images in the given format (based on available * extensions), but can save images in another format. * - * @param resource GD image resource. - * @param string? Optionally, preferred mime type. + * @param resource $data GD image resource. + * @param string? $preferred_mime Optionally, preferred mime type. * @return string Bytes of an image file. * @task save */ @@ -62,7 +62,7 @@ final class PhabricatorImageTransformer extends Phobject { /** * Save an image in PNG format, returning the file data as a string. * - * @param resource GD image resource. + * @param resource $image GD image resource. * @return string|null PNG file as a string, or null on failure. * @task save */ @@ -90,7 +90,7 @@ final class PhabricatorImageTransformer extends Phobject { /** * Save an image in GIF format, returning the file data as a string. * - * @param resource GD image resource. + * @param resource $image GD image resource. * @return string|null GIF file as a string, or null on failure. * @task save */ @@ -114,7 +114,7 @@ final class PhabricatorImageTransformer extends Phobject { /** * Save an image in JPG format, returning the file data as a string. * - * @param resource GD image resource. + * @param resource $image GD image resource. * @return string|null JPG file as a string, or null on failure. * @task save */ diff --git a/src/applications/files/document/render/PhabricatorDocumentRenderingEngine.php b/src/applications/files/document/render/PhabricatorDocumentRenderingEngine.php index f43689bbba..c664e56b6d 100644 --- a/src/applications/files/document/render/PhabricatorDocumentRenderingEngine.php +++ b/src/applications/files/document/render/PhabricatorDocumentRenderingEngine.php @@ -208,12 +208,12 @@ abstract class PhabricatorDocumentRenderingEngine $this->activeEngine = $engine; $encode_setting = $request->getStr('encode'); - if (strlen($encode_setting)) { + if (phutil_nonempty_string($encode_setting)) { $engine->setEncodingConfiguration($encode_setting); } $highlight_setting = $request->getStr('highlight'); - if (strlen($highlight_setting)) { + if (phutil_nonempty_string($highlight_setting)) { $engine->setHighlightingConfiguration($highlight_setting); } diff --git a/src/applications/files/engine/PhabricatorChunkedFileStorageEngine.php b/src/applications/files/engine/PhabricatorChunkedFileStorageEngine.php index 30de63b904..596bdc1c60 100644 --- a/src/applications/files/engine/PhabricatorChunkedFileStorageEngine.php +++ b/src/applications/files/engine/PhabricatorChunkedFileStorageEngine.php @@ -87,8 +87,8 @@ final class PhabricatorChunkedFileStorageEngine * Ideally, we'd like to be able to verify hashes, but this is complicated * and time consuming and gives us a fairly small benefit. * - * @param PhabricatorUser Viewing user. - * @param string Claimed file hash. + * @param PhabricatorUser $viewer Viewing user. + * @param string $hash Claimed file hash. * @return string Rehashed file hash. */ public static function getChunkedHash(PhabricatorUser $viewer, $hash) { diff --git a/src/applications/files/engine/PhabricatorFileStorageEngine.php b/src/applications/files/engine/PhabricatorFileStorageEngine.php index 4f2847f8e0..32c8ed13f8 100644 --- a/src/applications/files/engine/PhabricatorFileStorageEngine.php +++ b/src/applications/files/engine/PhabricatorFileStorageEngine.php @@ -7,7 +7,7 @@ * * You can extend this class to provide new file storage backends. * - * For more information, see @{article:File Storage Technical Documentation}. + * For more information, see @{article:Configuring File Storage}. * * @task construct Constructing an Engine * @task meta Engine Metadata @@ -160,8 +160,8 @@ abstract class PhabricatorFileStorageEngine extends Phobject { * throw an exception. If there are other satisfactory but less-preferred * storage engines available, they will be tried. * - * @param string The file data to write. - * @param array File metadata (name, author), if available. + * @param string $data The file data to write. + * @param array $params File metadata (name, author), if available. * @return string Unique string which identifies the stored file, max length * 255. * @task file @@ -172,8 +172,8 @@ abstract class PhabricatorFileStorageEngine extends Phobject { /** * Read the contents of a file previously written by @{method:writeFile}. * - * @param string The handle returned from @{method:writeFile} when the - * file was written. + * @param string $handle The handle returned from @{method:writeFile} + * when the file was written. * @return string File contents. * @task file */ @@ -183,8 +183,8 @@ abstract class PhabricatorFileStorageEngine extends Phobject { /** * Delete the data for a file previously written by @{method:writeFile}. * - * @param string The handle returned from @{method:writeFile} when the - * file was written. + * @param string $handle The handle returned from @{method:writeFile} + * when the file was written. * @return void * @task file */ @@ -200,7 +200,7 @@ abstract class PhabricatorFileStorageEngine extends Phobject { * select the MySQL and Local Disk storage engines if they are configured * to allow a given file. * - * @param int File size in bytes. + * @param int $length File size in bytes. * @task load */ public static function loadStorageEngines($length) { diff --git a/src/applications/files/engine/PhabricatorLocalDiskFileStorageEngine.php b/src/applications/files/engine/PhabricatorLocalDiskFileStorageEngine.php index afed9f20b3..04de4a14a1 100644 --- a/src/applications/files/engine/PhabricatorLocalDiskFileStorageEngine.php +++ b/src/applications/files/engine/PhabricatorLocalDiskFileStorageEngine.php @@ -116,7 +116,7 @@ final class PhabricatorLocalDiskFileStorageEngine /** * Convert a handle into an absolute local disk path. * - * @param string File data handle. + * @param string $handle File data handle. * @return string Absolute path to the corresponding file. * @task internal */ diff --git a/src/applications/files/engine/PhabricatorMySQLFileStorageEngine.php b/src/applications/files/engine/PhabricatorMySQLFileStorageEngine.php index eb49ef78c3..5d8ba94bdb 100644 --- a/src/applications/files/engine/PhabricatorMySQLFileStorageEngine.php +++ b/src/applications/files/engine/PhabricatorMySQLFileStorageEngine.php @@ -80,7 +80,7 @@ final class PhabricatorMySQLFileStorageEngine /** * Load the Lisk object that stores the file data for a handle. * - * @param string File data handle. + * @param string $handle File data handle. * @return PhabricatorFileStorageBlob Data DAO. * @task internal */ diff --git a/src/applications/files/engineextension/PhabricatorFileAttachmentDestructionEngineExtension.php b/src/applications/files/engineextension/PhabricatorFileAttachmentDestructionEngineExtension.php new file mode 100644 index 0000000000..6d62dfa601 --- /dev/null +++ b/src/applications/files/engineextension/PhabricatorFileAttachmentDestructionEngineExtension.php @@ -0,0 +1,22 @@ +loadAllWhere( + 'objectPHID = %s', + $object->getPHID()); + foreach ($attachments as $attachment) { + $attachment->delete(); + } + } +} diff --git a/src/applications/files/iconset/PhabricatorIconSet.php b/src/applications/files/iconset/PhabricatorIconSet.php index baf5422375..c31c3e11fc 100644 --- a/src/applications/files/iconset/PhabricatorIconSet.php +++ b/src/applications/files/iconset/PhabricatorIconSet.php @@ -3,6 +3,8 @@ abstract class PhabricatorIconSet extends Phobject { + abstract protected function newIcons(); + final public function getIconSetKey() { return $this->getPhobjectClassConstant('ICONSETKEY'); } diff --git a/src/applications/files/query/PhabricatorFileQuery.php b/src/applications/files/query/PhabricatorFileQuery.php index e712d533b0..bb81d54b7d 100644 --- a/src/applications/files/query/PhabricatorFileQuery.php +++ b/src/applications/files/query/PhabricatorFileQuery.php @@ -96,7 +96,8 @@ final class PhabricatorFileQuery * `PHID-FILE-aaaa` and all transformations of the file with PHID * `PHID-FILE-bbbb`. * - * @param list List of transform specifications, described above. + * @param list $specs List of transform specifications, described + * above. * @return this */ public function withTransforms(array $specs) { diff --git a/src/applications/files/storage/PhabricatorFile.php b/src/applications/files/storage/PhabricatorFile.php index 2c4c2fcc73..16681973e0 100644 --- a/src/applications/files/storage/PhabricatorFile.php +++ b/src/applications/files/storage/PhabricatorFile.php @@ -176,6 +176,10 @@ final class PhabricatorFile extends PhabricatorFileDAO return true; } + /** + * Get file monogram in the format of "F123" + * @return string + */ public function getMonogram() { return 'F'.$this->getID(); } @@ -803,8 +807,8 @@ final class PhabricatorFile extends PhabricatorFileDAO /** * Return an iterable which emits file content bytes. * - * @param int Offset for the start of data. - * @param int Offset for the end of data. + * @param int? $begin Offset for the start of data. + * @param int? $end Offset for the end of data. * @return Iterable Iterable object which emits requested data. */ public function getFileDataIterator($begin = null, $end = null) { @@ -821,10 +825,19 @@ final class PhabricatorFile extends PhabricatorFileDAO return $iterator; } + /** + * Get file URI in the format of "/F123" + * @return string + */ public function getURI() { return $this->getInfoURI(); } + /** + * Get file view URI in the format of + * https://phorge.example.com/file/data/foo/PHID-FILE-bar/filename + * @return string + */ public function getViewURI() { if (!$this->getPHID()) { throw new Exception( @@ -834,6 +847,12 @@ final class PhabricatorFile extends PhabricatorFileDAO return $this->getCDNURI('data'); } + /** + * Get file view URI in the format of + * https://phorge.example.com/file/data/foo/PHID-FILE-bar/filename or + * https://phorge.example.com/file/download/foo/PHID-FILE-bar/filename + * @return string + */ public function getCDNURI($request_kind) { if (($request_kind !== 'data') && ($request_kind !== 'download')) { @@ -876,7 +895,10 @@ final class PhabricatorFile extends PhabricatorFileDAO } } - + /** + * Get file info URI in the format of "/F123" + * @return string + */ public function getInfoURI() { return '/'.$this->getMonogram(); } @@ -889,6 +911,11 @@ final class PhabricatorFile extends PhabricatorFileDAO } } + /** + * Get file view URI in the format of + * https://phorge.example.com/file/download/foo/PHID-FILE-bar/filename + * @return string + */ public function getDownloadURI() { return $this->getCDNURI('download'); } @@ -917,10 +944,20 @@ final class PhabricatorFile extends PhabricatorFileDAO return PhabricatorEnv::getCDNURI($path); } + /** + * Whether the file can be viewed in a browser + * @return bool True if MIME type of the file is listed in the + * files.viewable-mime-types setting + */ public function isViewableInBrowser() { return ($this->getViewableMimeType() !== null); } + /** + * Whether the file is an image viewable in the browser + * @return bool True if MIME type of the file is listed in the + * files.image-mime-types setting and file is viewable in the browser + */ public function isViewableImage() { if (!$this->isViewableInBrowser()) { return false; @@ -931,6 +968,11 @@ final class PhabricatorFile extends PhabricatorFileDAO return idx($mime_map, $mime_type); } + /** + * Whether the file is an audio file + * @return bool True if MIME type of the file is listed in the + * files.audio-mime-types setting and file is viewable in the browser + */ public function isAudio() { if (!$this->isViewableInBrowser()) { return false; @@ -941,6 +983,11 @@ final class PhabricatorFile extends PhabricatorFileDAO return idx($mime_map, $mime_type); } + /** + * Whether the file is a video file + * @return bool True if MIME type of the file is listed in the + * files.video-mime-types setting and file is viewable in the browser + */ public function isVideo() { if (!$this->isViewableInBrowser()) { return false; @@ -951,6 +998,11 @@ final class PhabricatorFile extends PhabricatorFileDAO return idx($mime_map, $mime_type); } + /** + * Whether the file is a PDF file + * @return bool True if MIME type of the file is application/pdf and file is + * viewable in the browser + */ public function isPDF() { if (!$this->isViewableInBrowser()) { return false; @@ -1048,6 +1100,11 @@ final class PhabricatorFile extends PhabricatorFileDAO ->execute(); } + /** + * Whether the file is listed as a viewable MIME type + * @return bool True if MIME type of the file is listed in the + * files.viewable-mime-types setting + */ public function getViewableMimeType() { $mime_map = PhabricatorEnv::getEnvConfig('files.viewable-mime-types'); @@ -1157,8 +1214,9 @@ final class PhabricatorFile extends PhabricatorFileDAO * Builtins are located in `resources/builtin/` and identified by their * name. * - * @param PhabricatorUser Viewing user. - * @param list List of builtin file specs. + * @param PhabricatorUser $user Viewing user. + * @param list $builtins List of builtin file + * specs. * @return dict Dictionary of named builtins. */ public static function loadBuiltins(PhabricatorUser $user, array $builtins) { @@ -1224,8 +1282,8 @@ final class PhabricatorFile extends PhabricatorFileDAO /** * Convenience wrapper for @{method:loadBuiltins}. * - * @param PhabricatorUser Viewing user. - * @param string Single builtin name to load. + * @param PhabricatorUser $user Viewing user. + * @param string $name Single builtin name to load. * @return PhabricatorFile Corresponding builtin file. */ public static function loadBuiltin(PhabricatorUser $user, $name) { @@ -1416,7 +1474,7 @@ final class PhabricatorFile extends PhabricatorFileDAO * Write the policy edge between this file and some object. * This method is successful even if the file is already attached. * - * @param phid Object PHID to attach to. + * @param phid $phid Object PHID to attach to. * @return this */ public function attachToObject($phid) { @@ -1430,8 +1488,8 @@ final class PhabricatorFile extends PhabricatorFileDAO * NOTE: Please avoid to use this static method directly. * Instead, use PhabricatorFile#attachToObject(phid). * - * @param phid File PHID to attach from. - * @param phid Object PHID to attach to. + * @param phid $file_phid File PHID to attach from. + * @param phid $object_phid Object PHID to attach to. * @return void */ public static function attachFileToObject($file_phid, $object_phid) { @@ -1469,8 +1527,8 @@ final class PhabricatorFile extends PhabricatorFileDAO * This method is called both when creating a file from fresh data, and * when creating a new file which reuses existing storage. * - * @param map Bag of parameters, see @{class:PhabricatorFile} - * for documentation. + * @param map $params Bag of parameters, see + * @{class:PhabricatorFile} for documentation. * @return this */ private function readPropertiesFromParameters(array $params) { @@ -1728,6 +1786,14 @@ final class PhabricatorFile extends PhabricatorFileDAO PhabricatorDestructionEngine $engine) { $this->openTransaction(); + + $attachments = id(new PhabricatorFileAttachment())->loadAllWhere( + 'filePHID = %s', + $this->getPHID()); + foreach ($attachments as $attachment) { + $attachment->delete(); + } + $this->delete(); $this->saveTransaction(); } diff --git a/src/applications/files/transform/PhabricatorFileImageTransform.php b/src/applications/files/transform/PhabricatorFileImageTransform.php index 98ffcdd706..a06d00916b 100644 --- a/src/applications/files/transform/PhabricatorFileImageTransform.php +++ b/src/applications/files/transform/PhabricatorFileImageTransform.php @@ -11,7 +11,7 @@ abstract class PhabricatorFileImageTransform extends PhabricatorFileTransform { /** * Get an estimate of the transformed dimensions of a file. * - * @param PhabricatorFile File to transform. + * @param PhabricatorFile $file File to transform. * @return list|null Width and height, if available. */ public function getTransformedDimensions(PhabricatorFile $file) { @@ -132,7 +132,7 @@ abstract class PhabricatorFileImageTransform extends PhabricatorFileTransform { /** * Create a new @{class:PhabricatorFile} from raw data. * - * @param string Raw file data. + * @param string $data Raw file data. */ protected function newFileFromData($data) { if ($this->file) { @@ -159,8 +159,8 @@ abstract class PhabricatorFileImageTransform extends PhabricatorFileTransform { /** * Create a new image filled with transparent pixels. * - * @param int Desired image width. - * @param int Desired image height. + * @param int $w Desired image width. + * @param int $h Desired image height. * @return resource New image resource. */ protected function newEmptyImage($w, $h) { diff --git a/src/applications/flag/customfield/PhorgeFlagFlaggedObjectCustomField.php b/src/applications/flag/customfield/PhorgeFlagFlaggedObjectCustomField.php new file mode 100644 index 0000000000..8d2f358efd --- /dev/null +++ b/src/applications/flag/customfield/PhorgeFlagFlaggedObjectCustomField.php @@ -0,0 +1,46 @@ +flag) { + return; + } + // I'm very open to improvements in the way a Flag is displayed + $icon = PhabricatorFlagColor::getIcon($this->flag->getColor()); + $view->addIcon($icon); + } + + + public function shouldUseStorage() { + return true; + } + + public function setValueFromStorage($value) { + $this->flag = $value; + } + + // The parent function is defined to return a PhabricatorCustomFieldStorage, + // but that assumes a DTO with a particular form; That doesn't apply here. + // Maybe the function needs to be re-defined with a suitable interface. + // For now, PhorgeFlagFlaggedObjectFieldStorage just duck-types into the + // right shape. + public function newStorageObject() { + return id(new PhorgeFlagFlaggedObjectFieldStorage()) + ->setViewer($this->getViewer()); + } + +} diff --git a/src/applications/flag/customfield/PhorgeFlagFlaggedObjectFieldStorage.php b/src/applications/flag/customfield/PhorgeFlagFlaggedObjectFieldStorage.php new file mode 100644 index 0000000000..b8f28a2fac --- /dev/null +++ b/src/applications/flag/customfield/PhorgeFlagFlaggedObjectFieldStorage.php @@ -0,0 +1,36 @@ +viewer = $viewer; + return $this; + } + + public function getStorageSourceKey() { + return 'flags/flag'; + } + + public function loadStorageSourceData(array $fields) { + + $objects = mpull($fields, 'getObject'); + $object_phids = mpull($objects, 'getPHID'); + $flags = (new PhabricatorFlagQuery()) + ->setViewer($this->viewer) + ->withOwnerPHIDs(array($this->viewer->getPHID())) + ->withObjectPHIDs($object_phids) + ->execute(); + $flags = mpull($flags, null, 'getObjectPHID'); + + $result = array(); + foreach ($fields as $key => $field) { + $target_phid = $field->getObject()->getPHID(); + $result[$key] = idx($flags, $target_phid); + } + + return $result; + } + +} diff --git a/src/applications/harbormaster/constants/HarbormasterBuildStatus.php b/src/applications/harbormaster/constants/HarbormasterBuildStatus.php index 009758078f..9fe61b5f91 100644 --- a/src/applications/harbormaster/constants/HarbormasterBuildStatus.php +++ b/src/applications/harbormaster/constants/HarbormasterBuildStatus.php @@ -96,7 +96,7 @@ final class HarbormasterBuildStatus extends Phobject { /** * Get a human readable name for a build status constant. * - * @param const Build status constant. + * @param const $status Build status constant. * @return string Human-readable name. */ public static function getBuildStatusName($status) { diff --git a/src/applications/harbormaster/engine/HarbormasterBuildEngine.php b/src/applications/harbormaster/engine/HarbormasterBuildEngine.php index fd72e31a0e..266fed3804 100644 --- a/src/applications/harbormaster/engine/HarbormasterBuildEngine.php +++ b/src/applications/harbormaster/engine/HarbormasterBuildEngine.php @@ -322,8 +322,9 @@ final class HarbormasterBuildEngine extends Phobject { * particularly relevant when a build uses multiple hosts since it returns * hosts to the pool more quickly. * - * @param list Targets in the build. - * @param list List of running and waiting steps. + * @param list $targets Targets in the build. + * @param list $steps List of running and waiting + * steps. * @return void */ private function releaseUnusedArtifacts(array $targets, array $steps) { @@ -372,7 +373,7 @@ final class HarbormasterBuildEngine extends Phobject { * Process messages which were sent to these targets, kicking applicable * targets out of "Waiting" and into either "Passed" or "Failed". * - * @param list List of targets to process. + * @param list $targets List of targets to process. * @return void */ private function updateWaitingTargets(array $targets) { @@ -436,7 +437,7 @@ final class HarbormasterBuildEngine extends Phobject { * the overall state of the associated buildable. Compute the new aggregate * state and save it on the buildable. * - * @param HarbormasterBuild The buildable to update. + * @param HarbormasterBuild $buildable The buildable to update. * @return void */ public function updateBuildable(HarbormasterBuildable $buildable) { diff --git a/src/applications/harbormaster/engine/HarbormasterTargetEngine.php b/src/applications/harbormaster/engine/HarbormasterTargetEngine.php index d9efb8b9fd..62f679774d 100644 --- a/src/applications/harbormaster/engine/HarbormasterTargetEngine.php +++ b/src/applications/harbormaster/engine/HarbormasterTargetEngine.php @@ -56,7 +56,7 @@ final class HarbormasterTargetEngine extends Phobject { * * This method creates the steps if they do not yet exist. * - * @param list Autotarget keys, like `"core.arc.lint"`. + * @param list $autotargets Autotarget keys, like `"core.arc.lint"`. * @return map Map of keys to step objects. */ private function generateBuildStepMap(array $autotargets) { @@ -127,7 +127,7 @@ final class HarbormasterTargetEngine extends Phobject { * Get all of the @{class:HarbormasterBuildStepImplementation} objects for * a list of autotarget keys. * - * @param list Autotarget keys, like `"core.arc.lint"`. + * @param list $autotargets Autotarget keys, like `"core.arc.lint"`. * @return map Map of keys to implementations. */ private function getAutosteps(array $autotargets) { @@ -154,8 +154,8 @@ final class HarbormasterTargetEngine extends Phobject { * * If some targets or builds do not exist, they are created. * - * @param HarbormasterBuildable A buildable. - * @param map Map of keys to steps. + * @param HarbormasterBuildable $buildable A buildable. + * @param map $step_map Map of keys to steps. * @return map Map of keys to targets. */ private function generateBuildTargetMap( diff --git a/src/applications/harbormaster/step/HarbormasterBuildStepImplementation.php b/src/applications/harbormaster/step/HarbormasterBuildStepImplementation.php index 3bbb72cd95..851a020687 100644 --- a/src/applications/harbormaster/step/HarbormasterBuildStepImplementation.php +++ b/src/applications/harbormaster/step/HarbormasterBuildStepImplementation.php @@ -187,9 +187,11 @@ abstract class HarbormasterBuildStepImplementation extends Phobject { * * ls 'dir with spaces' * - * @param string Name of a `vxsprintf` function, like @{function:vcsprintf}. - * @param string User-provided pattern string containing `${variables}`. - * @param dict List of available replacement variables. + * @param string $function Name of a `vxsprintf` function, like + * @{function:vcsprintf}. + * @param string $pattern User-provided pattern string containing + * `${variables}`. + * @param dict $variables List of available replacement variables. * @return string String with variables replaced safely into it. */ protected function mergeVariables($function, $pattern, array $variables) { diff --git a/src/applications/harbormaster/storage/HarbormasterBuildable.php b/src/applications/harbormaster/storage/HarbormasterBuildable.php index df8451d842..2d085ce523 100644 --- a/src/applications/harbormaster/storage/HarbormasterBuildable.php +++ b/src/applications/harbormaster/storage/HarbormasterBuildable.php @@ -60,9 +60,9 @@ final class HarbormasterBuildable /** * Start builds for a given buildable. * - * @param phid PHID of the object to build. - * @param phid Container PHID for the buildable. - * @param list List of builds to perform. + * @param phid $phid PHID of the object to build. + * @param phid $container_phid Container PHID for the buildable. + * @param list $requests List of builds to perform. * @return void */ public static function applyBuildPlans( diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuildLogChunkIterator.php b/src/applications/harbormaster/storage/build/HarbormasterBuildLogChunkIterator.php index 514ad7013c..b477dd7e31 100644 --- a/src/applications/harbormaster/storage/build/HarbormasterBuildLogChunkIterator.php +++ b/src/applications/harbormaster/storage/build/HarbormasterBuildLogChunkIterator.php @@ -18,6 +18,7 @@ final class HarbormasterBuildLogChunkIterator $this->cursor = $this->min; } + #[\ReturnTypeWillChange] public function key() { return $this->current()->getID(); } diff --git a/src/applications/herald/action/HeraldAction.php b/src/applications/herald/action/HeraldAction.php index d914c97a1c..38542cf5ed 100644 --- a/src/applications/herald/action/HeraldAction.php +++ b/src/applications/herald/action/HeraldAction.php @@ -298,10 +298,9 @@ abstract class HeraldAction extends Phobject { $no_permission[] = $phid; unset($targets[$phid]); } - } - - if ($no_permission) { - $this->logEffect(self::DO_STANDARD_PERMISSION, $no_permission); + if ($no_permission) { + $this->logEffect(self::DO_STANDARD_PERMISSION, $no_permission); + } } return $targets; diff --git a/src/applications/herald/adapter/HeraldAdapter.php b/src/applications/herald/adapter/HeraldAdapter.php index ce6f371d91..8824f645e3 100644 --- a/src/applications/herald/adapter/HeraldAdapter.php +++ b/src/applications/herald/adapter/HeraldAdapter.php @@ -171,7 +171,8 @@ abstract class HeraldAdapter extends Phobject { * These transactions are set by @{class:PhabricatorApplicationEditor} * automatically, before it invokes Herald. * - * @param list List of transactions. + * @param list $xactions List of + * transactions. * @return this */ final public function setAppliedTransactions(array $xactions) { @@ -843,16 +844,6 @@ abstract class HeraldAdapter extends Phobject { return $impl->getHeraldActionValueType(); } - private function buildTokenizerFieldValue( - PhabricatorTypeaheadDatasource $datasource) { - - $key = 'action.'.get_class($datasource); - - return id(new HeraldTokenizerFieldValue()) - ->setKey($key) - ->setDatasource($datasource); - } - /* -( Repetition )--------------------------------------------------------- */ diff --git a/src/applications/herald/engine/HeraldEngine.php b/src/applications/herald/engine/HeraldEngine.php index 91a2050779..852fbab090 100644 --- a/src/applications/herald/engine/HeraldEngine.php +++ b/src/applications/herald/engine/HeraldEngine.php @@ -3,7 +3,6 @@ final class HeraldEngine extends Phobject { protected $rules = array(); - protected $activeRule; protected $transcript; private $fieldCache = array(); @@ -533,8 +532,12 @@ final class HeraldEngine extends Phobject { if ($caught) { $result_data = array( 'exception.class' => get_class($caught), - 'exception.message' => $ex->getMessage(), + 'exception.message' => $caught->getMessage(), ); + phlog(pht('An exception occurred executing Herald rule %s: "%s" Review '. + 'the Herald transcripts and correct or disable the problematic rule.', + $rule->getMonogram(), + $caught->getMessage())); } $result = HeraldConditionResult::newFromResultCode($result_code) @@ -590,6 +593,10 @@ final class HeraldEngine extends Phobject { $this->popProfilerRule($rule); if ($caught) { + phlog(pht('An exception occurred executing Herald rule %s: "%s" Review '. + 'the Herald transcripts and correct or disable the problematic rule.', + $rule->getMonogram(), + $caught->getMessage())); throw $caught; } @@ -681,6 +688,14 @@ final class HeraldEngine extends Phobject { ->setTarget($action->getTarget()) ->setRule($rule); + if ($object->getActionImplementation($action->getAction()) === null) { + phlog(pht('An exception occurred executing Herald rule %s: Unknown '. + 'action: "%s". Review the Herald transcripts and correct or '. + 'disable the problematic rule.', + $rule->getMonogram(), + $action->getAction())); + } + $name = $rule->getName(); $id = $rule->getID(); $effect->setReason( diff --git a/src/applications/maniphest/constants/ManiphestTaskPriority.php b/src/applications/maniphest/constants/ManiphestTaskPriority.php index 8b43da132b..1b20b52738 100644 --- a/src/applications/maniphest/constants/ManiphestTaskPriority.php +++ b/src/applications/maniphest/constants/ManiphestTaskPriority.php @@ -137,7 +137,7 @@ final class ManiphestTaskPriority extends ManiphestConstants { /** * Retrieve the full name of the priority level provided. * - * @param int A priority level. + * @param int $priority A priority level. * @return string The priority name if the level is a valid one. */ public static function getTaskPriorityName($priority) { @@ -147,7 +147,7 @@ final class ManiphestTaskPriority extends ManiphestConstants { /** * Retrieve the color of the priority level given * - * @param int A priority level. + * @param int $priority A priority level. * @return string The color of the priority if the level is valid, * or black if it is not. */ diff --git a/src/applications/maniphest/controller/ManiphestReportController.php b/src/applications/maniphest/controller/ManiphestReportController.php index a7efe89194..012d6d136b 100644 --- a/src/applications/maniphest/controller/ManiphestReportController.php +++ b/src/applications/maniphest/controller/ManiphestReportController.php @@ -487,6 +487,12 @@ final class ManiphestReportController extends ManiphestController { ); } + private function getAveragePriority() { + // TODO: This is sort of a hard-code for the default "normal" status. + // When reports are more powerful, this should be made more general. + return 50; + } + public function renderOpenTasks() { $request = $this->getRequest(); $viewer = $request->getUser(); @@ -626,9 +632,7 @@ final class ManiphestReportController extends ManiphestController { $normal_or_better = array(); foreach ($taskv as $id => $task) { - // TODO: This is sort of a hard-code for the default "normal" status. - // When reports are more powerful, this should be made more general. - if ($task->getPriority() < 50) { + if ($task->getPriority() < $this->getAveragePriority()) { continue; } $normal_or_better[$id] = $task; @@ -700,13 +704,22 @@ final class ManiphestReportController extends ManiphestController { ), pht('Oldest (All)')); $cclass[] = 'n'; + $low_priorities = array(); + $priorities_map = ManiphestTaskPriority::getTaskPriorityMap(); + $normal_priority = $this->getAveragePriority(); + foreach ($priorities_map as $pri => $full_label) { + if ($pri < $normal_priority) { + $low_priorities[] = $full_label; + } + } + $pri_string = implode(', ', $low_priorities); $cname[] = javelin_tag( 'span', array( 'sigil' => 'has-tooltip', 'meta' => array( 'tip' => pht( - 'Oldest open task, excluding those with Low or Wishlist priority.'), + 'Oldest open task, excluding those with priority %s', $pri_string), 'size' => 200, ), ), diff --git a/src/applications/maniphest/controller/ManiphestTaskDetailController.php b/src/applications/maniphest/controller/ManiphestTaskDetailController.php index af23126f9b..fe79cd48b3 100644 --- a/src/applications/maniphest/controller/ManiphestTaskDetailController.php +++ b/src/applications/maniphest/controller/ManiphestTaskDetailController.php @@ -203,8 +203,7 @@ final class ManiphestTaskDetailController extends ManiphestController { ->addPropertySection(pht('Description'), $description) ->addPropertySection(pht('Details'), $details); - - return $this->newPage() + $page = $this->newPage() ->setTitle($title) ->setCrumbs($crumbs) ->setPageObjectPHIDs( @@ -213,6 +212,74 @@ final class ManiphestTaskDetailController extends ManiphestController { )) ->appendChild($view); + if ($this->getIncludeOpenGraphMetadata($viewer, $task)) { + $page = $this->addOpenGraphProtocolMetadataTags($page, $task); + } + + return $page; + } + + /** + * Whether the page should include Open Graph metadata tags + * @param PhabricatorUser $viewer Viewer of the object + * @param object $object + * @return bool True if the page should serve Open Graph metadata tags + */ + private function getIncludeOpenGraphMetadata(PhabricatorUser $viewer, + $object) { + // Don't waste time adding OpenGraph metadata for logged-in users + if ($viewer->getIsStandardUser()) { + return false; + } + // Include OpenGraph tags only for public objects + return $object->getViewPolicy() === PhabricatorPolicies::POLICY_PUBLIC; + } + + /** + * Get Open Graph Protocol metadata values + * @param ManiphestTask $task + * @return array Map of Open Graph property => value + */ + private function getOpenGraphProtocolMetadataValues($task) { + $viewer = $this->getViewer(); + + $v = []; + $v['og:site_name'] = PlatformSymbols::getPlatformServerName(); + $v['og:type'] = 'object'; + $v['og:url'] = PhabricatorEnv::getProductionURI($task->getURI()); + $v['og:title'] = $task->getMonogram().' '.$task->getTitle(); + + $desc = $task->getDescription(); + if (phutil_nonempty_string($desc)) { + $v['og:description'] = + PhabricatorMarkupEngine::summarizeSentence($desc); + } + + $v['og:image'] = + PhabricatorCustomLogoConfigType::getLogoURI($viewer); + + $v['og:image:height'] = 64; + $v['og:image:width'] = 64; + + return $v; + } + + /** + * Add Open Graph Protocol metadata tags to Maniphest task page + * @param PhabricatorStandardPageView $page + * @param ManiphestTask $task + * @return $page with additional OGP tags + */ + private function addOpenGraphProtocolMetadataTags($page, $task) { + foreach ($this->getOpenGraphProtocolMetadataValues($task) as $k => $v) { + $page->addHeadItem(phutil_tag( + 'meta', + array( + 'property' => $k, + 'content' => $v, + ))); + } + return $page; } private function buildHeaderView(ManiphestTask $task) { diff --git a/src/applications/maniphest/editor/ManiphestEditEngine.php b/src/applications/maniphest/editor/ManiphestEditEngine.php index 46877168e7..e11c977436 100644 --- a/src/applications/maniphest/editor/ManiphestEditEngine.php +++ b/src/applications/maniphest/editor/ManiphestEditEngine.php @@ -69,6 +69,14 @@ final class ManiphestEditEngine return pht('Set Sail for Adventure'); } + public function getCommentFieldPlaceholderText($object) { + if ($object->getStatus() === ManiphestTaskStatus::STATUS_CLOSED_DUPLICATE) { + return pht('This task is closed as a duplicate. '. + 'Only comment if you think that this task is not a duplicate.'); + } + return ''; + } + protected function getObjectViewURI($object) { return '/'.$object->getMonogram(); } diff --git a/src/applications/maniphest/field/ManiphestCustomField.php b/src/applications/maniphest/field/ManiphestCustomField.php index bbd97babf4..26c18cbb8c 100644 --- a/src/applications/maniphest/field/ManiphestCustomField.php +++ b/src/applications/maniphest/field/ManiphestCustomField.php @@ -15,17 +15,4 @@ abstract class ManiphestCustomField return new ManiphestCustomFieldNumericIndex(); } - /** - * When the user creates a task, the UI prompts them to "Create another - * similar task". This copies some fields (e.g., Owner and CCs) but not other - * fields (e.g., description). If this custom field should also be copied, - * return true from this method. - * - * @return bool True to copy the default value from the template task when - * creating a new similar task. - */ - public function shouldCopyWhenCreatingSimilarTask() { - return false; - } - } diff --git a/src/applications/maniphest/field/ManiphestFlagCustomField.php b/src/applications/maniphest/field/ManiphestFlagCustomField.php new file mode 100644 index 0000000000..febf0aea8d --- /dev/null +++ b/src/applications/maniphest/field/ManiphestFlagCustomField.php @@ -0,0 +1,18 @@ +setProxy(new PhorgeFlagFlaggedObjectCustomField()); + } + + public function canSetProxy() { + return true; + } + + public function newStorageObject() { + return $this->getProxy()->newStorageObject(); + } +} diff --git a/src/applications/maniphest/query/ManiphestTaskQuery.php b/src/applications/maniphest/query/ManiphestTaskQuery.php index c206bd6599..7947981ec7 100644 --- a/src/applications/maniphest/query/ManiphestTaskQuery.php +++ b/src/applications/maniphest/query/ManiphestTaskQuery.php @@ -358,6 +358,10 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery { $where[] = $this->buildOwnerWhereClause($conn); if ($this->taskIDs !== null) { + if (!ctype_digit(implode('', $this->taskIDs))) { + throw new PhutilSearchQueryCompilerSyntaxException( + pht('Task IDs must be integer numbers.')); + } $where[] = qsprintf( $conn, 'task.id in (%Ld)', diff --git a/src/applications/maniphest/query/ManiphestTaskSearchEngine.php b/src/applications/maniphest/query/ManiphestTaskSearchEngine.php index 4f3e9c0462..541f7f3831 100644 --- a/src/applications/maniphest/query/ManiphestTaskSearchEngine.php +++ b/src/applications/maniphest/query/ManiphestTaskSearchEngine.php @@ -374,11 +374,17 @@ final class ManiphestTaskSearchEngine ManiphestBulkEditCapability::CAPABILITY); } + $custom_field_lists = $this->loadCustomFields( + $tasks, + PhabricatorCustomField::ROLE_LIST); + $list = id(new ManiphestTaskResultListView()) ->setUser($viewer) ->setTasks($tasks) + ->setHandles($handles) ->setSavedQuery($saved) ->setCanBatchEdit($can_bulk_edit) + ->setCustomFieldLists($custom_field_lists) ->setShowBatchControls($this->showBatchControls); $result = new PhabricatorApplicationSearchResultView(); @@ -387,6 +393,24 @@ final class ManiphestTaskSearchEngine return $result; } + protected function getRequiredHandlePHIDsForResultList( + array $objects, + PhabricatorSavedQuery $query) { + + $phids = array(); + foreach ($objects as $task) { + $assigned_phid = $task->getOwnerPHID(); + if ($assigned_phid) { + $phids[] = $assigned_phid; + } + foreach ($task->getProjectPHIDs() as $project_phid) { + $phids[] = $project_phid; + } + } + + return $phids; + } + protected function willUseSavedQuery(PhabricatorSavedQuery $saved) { // The 'withUnassigned' parameter may be present in old saved queries from diff --git a/src/applications/maniphest/view/ManiphestTaskListView.php b/src/applications/maniphest/view/ManiphestTaskListView.php index f9ad9e6046..d5d3d1e9c5 100644 --- a/src/applications/maniphest/view/ManiphestTaskListView.php +++ b/src/applications/maniphest/view/ManiphestTaskListView.php @@ -4,6 +4,7 @@ final class ManiphestTaskListView extends ManiphestView { private $tasks; private $handles; + private $customFieldLists = array(); private $showBatchControls; private $noDataString; @@ -19,6 +20,11 @@ final class ManiphestTaskListView extends ManiphestView { return $this; } + public function setCustomFieldLists(array $lists) { + $this->customFieldLists = $lists; + return $this; + } + public function setShowBatchControls($show_batch_controls) { $this->showBatchControls = $show_batch_controls; return $this; @@ -132,12 +138,21 @@ final class ManiphestTaskListView extends ManiphestView { ->setHref($href)); } + + $field_list = idx($this->customFieldLists, $task->getPHID()); + if ($field_list) { + $field_list + ->addFieldsToListViewItem($task, $this->getViewer(), $item); + } + $list->addItem($item); } return $list; } + // This method should be removed, and all call-sites switch + // to use ManiphestSearchEngine public static function loadTaskHandles( PhabricatorUser $viewer, array $tasks) { diff --git a/src/applications/maniphest/view/ManiphestTaskResultListView.php b/src/applications/maniphest/view/ManiphestTaskResultListView.php index cc2a135292..6ee2025265 100644 --- a/src/applications/maniphest/view/ManiphestTaskResultListView.php +++ b/src/applications/maniphest/view/ManiphestTaskResultListView.php @@ -3,6 +3,8 @@ final class ManiphestTaskResultListView extends ManiphestView { private $tasks; + private $handles; + private $customFieldLists = array(); private $savedQuery; private $canBatchEdit; private $showBatchControls; @@ -17,6 +19,16 @@ final class ManiphestTaskResultListView extends ManiphestView { return $this; } + public function setHandles(array $handles) { + $this->handles = $handles; + return $this; + } + + public function setCustomFieldLists(array $lists) { + $this->customFieldLists = $lists; + return $this; + } + public function setCanBatchEdit($can_batch_edit) { $this->canBatchEdit = $can_batch_edit; return $this; @@ -42,11 +54,10 @@ final class ManiphestTaskResultListView extends ManiphestView { $group_parameter = nonempty($query->getParameter('group'), 'priority'); $order_parameter = nonempty($query->getParameter('order'), 'priority'); - $handles = ManiphestTaskListView::loadTaskHandles($viewer, $tasks); $groups = $this->groupTasks( $tasks, $group_parameter, - $handles); + $this->handles); $result = array(); @@ -56,7 +67,8 @@ final class ManiphestTaskResultListView extends ManiphestView { $task_list->setShowBatchControls($this->showBatchControls); $task_list->setUser($viewer); $task_list->setTasks($list); - $task_list->setHandles($handles); + $task_list->setHandles($this->handles); + $task_list->setCustomFieldLists($this->customFieldLists); $header = id(new PHUIHeaderView()) ->addSigil('task-group') diff --git a/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php index 3b24dff590..b856507374 100644 --- a/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php +++ b/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php @@ -33,6 +33,11 @@ final class ManiphestTaskPointsTransaction '%s set the point value for this task to %s.', $this->renderAuthor(), $this->renderNewValue()); + } else if ($new === null && $old !== null) { + return pht( + '%s removed the point value %s for this task.', + $this->renderAuthor(), + $this->renderOldValue()); } else if ($new === null) { return pht( '%s removed the point value for this task.', @@ -77,7 +82,7 @@ final class ManiphestTaskPointsTransaction foreach ($xactions as $xaction) { $new = $xaction->getNewValue(); - if (strlen($new) && !is_numeric($new)) { + if (phutil_nonempty_string($new) && !is_numeric($new)) { $errors[] = $this->newInvalidError( pht('Points value must be numeric or empty.')); continue; diff --git a/src/applications/maniphest/xaction/ManiphestTaskTitleTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskTitleTransaction.php index c01c263ba6..bef8914cf7 100644 --- a/src/applications/maniphest/xaction/ManiphestTaskTitleTransaction.php +++ b/src/applications/maniphest/xaction/ManiphestTaskTitleTransaction.php @@ -4,6 +4,7 @@ final class ManiphestTaskTitleTransaction extends ManiphestTaskTransactionType { const TRANSACTIONTYPE = 'title'; + private $maximumTaskTitleLength = 255; public function generateOldValue($object) { return $object->getTitle(); @@ -78,6 +79,13 @@ final class ManiphestTaskTitleTransaction $xaction); continue; } + if (mb_strlen($new) > $this->maximumTaskTitleLength) { + $errors[] = $this->newInvalidError( + pht('Task title cannot exceed %d characters.', + $this->maximumTaskTitleLength), + $xaction); + continue; + } } if (!$errors) { diff --git a/src/applications/meta/panel/PhabricatorApplicationConfigurationPanel.php b/src/applications/meta/panel/PhabricatorApplicationConfigurationPanel.php index d96132c9b0..944daa9e08 100644 --- a/src/applications/meta/panel/PhabricatorApplicationConfigurationPanel.php +++ b/src/applications/meta/panel/PhabricatorApplicationConfigurationPanel.php @@ -27,7 +27,7 @@ abstract class PhabricatorApplicationConfigurationPanel /** * Get the URI for this application configuration panel. * - * @param string? Optional path to append. + * @param string? $path Optional path to append. * @return string Relative URI for the panel. */ public function getPanelURI($path = '') { diff --git a/src/applications/metamta/adapter/PhabricatorMailSMTPAdapter.php b/src/applications/metamta/adapter/PhabricatorMailSMTPAdapter.php index abbda40146..f46968ad1a 100644 --- a/src/applications/metamta/adapter/PhabricatorMailSMTPAdapter.php +++ b/src/applications/metamta/adapter/PhabricatorMailSMTPAdapter.php @@ -62,7 +62,7 @@ final class PhabricatorMailSMTPAdapter $smtp->Host = $this->getOption('host'); $smtp->Port = $this->getOption('port'); $user = $this->getOption('user'); - if (strlen($user)) { + if (phutil_nonempty_string($user)) { $smtp->SMTPAuth = true; $smtp->Username = $user; $smtp->Password = $this->getOption('password'); diff --git a/src/applications/metamta/engine/PhabricatorMailEmailEngine.php b/src/applications/metamta/engine/PhabricatorMailEmailEngine.php index 4278aa96a1..872f417cb2 100644 --- a/src/applications/metamta/engine/PhabricatorMailEmailEngine.php +++ b/src/applications/metamta/engine/PhabricatorMailEmailEngine.php @@ -72,7 +72,7 @@ final class PhabricatorMailEmailEngine $message->setSubject($subject); $headers = $this->newEmailHeaders(); - foreach ($this->newEmailThreadingHeaders($mailer) as $threading_header) { + foreach ($this->newEmailThreadingHeaders() as $threading_header) { $headers[] = $threading_header; } diff --git a/src/applications/metamta/parser/PhabricatorMetaMTAEmailBodyParser.php b/src/applications/metamta/parser/PhabricatorMetaMTAEmailBodyParser.php index f20e626573..45b7735ce0 100644 --- a/src/applications/metamta/parser/PhabricatorMetaMTAEmailBodyParser.php +++ b/src/applications/metamta/parser/PhabricatorMetaMTAEmailBodyParser.php @@ -26,7 +26,7 @@ final class PhabricatorMetaMTAEmailBodyParser extends Phobject { * ), * ) * - * @param string Raw mail text body. + * @param string $body Raw mail text body. * @return dict Parsed body. */ public function parseBody($body) { diff --git a/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php b/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php index 0342a94a60..4b7b0eee37 100644 --- a/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php +++ b/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php @@ -19,10 +19,11 @@ abstract class PhabricatorObjectMailReceiver extends PhabricatorMailReceiver { * Load the object receiving mail, based on an identifying pattern. Normally * this pattern is some sort of object ID. * - * @param string A string matched by @{method:getObjectPattern} - * fragment. - * @param PhabricatorUser The viewing user. - * @return void + * @param string $pattern A string matched by + * @{method:getObjectPattern} fragment. + * @param PhabricatorUser $viewer The viewing user. + * @return object|null The object to receive mail, or null if no such + * object exists. */ abstract protected function loadObject($pattern, PhabricatorUser $viewer); diff --git a/src/applications/metamta/replyhandler/PhabricatorMailReplyHandler.php b/src/applications/metamta/replyhandler/PhabricatorMailReplyHandler.php index 72e23ec2c1..53941e94cf 100644 --- a/src/applications/metamta/replyhandler/PhabricatorMailReplyHandler.php +++ b/src/applications/metamta/replyhandler/PhabricatorMailReplyHandler.php @@ -199,8 +199,8 @@ abstract class PhabricatorMailReplyHandler extends Phobject { * Each target should be sent a separate email, and contains the information * required to generate it with appropriate permissions and configuration. * - * @param list List of "To" PHIDs. - * @param list List of "CC" PHIDs. + * @param list $raw_to List of "To" PHIDs. + * @param list $raw_cc List of "CC" PHIDs. * @return list List of targets. */ final public function getMailTargets(array $raw_to, array $raw_cc) { @@ -274,8 +274,8 @@ abstract class PhabricatorMailReplyHandler extends Phobject { * This takes any compound recipients (like projects) and looks up all their * members. * - * @param list List of To PHIDs. - * @param list List of CC PHIDs. + * @param list $to List of To PHIDs. + * @param list $cc List of CC PHIDs. * @return pair, list> Expanded PHID lists. */ private function expandRecipientPHIDs(array $to, array $cc) { @@ -332,8 +332,8 @@ abstract class PhabricatorMailReplyHandler extends Phobject { * * Invalid recipients are dropped from the results. * - * @param list List of To PHIDs. - * @param list List of CC PHIDs. + * @param list $to List of To PHIDs. + * @param list $cc List of CC PHIDs. * @return pair Maps from PHIDs to users. */ private function loadRecipientUsers(array $to, array $cc) { @@ -370,8 +370,8 @@ abstract class PhabricatorMailReplyHandler extends Phobject { /** * Remove recipients who do not have permission to view the mail receiver. * - * @param map Map of "To" users. - * @param map Map of "CC" users. + * @param map $to Map of "To" users. + * @param map $cc Map of "CC" users. * @return pair Filtered user maps. */ private function filterRecipientUsers(array $to, array $cc) { diff --git a/src/applications/metamta/storage/PhabricatorMetaMTAMail.php b/src/applications/metamta/storage/PhabricatorMetaMTAMail.php index cc3ae82bef..2d1ec54bb9 100644 --- a/src/applications/metamta/storage/PhabricatorMetaMTAMail.php +++ b/src/applications/metamta/storage/PhabricatorMetaMTAMail.php @@ -88,7 +88,7 @@ final class PhabricatorMetaMTAMail * These tags are used to allow users to opt out of receiving certain types * of mail, like updates when a task's projects change. * - * @param list + * @param list $tags * @return this */ public function setMailTags(array $tags) { @@ -109,7 +109,7 @@ final class PhabricatorMetaMTAMail * needs to be set whenever an action is triggered by an email message. See * T251 for more details. * - * @param string The "Message-ID" of the email which precedes this one. + * @param string $id The "Message-ID" of the email which precedes this one. * @return this */ public function setParentMessageID($id) { @@ -419,7 +419,7 @@ final class PhabricatorMetaMTAMail * This is primarily intended to let users who don't want any email still * receive things like password resets. * - * @param bool True to force delivery despite user preferences. + * @param bool $force True to force delivery despite user preferences. * @return this */ public function setForceDelivery($force) { @@ -437,7 +437,7 @@ final class PhabricatorMetaMTAMail * "Precedence: bulk" or similar, but is implementation and configuration * dependent. * - * @param bool True if the mail is automated bulk mail. + * @param bool $is_bulk True if the mail is automated bulk mail. * @return this */ public function setIsBulk($is_bulk) { @@ -454,9 +454,10 @@ final class PhabricatorMetaMTAMail * set appropriate headers (Message-ID, In-Reply-To, References and * Thread-Index) based on the capabilities of the underlying mailer. * - * @param string Unique identifier, appropriate for use in a Message-ID, - * In-Reply-To or References headers. - * @param bool If true, indicates this is the first message in the thread. + * @param string $thread_id Unique identifier, appropriate for use in a + * Message-ID, In-Reply-To or References headers. + * @param bool? $is_first_message If true, indicates this is the first + * message in the thread. * @return this */ public function setThreadID($thread_id, $is_first_message = false) { @@ -858,8 +859,8 @@ final class PhabricatorMetaMTAMail * For example, this will expand project PHIDs into a list of the project's * members. * - * @param list List of recipient PHIDs, possibly including aggregate - * recipients. + * @param list $phids List of recipient PHIDs, possibly including + * aggregate recipients. * @return list Deaggregated list of mailable recipients. */ public function expandRecipients(array $phids) { diff --git a/src/applications/metamta/util/PhabricatorMailUtil.php b/src/applications/metamta/util/PhabricatorMailUtil.php index 270e9786f3..66f2289227 100644 --- a/src/applications/metamta/util/PhabricatorMailUtil.php +++ b/src/applications/metamta/util/PhabricatorMailUtil.php @@ -10,7 +10,7 @@ final class PhabricatorMailUtil * which can make forwarding rules easier to write. This method strips the * prefix if it is present, and normalizes casing and whitespace. * - * @param PhutilEmailAddress Email address. + * @param PhutilEmailAddress $address Email address. * @return PhutilEmailAddress Normalized address. */ public static function normalizeAddress(PhutilEmailAddress $address) { @@ -47,8 +47,8 @@ final class PhabricatorMailUtil * * "Abraham" # With configured prefix. * - * @param PhutilEmailAddress Email address. - * @param PhutilEmailAddress Another email address. + * @param PhutilEmailAddress $u Email address. + * @param PhutilEmailAddress $v Another email address. * @return bool True if addresses are effectively the same address. */ public static function matchAddresses( diff --git a/src/applications/metamta/view/PhabricatorMetaMTAMailBody.php b/src/applications/metamta/view/PhabricatorMetaMTAMailBody.php index 6622f0afeb..2178bfc130 100644 --- a/src/applications/metamta/view/PhabricatorMetaMTAMailBody.php +++ b/src/applications/metamta/view/PhabricatorMetaMTAMailBody.php @@ -40,7 +40,7 @@ final class PhabricatorMetaMTAMailBody extends Phobject { /** * Add a raw block of text to the email. This will be rendered as-is. * - * @param string Block of text. + * @param string $text Block of text. * @return this * @task compose */ @@ -100,8 +100,8 @@ final class PhabricatorMetaMTAMailBody extends Phobject { * HEADER * Text is indented. * - * @param string Header text. - * @param string Section text. + * @param string $header Header text. + * @param string $section Section text. * @return this * @task compose */ @@ -155,7 +155,7 @@ final class PhabricatorMetaMTAMailBody extends Phobject { /** * Add an attachment. * - * @param PhabricatorMailAttachment Attachment. + * @param PhabricatorMailAttachment $attachment Attachment. * @return this * @task compose */ @@ -198,7 +198,7 @@ final class PhabricatorMetaMTAMailBody extends Phobject { /** * Indent a block of text for rendering under a section heading. * - * @param string Text to indent. + * @param string $text Text to indent. * @return string Indented text. * @task render */ diff --git a/src/applications/notification/query/PhabricatorNotificationQuery.php b/src/applications/notification/query/PhabricatorNotificationQuery.php index 021d666b13..3c2a26a217 100644 --- a/src/applications/notification/query/PhabricatorNotificationQuery.php +++ b/src/applications/notification/query/PhabricatorNotificationQuery.php @@ -31,8 +31,8 @@ final class PhabricatorNotificationQuery * only unread notifications, while `false` means to return only //read// * notifications. The default is `null`, which returns both. * - * @param mixed True or false to filter results by read status. Null to remove - * the filter. + * @param mixed $unread True or false to filter results by read status. Null + * to remove the filter. * @return this * @task config */ diff --git a/src/applications/nuance/item/NuanceGitHubEventItemType.php b/src/applications/nuance/item/NuanceGitHubEventItemType.php index 1f6249f222..9e8f902a16 100644 --- a/src/applications/nuance/item/NuanceGitHubEventItemType.php +++ b/src/applications/nuance/item/NuanceGitHubEventItemType.php @@ -20,6 +20,10 @@ final class NuanceGitHubEventItemType return $this->newRawEvent($item)->getEventFullTitle(); } + protected function newWorkCommands(NuanceItem $item) { + return array(); + } + public function canUpdateItems() { return true; } diff --git a/src/applications/nuance/item/NuanceItemType.php b/src/applications/nuance/item/NuanceItemType.php index 3a65ad0195..245e11187d 100644 --- a/src/applications/nuance/item/NuanceItemType.php +++ b/src/applications/nuance/item/NuanceItemType.php @@ -6,6 +6,8 @@ abstract class NuanceItemType private $viewer; private $controller; + abstract protected function newWorkCommands(NuanceItem $item); + public function setViewer(PhabricatorUser $viewer) { $this->viewer = $viewer; return $this; diff --git a/src/applications/passphrase/view/PassphraseCredentialControl.php b/src/applications/passphrase/view/PassphraseCredentialControl.php index 98294832f7..09245157d2 100644 --- a/src/applications/passphrase/view/PassphraseCredentialControl.php +++ b/src/applications/passphrase/view/PassphraseCredentialControl.php @@ -157,9 +157,9 @@ final class PassphraseCredentialControl extends AphrontFormControl { * - If you do change the credential, the new credential must be one you * can use. * - * @param PhabricatorUser The acting user. - * @param list List of credential altering - * transactions. + * @param PhabricatorUser $actor The acting user. + * @param list $xactions List of + * credential altering transactions. * @return bool True if the transactions are valid. */ public static function validateTransactions( diff --git a/src/applications/people/controller/PhabricatorPeopleController.php b/src/applications/people/controller/PhabricatorPeopleController.php index c2c262f9f4..d51dc4ceec 100644 --- a/src/applications/people/controller/PhabricatorPeopleController.php +++ b/src/applications/people/controller/PhabricatorPeopleController.php @@ -6,37 +6,33 @@ abstract class PhabricatorPeopleController extends PhabricatorController { return true; } + /** + * return AphrontSideNavFilterView + */ public function buildSideNavView($for_app = false) { + // we are on /people/* $nav = new AphrontSideNavFilterView(); $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); - $name = null; - if ($for_app) { - $name = $this->getRequest()->getURIData('username'); - if ($name) { - $nav->setBaseURI(new PhutilURI('/p/')); - $nav->addFilter("{$name}/", $name); - $nav->addFilter("{$name}/calendar/", pht('Calendar')); - } - } + $viewer = $this->getRequest()->getUser(); + id(new PhabricatorPeopleSearchEngine()) + ->setViewer($viewer) + ->addNavigationItems($nav->getMenu()); - if (!$name) { - $viewer = $this->getRequest()->getUser(); - id(new PhabricatorPeopleSearchEngine()) - ->setViewer($viewer) - ->addNavigationItems($nav->getMenu()); - - if ($viewer->getIsAdmin()) { - $nav->addLabel(pht('User Administration')); - $nav->addFilter('logs', pht('Activity Logs')); - $nav->addFilter('invite', pht('Email Invitations')); - } + if ($viewer->getIsAdmin()) { + $nav->addLabel(pht('User Administration')); + $nav->addFilter('logs', pht('Activity Logs')); + $nav->addFilter('invite', pht('Email Invitations')); } return $nav; } public function buildApplicationMenu() { + if ($this->getRequest()->getURIData('username')) { + // we are on /p/name/ so return the default user profile sidebar + return parent::buildApplicationMenu(); + } return $this->buildSideNavView(true)->getMenu(); } diff --git a/src/applications/people/mail/PhabricatorPeopleEmailLoginMailEngine.php b/src/applications/people/mail/PhabricatorPeopleEmailLoginMailEngine.php index 4904e269a6..f34e86585a 100644 --- a/src/applications/people/mail/PhabricatorPeopleEmailLoginMailEngine.php +++ b/src/applications/people/mail/PhabricatorPeopleEmailLoginMailEngine.php @@ -84,14 +84,17 @@ final class PhabricatorPeopleEmailLoginMailEngine "Condolences on forgetting your password. You can use this ". "link to reset it:\n\n". " %s\n\n". - "After you set a new password, consider writing it down on a ". - "sticky note and attaching it to your monitor so you don't ". - "forget again! Choosing a very short, easy-to-remember password ". - "like \"cat\" or \"1234\" might also help.\n\n". - "Best Wishes,\nPhabricator\n", - $login_uri); + "After setting a new password, consider writing it down ". + "on a sticky note and attaching it to your monitor so others ". + "can impersonate you at any time. Choosing a short, ". + "easy-to-remember password like \"cat\" or \"1234\"". + " might also help to get your machine hacked, your". + " bank account emptied, or your company ruined.". + "\n\nBest Wishes,\n%s", + $login_uri, + PlatformSymbols::getPlatformServerName()); - } + } } else { $body[] = pht( "You can use this login link to regain access to your account:". diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php index 82f1affe82..0e1e2560ac 100644 --- a/src/applications/people/storage/PhabricatorUser.php +++ b/src/applications/people/storage/PhabricatorUser.php @@ -429,8 +429,8 @@ final class PhabricatorUser /** * Test if a given setting is set to a particular value. * - * @param const Setting key. - * @param wild Value to compare. + * @param const $key Setting key. + * @param wild $value Value to compare. * @return bool True if the setting has the specified value. * @task settings */ @@ -477,7 +477,7 @@ final class PhabricatorUser * * This is primarily useful for unit tests. * - * @param string New timezone identifier. + * @param string $identifier New timezone identifier. * @return this * @task settings */ @@ -770,8 +770,8 @@ final class PhabricatorUser /** * Write to the availability cache. * - * @param wild Availability cache data. - * @param int|null Cache TTL. + * @param wild $availability Availability cache data. + * @param int|null $ttl Cache TTL. * @return this * @task availability */ @@ -916,7 +916,7 @@ final class PhabricatorUser * Get a @{class:PhabricatorHandleList} which benefits from this viewer's * internal handle pool. * - * @param list List of PHIDs to load. + * @param list $phids List of PHIDs to load. * @return PhabricatorHandleList Handle list object. * @task handle */ @@ -935,7 +935,7 @@ final class PhabricatorUser * * This benefits from the viewer's internal handle pool. * - * @param phid PHID to render a handle for. + * @param phid $phid PHID to render a handle for. * @return PHUIHandleView View of the handle. * @task handle */ @@ -949,7 +949,7 @@ final class PhabricatorUser * * This benefits from the viewer's internal handle pool. * - * @param list List of PHIDs to render. + * @param list $phids List of PHIDs to render. * @return PHUIHandleListView View of the handles. * @task handle */ diff --git a/src/applications/people/storage/PhabricatorUserCache.php b/src/applications/people/storage/PhabricatorUserCache.php index 71b3e4816b..36292db4e9 100644 --- a/src/applications/people/storage/PhabricatorUserCache.php +++ b/src/applications/people/storage/PhabricatorUserCache.php @@ -32,11 +32,6 @@ final class PhabricatorUserCache extends PhabricatorUserDAO { ) + parent::getConfiguration(); } - public function save() { - $this->cacheIndex = Filesystem::digestForIndex($this->getCacheKey()); - return parent::save(); - } - public static function writeCache( PhabricatorUserCacheType $type, $key, diff --git a/src/applications/people/storage/PhabricatorUserEmail.php b/src/applications/people/storage/PhabricatorUserEmail.php index d9866f2c43..bf6b487d88 100644 --- a/src/applications/people/storage/PhabricatorUserEmail.php +++ b/src/applications/people/storage/PhabricatorUserEmail.php @@ -180,7 +180,7 @@ final class PhabricatorUserEmail /** * Send a verification email from $user to this address. * - * @param PhabricatorUser The user sending the verification. + * @param PhabricatorUser $user The user sending the verification. * @return this * @task email */ @@ -229,9 +229,8 @@ final class PhabricatorUserEmail * Send a notification email from $user to this address, informing the * recipient that this is no longer their account's primary address. * - * @param PhabricatorUser The user sending the notification. - * @param PhabricatorUserEmail New primary email address. - * @return this + * @param PhabricatorUser $user The user sending the notification. + * @param PhabricatorUserEmail $new New primary email address. * @task email */ public function sendOldPrimaryEmail( @@ -270,7 +269,7 @@ final class PhabricatorUserEmail * Send a notification email from $user to this address, informing the * recipient that this is now their account's new primary email address. * - * @param PhabricatorUser The user sending the verification. + * @param PhabricatorUser $user The user sending the verification. * @return this * @task email */ diff --git a/src/applications/people/typeahead/PhabricatorViewerDatasource.php b/src/applications/people/typeahead/PhabricatorViewerDatasource.php index 6f6d1181fe..cb367ccd4d 100644 --- a/src/applications/people/typeahead/PhabricatorViewerDatasource.php +++ b/src/applications/people/typeahead/PhabricatorViewerDatasource.php @@ -34,8 +34,12 @@ final class PhabricatorViewerDatasource ); } + protected function isFunctionWithLoginRequired($function) { + return true; + } + public function loadResults() { - if ($this->getViewer()->getPHID()) { + if ($this->getViewer()->isLoggedIn()) { $results = array($this->renderViewerFunctionToken()); } else { $results = array(); @@ -45,7 +49,7 @@ final class PhabricatorViewerDatasource } protected function canEvaluateFunction($function) { - if (!$this->getViewer()->getPHID()) { + if (!$this->getViewer()->isLoggedIn()) { return false; } diff --git a/src/applications/phame/storage/PhamePost.php b/src/applications/phame/storage/PhamePost.php index 5291bcd465..9b4a6ba01a 100644 --- a/src/applications/phame/storage/PhamePost.php +++ b/src/applications/phame/storage/PhamePost.php @@ -268,6 +268,7 @@ final class PhamePost extends PhameDAO case self::MARKUP_FIELD_BODY: return $this->getBody(); } + return null; } public function didMarkupText( diff --git a/src/applications/phid/PhabricatorObjectHandle.php b/src/applications/phid/PhabricatorObjectHandle.php index 577fd05692..6a6c1f19b8 100644 --- a/src/applications/phid/PhabricatorObjectHandle.php +++ b/src/applications/phid/PhabricatorObjectHandle.php @@ -275,7 +275,7 @@ final class PhabricatorObjectHandle * Set whether or not the underlying object is complete. See * @{method:isComplete} for an explanation of what it means to be complete. * - * @param bool True if the handle represents a complete object. + * @param bool $complete True if the handle represents a complete object. * @return this */ public function setComplete($complete) { diff --git a/src/applications/phid/query/PhabricatorObjectQuery.php b/src/applications/phid/query/PhabricatorObjectQuery.php index 8364f0210d..f1dd1feb4d 100644 --- a/src/applications/phid/query/PhabricatorObjectQuery.php +++ b/src/applications/phid/query/PhabricatorObjectQuery.php @@ -196,8 +196,8 @@ final class PhabricatorObjectQuery * viewer. This method is generally used to validate that PHIDs affected by * a transaction are valid. * - * @param PhabricatorUser Viewer. - * @param list List of ostensibly valid PHIDs. + * @param PhabricatorUser $viewer Viewer. + * @param list $phids List of ostensibly valid PHIDs. * @return list List of invalid or restricted PHIDs. */ public static function loadInvalidPHIDsForViewer( diff --git a/src/applications/phid/type/PhabricatorPHIDType.php b/src/applications/phid/type/PhabricatorPHIDType.php index 6a283a7d08..fda09c00d8 100644 --- a/src/applications/phid/type/PhabricatorPHIDType.php +++ b/src/applications/phid/type/PhabricatorPHIDType.php @@ -52,8 +52,8 @@ abstract class PhabricatorPHIDType extends Phobject { * can provide a dummy implementation for this method and overload * @{method:loadObjects} instead. * - * @param PhabricatorObjectQuery Query being executed. - * @param list PHIDs to load. + * @param PhabricatorObjectQuery $query Query being executed. + * @param list $phids PHIDs to load. * @return PhabricatorPolicyAwareQuery Query object which loads the * specified PHIDs when executed. */ @@ -67,8 +67,8 @@ abstract class PhabricatorPHIDType extends Phobject { * necessary to implement @{method:buildQueryForObjects} to get object * loading to work. * - * @param PhabricatorObjectQuery Query being executed. - * @param list PHIDs to load. + * @param PhabricatorObjectQuery $query Query being executed. + * @param list $phids PHIDs to load. * @return list Corresponding objects. */ public function loadObjects( @@ -113,10 +113,11 @@ abstract class PhabricatorPHIDType extends Phobject { * each handle at a minimum. See @{class:PhabricatorObjectHandle} for other * handle properties. * - * @param PhabricatorHandleQuery Issuing query object. - * @param list Handles to populate with data. - * @param list Objects for these PHIDs loaded by - * @{method:buildQueryForObjects()}. + * @param PhabricatorHandleQuery $query Issuing query object. + * @param list $handles Handles to populate with + * data. + * @param list $objects Objects for these PHIDs + * loaded by @{method:buildQueryForObjects()}. * @return void */ abstract public function loadHandles( @@ -165,7 +166,7 @@ abstract class PhabricatorPHIDType extends Phobject { /** * Get all PHID types of applications installed for a given viewer. * - * @param PhabricatorUser Viewing user. + * @param PhabricatorUser $viewer Viewing user. * @return dict Map of constants to installed * types. */ @@ -209,7 +210,7 @@ abstract class PhabricatorPHIDType extends Phobject { /** * Get all PHID types of an application. * - * @param string Class name of an application + * @param string $application Class name of an application * @return dict Map of constants of application */ public static function getAllTypesForApplication( diff --git a/src/applications/phid/utils.php b/src/applications/phid/utils.php index 152a547284..eb14c57df9 100644 --- a/src/applications/phid/utils.php +++ b/src/applications/phid/utils.php @@ -4,7 +4,7 @@ * Look up the type of a PHID. Returns * PhabricatorPHIDConstants::PHID_TYPE_UNKNOWN if it fails to look up the type * - * @param phid Anything. + * @param phid $phid Anything. * @return string A value from PhabricatorPHIDConstants (ideally) */ function phid_get_type($phid) { @@ -18,7 +18,7 @@ function phid_get_type($phid) { /** * Group a list of phids by type. * - * @param phids array of phids + * @param $phids Array of phids * @return map of phid type => list of phids */ function phid_group_by_type($phids) { diff --git a/src/applications/pholio/controller/PholioMockViewController.php b/src/applications/pholio/controller/PholioMockViewController.php index f4695d120c..36a8ec8c81 100644 --- a/src/applications/pholio/controller/PholioMockViewController.php +++ b/src/applications/pholio/controller/PholioMockViewController.php @@ -223,7 +223,7 @@ final class PholioMockViewController extends PholioController { $form = id(new PhabricatorApplicationTransactionCommentView()) ->setUser($viewer) - ->setObjectPHID($mock->getPHID()) + ->setObject($mock) ->setFormID($comment_form_id) ->setDraft($draft) ->setHeaderText($title) diff --git a/src/applications/phortune/pdf/PhabricatorPDFIterator.php b/src/applications/phortune/pdf/PhabricatorPDFIterator.php index d39168369d..81626b91f5 100644 --- a/src/applications/phortune/pdf/PhabricatorPDFIterator.php +++ b/src/applications/phortune/pdf/PhabricatorPDFIterator.php @@ -45,7 +45,7 @@ final class PhabricatorPDFIterator } public function key() { - return $this->framgentKey; + return $this->fragmentKey; } public function next() { diff --git a/src/applications/phpast/controller/PhabricatorXHPASTViewController.php b/src/applications/phpast/controller/PhabricatorXHPASTViewController.php index 2ef34fb36f..3b36112a24 100644 --- a/src/applications/phpast/controller/PhabricatorXHPASTViewController.php +++ b/src/applications/phpast/controller/PhabricatorXHPASTViewController.php @@ -1,18 +1,4 @@ buildStandardPageView(); - - $page->setApplicationName('XHPASTView'); - $page->setBaseURI('/xhpast/'); - $page->setTitle(idx($data, 'title')); - $page->setGlyph("\xE2\x96\xA0"); - $page->appendChild($view); - - $response = new AphrontWebpageResponse(); - return $response->setContent($page->render()); - } - } diff --git a/src/applications/phpast/controller/PhabricatorXHPASTViewFrameController.php b/src/applications/phpast/controller/PhabricatorXHPASTViewFrameController.php index 89f5b8fd7d..73c10cab44 100644 --- a/src/applications/phpast/controller/PhabricatorXHPASTViewFrameController.php +++ b/src/applications/phpast/controller/PhabricatorXHPASTViewFrameController.php @@ -10,17 +10,18 @@ final class PhabricatorXHPASTViewFrameController public function handleRequest(AphrontRequest $request) { $id = $request->getURIData('id'); - return $this->buildStandardPageResponse( - phutil_tag( + return $this->newPage() + ->setApplicationName('XHPASTView') + ->setBaseURI('/xhpast/') + ->setTitle(pht('XHPAST View')) + ->setGlyph("\xE2\x96\xA0") + ->appendChild(phutil_tag( 'iframe', array( 'src' => "/xhpast/frameset/{$id}/", 'frameborder' => '0', 'style' => 'width: 100%; height: 800px;', '', - )), - array( - 'title' => pht('XHPAST View'), - )); + ))); } } diff --git a/src/applications/phrequent/storage/PhrequentTimeBlock.php b/src/applications/phrequent/storage/PhrequentTimeBlock.php index 3800651476..8649b4f67a 100644 --- a/src/applications/phrequent/storage/PhrequentTimeBlock.php +++ b/src/applications/phrequent/storage/PhrequentTimeBlock.php @@ -254,7 +254,8 @@ final class PhrequentTimeBlock extends Phobject { * This is used to avoid double-counting time on objects which had timers * started multiple times. * - * @param list> List of possibly overlapping time ranges. + * @param list> $ranges List of possibly overlapping time + * ranges. * @return list> Nonoverlapping time ranges. */ public static function mergeTimeRanges(array $ranges) { diff --git a/src/applications/phriction/conduit/PhrictionDocumentEditConduitAPIMethod.php b/src/applications/phriction/conduit/PhrictionDocumentEditConduitAPIMethod.php new file mode 100644 index 0000000000..247875cce4 --- /dev/null +++ b/src/applications/phriction/conduit/PhrictionDocumentEditConduitAPIMethod.php @@ -0,0 +1,19 @@ +setTransactionType(PhrictionDocumentTitleTransaction::TRANSACTIONTYPE) ->setNewValue($title); - $xactions[] = id(new PhrictionTransaction()) + $content_xaction = id(new PhrictionTransaction()) ->setTransactionType($edit_type) ->setNewValue($content_text); + + $content_metadata = $request->getStr('content_metadata'); + if ($content_metadata) { + $content_metadata = phutil_json_decode($content_metadata); + $attached_file_phids = idx( + $content_metadata, + 'attachedFilePHIDs', + array()); + + if ($attached_file_phids) { + $metadata_object = array( + 'remarkup.control' => array( + 'attachedFilePHIDs' => $attached_file_phids, + ), + ); + + $content_xaction->setMetadata($metadata_object); + } + } + + $xactions[] = $content_xaction; + $xactions[] = id(new PhrictionTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) ->setNewValue($v_view) diff --git a/src/applications/phriction/phid/PhrictionDocumentPHIDType.php b/src/applications/phriction/phid/PhrictionDocumentPHIDType.php index 4dcb039121..458b32b09e 100644 --- a/src/applications/phriction/phid/PhrictionDocumentPHIDType.php +++ b/src/applications/phriction/phid/PhrictionDocumentPHIDType.php @@ -47,4 +47,26 @@ final class PhrictionDocumentPHIDType extends PhabricatorPHIDType { } } + public function canLoadNamedObject($name) { + return preg_match('/.*\/$/', $name); + } + + public function loadNamedObjects( + PhabricatorObjectQuery $query, + array $names) { + $objects = id(new PhrictionDocumentQuery()) + ->setViewer($query->getViewer()) + ->withSlugs($names) + ->execute(); + + $results = array(); + foreach ($objects as $id => $object) { + foreach ($names as $name) { + $results[$name] = $object; + } + } + + return $results; + } + } diff --git a/src/applications/policy/filter/PhabricatorPolicyFilter.php b/src/applications/policy/filter/PhabricatorPolicyFilter.php index e7e8c86ee7..2913381be9 100644 --- a/src/applications/policy/filter/PhabricatorPolicyFilter.php +++ b/src/applications/policy/filter/PhabricatorPolicyFilter.php @@ -52,11 +52,13 @@ final class PhabricatorPolicyFilter extends Phobject { * ...will throw a @{class:PhabricatorPolicyException} if the new policy would * remove the user's ability to edit the object. * - * @param PhabricatorUser The viewer to perform a policy check for. - * @param PhabricatorPolicyInterface The object to perform a policy check on. - * @param string Capability to test. - * @param string Perform the test as though the object has this - * policy instead of the policy it actually has. + * @param PhabricatorUser $viewer The viewer to perform a policy check for. + * @param PhabricatorPolicyInterface $object The object to perform a policy + * check on. + * @param string $capability Capability to test. + * @param string $forced_policy Perform the test as though the + * object has this policy instead of the policy it + * actually has. * @return void */ public static function requireCapabilityWithForcedPolicy( diff --git a/src/applications/policy/interface/PhabricatorExtendedPolicyInterface.php b/src/applications/policy/interface/PhabricatorExtendedPolicyInterface.php index deeaecdbef..1a56dacfba 100644 --- a/src/applications/policy/interface/PhabricatorExtendedPolicyInterface.php +++ b/src/applications/policy/interface/PhabricatorExtendedPolicyInterface.php @@ -62,8 +62,8 @@ interface PhabricatorExtendedPolicyInterface { * // ... * ); * - * @param const Capability being tested. - * @param PhabricatorUser Viewer whose capabilities are being tested. + * @param const $capability Capability being tested. + * @param PhabricatorUser $viewer Viewer whose capabilities are being tested. * @return list> List of extended policies. */ public function getExtendedPolicy($capability, PhabricatorUser $viewer); diff --git a/src/applications/policy/rule/PhabricatorPolicyRule.php b/src/applications/policy/rule/PhabricatorPolicyRule.php index 2f9fac9cfb..fe3edce0aa 100644 --- a/src/applications/policy/rule/PhabricatorPolicyRule.php +++ b/src/applications/policy/rule/PhabricatorPolicyRule.php @@ -120,9 +120,9 @@ abstract class PhabricatorPolicyRule extends Phobject { * rendering a verdict about whether the user will be able to see the object * or not after applying the policy change. * - * @param PhabricatorPolicyInterface Object to pass a hint about. - * @param PhabricatorPolicyRule Rule to pass hint to. - * @param wild Hint. + * @param PhabricatorPolicyInterface $object Object to pass a hint about. + * @param PhabricatorPolicyRule $rule Rule to pass hint to. + * @param wild $hint Hint. * @return void */ public static function passTransactionHintToRule( diff --git a/src/applications/policy/storage/PhabricatorPolicy.php b/src/applications/policy/storage/PhabricatorPolicy.php index 5e5ec14107..56f15e37b8 100644 --- a/src/applications/policy/storage/PhabricatorPolicy.php +++ b/src/applications/policy/storage/PhabricatorPolicy.php @@ -348,7 +348,7 @@ final class PhabricatorPolicy * policy. This is used to bulk load data (like project memberships) in order * to apply policy filters efficiently. * - * @param string Policy rule classname. + * @param string $rule_class Policy rule classname. * @return list List of values used in this policy. */ public function getCustomRuleValues($rule_class) { @@ -402,7 +402,7 @@ final class PhabricatorPolicy * set of unique users. In this case, neither is strictly stronger than * the other. * - * @param PhabricatorPolicy Other policy. + * @param PhabricatorPolicy $other Other policy. * @return bool `true` if this policy is more restrictive than the other * policy. */ diff --git a/src/applications/ponder/controller/PonderQuestionViewController.php b/src/applications/ponder/controller/PonderQuestionViewController.php index 2cd555204c..63fe4e3539 100644 --- a/src/applications/ponder/controller/PonderQuestionViewController.php +++ b/src/applications/ponder/controller/PonderQuestionViewController.php @@ -60,7 +60,7 @@ final class PonderQuestionViewController extends PonderController { $add_comment = id(new PhabricatorApplicationTransactionCommentView()) ->setUser($viewer) - ->setObjectPHID($question->getPHID()) + ->setObject($question) ->setShowPreview(false) ->setAction($this->getApplicationURI("/question/comment/{$id}/")) ->setSubmitButtonName(pht('Comment')); diff --git a/src/applications/ponder/mail/PonderQuestionCreateMailReceiver.php b/src/applications/ponder/mail/PonderQuestionCreateMailReceiver.php index 32669855ea..496cedb8a1 100644 --- a/src/applications/ponder/mail/PonderQuestionCreateMailReceiver.php +++ b/src/applications/ponder/mail/PonderQuestionCreateMailReceiver.php @@ -20,11 +20,11 @@ final class PonderQuestionCreateMailReceiver $xactions = array(); $xactions[] = id(new PonderQuestionTransaction()) - ->setTransactionType(PonderQuestionTransaction::TYPE_TITLE) + ->setTransactionType(PonderQuestionTitleTransaction::TRANSACTIONTYPE) ->setNewValue($title); $xactions[] = id(new PonderQuestionTransaction()) - ->setTransactionType(PonderQuestionTransaction::TYPE_CONTENT) + ->setTransactionType(PonderQuestionContentTransaction::TRANSACTIONTYPE) ->setNewValue($mail->getCleanTextBody()); $question = PonderQuestion::initializeNewQuestion($author); diff --git a/src/applications/ponder/view/PonderAnswerView.php b/src/applications/ponder/view/PonderAnswerView.php index 474661381c..a16e7bfbd4 100644 --- a/src/applications/ponder/view/PonderAnswerView.php +++ b/src/applications/ponder/view/PonderAnswerView.php @@ -121,7 +121,7 @@ final class PonderAnswerView extends AphrontTagView { $comment_view = id(new PhabricatorApplicationTransactionCommentView()) ->setUser($viewer) - ->setObjectPHID($answer->getPHID()) + ->setObject($answer) ->setShowPreview(false) ->setHeaderText(pht('Answer Comment')) ->setAction("/ponder/answer/comment/{$id}/") diff --git a/src/applications/project/controller/PhabricatorProjectSubprojectsController.php b/src/applications/project/controller/PhabricatorProjectSubprojectsController.php index be787e7a4f..5b8ac2486a 100644 --- a/src/applications/project/controller/PhabricatorProjectSubprojectsController.php +++ b/src/applications/project/controller/PhabricatorProjectSubprojectsController.php @@ -212,16 +212,4 @@ final class PhabricatorProjectSubprojectsController return $curtain; } - private function renderStatus($icon, $target, $note) { - $item = id(new PHUIStatusItemView()) - ->setIcon($icon) - ->setTarget(phutil_tag('strong', array(), $target)) - ->setNote($note); - - return id(new PHUIStatusListView()) - ->addItem($item); - } - - - } diff --git a/src/applications/project/trigger/PhabricatorProjectTriggerAddSubscribersRule.php b/src/applications/project/trigger/PhabricatorProjectTriggerAddSubscribersRule.php new file mode 100644 index 0000000000..28b0c614fe --- /dev/null +++ b/src/applications/project/trigger/PhabricatorProjectTriggerAddSubscribersRule.php @@ -0,0 +1,127 @@ +getDatasource()->getWireTokens($this->getValue()); + } + + protected function assertValidRuleRecordFormat($value) { + if (!is_array($value)) { + throw new Exception( + pht( + 'Add subscribers rule value should be a list, but is not '. + '(value is "%s").', + phutil_describe_type($value))); + } + } + + protected function assertValidRuleRecordValue($value) { + if (!$value) { + throw new Exception( + pht( + 'You must select at least one user or project tag to add.')); + } + } + + protected function newDropTransactions($object, $value) { + $subscriber_edge_type = PhabricatorObjectHasSubscriberEdgeType::EDGECONST; + + $xaction = $object->getApplicationTransactionTemplate() + ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) + ->setMetadataValue('edge:type', $subscriber_edge_type) + ->setNewValue( + array( + '+' => array_fuse($value), + )); + + return array($xaction); + } + + protected function newDropEffects($value) { + return array( + $this->newEffect() + ->setIcon('fa-briefcase') + ->setContent($this->getRuleViewDescription($value)), + ); + } + + protected function getDefaultValue() { + return null; + } + + protected function getPHUIXControlType() { + return 'tokenizer'; + } + + private function getDatasource() { + $datasource = new PhabricatorProjectOrUserDatasource(); + + if ($this->getViewer()) { + $datasource->setViewer($this->getViewer()); + } + + return $datasource; + } + + protected function getPHUIXControlSpecification() { + $template = id(new AphrontTokenizerTemplateView()) + ->setViewer($this->getViewer()); + + $template_markup = $template->render(); + $datasource = $this->getDatasource(); + + return array( + 'markup' => (string)hsprintf('%s', $template_markup), + 'config' => array( + 'src' => $datasource->getDatasourceURI(), + 'browseURI' => $datasource->getBrowseURI(), + 'placeholder' => $datasource->getPlaceholderText(), + 'limit' => $datasource->getLimit(), + ), + 'value' => null, + ); + } + + public function getRuleViewLabel() { + return pht('Add subscribers'); + } + + public function getRuleViewDescription($value) { + return pht( + 'Add subscribers: %s.', + phutil_tag( + 'strong', + array(), + $this->getViewer() + ->renderHandleList($value) + ->setAsInline(true) + ->render())); + } + + public function getRuleViewIcon($value) { + return id(new PHUIIconView()) + ->setIcon('fa-users', 'green'); + } + + +} diff --git a/src/applications/project/trigger/PhabricatorProjectTriggerRemoveSubscribersRule.php b/src/applications/project/trigger/PhabricatorProjectTriggerRemoveSubscribersRule.php new file mode 100644 index 0000000000..2cd3fadfee --- /dev/null +++ b/src/applications/project/trigger/PhabricatorProjectTriggerRemoveSubscribersRule.php @@ -0,0 +1,127 @@ +getDatasource()->getWireTokens($this->getValue()); + } + + protected function assertValidRuleRecordFormat($value) { + if (!is_array($value)) { + throw new Exception( + pht( + 'Remove subscribers rule value should be a list, but is not '. + '(value is "%s").', + phutil_describe_type($value))); + } + } + + protected function assertValidRuleRecordValue($value) { + if (!$value) { + throw new Exception( + pht( + 'You must select at least one user or project tag to remove.')); + } + } + + protected function newDropTransactions($object, $value) { + $subscriber_edge_type = PhabricatorObjectHasSubscriberEdgeType::EDGECONST; + + $xaction = $object->getApplicationTransactionTemplate() + ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) + ->setMetadataValue('edge:type', $subscriber_edge_type) + ->setNewValue( + array( + '-' => array_fuse($value), + )); + + return array($xaction); + } + + protected function newDropEffects($value) { + return array( + $this->newEffect() + ->setIcon('fa-briefcase') + ->setContent($this->getRuleViewDescription($value)), + ); + } + + protected function getDefaultValue() { + return null; + } + + protected function getPHUIXControlType() { + return 'tokenizer'; + } + + private function getDatasource() { + $datasource = new PhabricatorProjectOrUserDatasource(); + + if ($this->getViewer()) { + $datasource->setViewer($this->getViewer()); + } + + return $datasource; + } + + protected function getPHUIXControlSpecification() { + $template = id(new AphrontTokenizerTemplateView()) + ->setViewer($this->getViewer()); + + $template_markup = $template->render(); + $datasource = $this->getDatasource(); + + return array( + 'markup' => (string)hsprintf('%s', $template_markup), + 'config' => array( + 'src' => $datasource->getDatasourceURI(), + 'browseURI' => $datasource->getBrowseURI(), + 'placeholder' => $datasource->getPlaceholderText(), + 'limit' => $datasource->getLimit(), + ), + 'value' => null, + ); + } + + public function getRuleViewLabel() { + return pht('Remove subscribers'); + } + + public function getRuleViewDescription($value) { + return pht( + 'Remove subscribers: %s.', + phutil_tag( + 'strong', + array(), + $this->getViewer() + ->renderHandleList($value) + ->setAsInline(true) + ->render())); + } + + public function getRuleViewIcon($value) { + return id(new PHUIIconView()) + ->setIcon('fa-users', 'red'); + } + +} diff --git a/src/applications/project/typeahead/PhabricatorProjectLogicalViewerDatasource.php b/src/applications/project/typeahead/PhabricatorProjectLogicalViewerDatasource.php index 9d1a91376a..acd4ea8aa7 100644 --- a/src/applications/project/typeahead/PhabricatorProjectLogicalViewerDatasource.php +++ b/src/applications/project/typeahead/PhabricatorProjectLogicalViewerDatasource.php @@ -35,8 +35,12 @@ final class PhabricatorProjectLogicalViewerDatasource ); } + protected function isFunctionWithLoginRequired($function) { + return true; + } + public function loadResults() { - if ($this->getViewer()->getPHID()) { + if ($this->getViewer()->isLoggedIn()) { $results = array($this->renderViewerProjectsFunctionToken()); } else { $results = array(); @@ -46,7 +50,7 @@ final class PhabricatorProjectLogicalViewerDatasource } protected function canEvaluateFunction($function) { - if (!$this->getViewer()->getPHID()) { + if (!$this->getViewer()->isLoggedIn()) { return false; } diff --git a/src/applications/reference/src/application/ReferenceApplication.php b/src/applications/reference/src/application/ReferenceApplication.php new file mode 100644 index 0000000000..5c92d2131c --- /dev/null +++ b/src/applications/reference/src/application/ReferenceApplication.php @@ -0,0 +1,27 @@ + array( + 'remarkup/' => 'RemarkupReferenceController', + 'cowsay/' => 'CowsayReferenceController', + 'figlet/' => 'FigletReferenceController', + ), + ); + } + +} diff --git a/src/applications/reference/src/controller/CowsayReferenceController.php b/src/applications/reference/src/controller/CowsayReferenceController.php new file mode 100644 index 0000000000..140f7fadfa --- /dev/null +++ b/src/applications/reference/src/controller/CowsayReferenceController.php @@ -0,0 +1,291 @@ +getJokes(); + shuffle($jokes); + + $j = 0; + + foreach ($directories as $directory) { + foreach (Filesystem::listDirectory($directory, false) as $cow_file) { + $matches = null; + if (!preg_match('/^(.*)\.cow$/', $cow_file, $matches)) { + continue; + } + $cow_name = $matches[1]; + $cow_name = phutil_utf8_strtolower($cow_name); + + $joke = $jokes[$j % count($jokes)]; + + $content .= "\n=== ".$cow_name; + $content .= "\n```"; + $content .= "\ncowsay(cow='".$cow_name."'){{{{$joke}}}}"; + $content .= "\n```"; + $content .= "\ncowsay(cow='".$cow_name."'){{{{$joke}}}}"; + $content .= "\n"; + + $j++; + } + } + + $content .= "\n"; + $content .= '== Parameters'; + + $joke = $jokes[$j % count($jokes)]; + $j++; + + $content .= "\n=== eyes"; + $content .= "\n```"; + $content .= "\ncowsay(cow='cow', eyes='-x'){{{{$joke}}}}"; + $content .= "\n```"; + $content .= "\ncowsay(cow='cow', eyes='-x'){{{{$joke}}}}"; + $content .= "\n"; + + $joke = $jokes[$j % count($jokes)]; + $j++; + + $content .= "\n=== think"; + $content .= "\n```"; + $content .= "\ncowsay(cow='cow', think=1){{{{$joke}}}}"; + $content .= "\n```"; + $content .= "\ncowsay(cow='cow', think=1){{{{$joke}}}}"; + $content .= "\n"; + + $joke = $jokes[$j % count($jokes)]; + $j++; + + $content .= "\n=== tongue"; + $content .= "\n```"; + $content .= "\ncowsay(cow='cow', tongue=U){{{{$joke}}}}"; + $content .= "\n```"; + $content .= "\ncowsay(cow='cow', tongue=U){{{{$joke}}}}"; + $content .= "\n"; + + return $content; + } +} diff --git a/src/applications/reference/src/controller/FigletReferenceController.php b/src/applications/reference/src/controller/FigletReferenceController.php new file mode 100644 index 0000000000..9913273d79 --- /dev/null +++ b/src/applications/reference/src/controller/FigletReferenceController.php @@ -0,0 +1,39 @@ +getRequest(); + $viewer = $request->getViewer(); + + $content = $this->getContent(); + + $remarkup_view = id(new PHUIRemarkupView($viewer, $content)) + ->setContextObject($this) + ->setRemarkupOption('uri.same-window', true) + ->setRemarkupOption(PHUIRemarkupView::OPTION_GENERATE_TOC, true) + ->setRemarkupOption(PHUIRemarkupView::OPTION_PRESERVE_LINEBREAKS, false) + ->setGenerateTableOfContents(true); + + $content = $remarkup_view->render(); + + $toc = $remarkup_view->getTableOfContents(); + $toc = $this->formatToc($toc); + + $document = id(new PHUIDocumentView()) + ->addClass('reference-documentation') + ->setToc($toc) + ->appendChild($remarkup_view); + + $crumbs = $this->buildApplicationCrumbs(); + + return $this->newPage() + ->setTitle($this->getTitle()) + ->setCrumbs($crumbs) + ->appendChild($document); + } + + protected function buildApplicationCrumbs() { + $crumbs = array(); + + $application = $this->getCurrentApplication(); + if ($application) { + $icon = $application->getIcon(); + if (!$icon) { + $icon = 'fa-puzzle'; + } + + $crumbs[] = id(new PHUICrumbView()) + ->setName($this->getTitle()) + ->setIcon($icon); + } + + $view = new PHUICrumbsView(); + foreach ($crumbs as $crumb) { + $view->addCrumb($crumb); + } + + return $view; + } + + protected function formatToc($toc) { + + if ($toc) { + $toc = phutil_tag_div('phui-document-toc-content', array( + phutil_tag_div( + 'phui-document-toc-header', + pht('Contents')), + $toc, + )); + } + + return $toc; + } +} diff --git a/src/applications/reference/src/controller/RemarkupReferenceController.php b/src/applications/reference/src/controller/RemarkupReferenceController.php new file mode 100644 index 0000000000..939845d657 --- /dev/null +++ b/src/applications/reference/src/controller/RemarkupReferenceController.php @@ -0,0 +1,762 @@ + Quoted Text + + Use `- ` or `* ` for bulleted lists, and `# ` for numbered lists. + Use ``` or indent two spaces for code. + Use %%% for a literal block. + Use | ... | ... for tables. + += Basic Styling = + +Format **basic text styles** like this: + + **bold text** + //italic text// + `monospaced text` + ##monospaced text## + ~~deleted text~~ + __underlined text__ + !!highlighted text!! + +Those produce **bold text**, //italic text//, `monospaced text`, ##monospaced +text##, ~~deleted text~~, __underlined text__, and !!highlighted text!! +respectively. + += Layout = + +Make **headers** like this: + + = Large Header = + + == Smaller Header == + + ===== Very Small Header ===== + + Alternate Large Header + ====================== + + Alternate Smaller Header + ------------------------ + +You can optionally omit the trailing `=` signs -- that is, these are the same: + + == Smaller Header == + + == Smaller Header + +This produces headers like the ones in this document. Make sure you have an +empty line before and after the header. + +Lists +===== + +Make **lists** by beginning each item with a `-` or a `*`: + + lang=text + - milk + - eggs + - bread + + * duck + * duck + * goose + +This produces a list like this: + + - milk + - eggs + - bread + +(Note that you need to put a space after the `-` or `*`.) + +You can make numbered lists with a `#` instead of `-` or `*`: + + # Articuno + # Zapdos + # Moltres + +Numbered lists can also be started with `1.` or `1)`. If you use a number other +than `1`, the list will start at that number instead. For example, this: + +``` + 200) OK + 201) Created + 202) Accepted +``` + +...produces this: + + 200) OK + 201) Created + 202) Accepted + +You can also nest lists: + +```- Body + - Head + - Arm + - Elbow + - Hand + # Thumb + # Index + # Middle + # Ring + # Pinkie + - Leg + - Knee + - Foot``` + +...which produces: + + - Body + - Head + - Arm + - Elbow + - Hand + # Thumb + # Index + # Middle + # Ring + # Pinkie + - Leg + - Knee + - Foot + +If you prefer, you can indent lists using multiple characters to show indent +depth, like this: + +```- Tree +-- Branch +--- Twig``` + +As expected, this produces: + +- Tree +-- Branch +--- Twig + +You can add checkboxes to items by prefacing them with `[ ]` or `[X]`, like +this: + +``` + - [X] Preheat oven to 450 degrees. + - [ ] Zest 35 lemons. +``` + +When rendered, this produces: + + - [X] Preheat oven to 450 degrees. + - [ ] Zest 35 lemons. + +Code Blocks +=========== + +Make **code blocks** by indenting two spaces: + + f(x, y); + +You can also use three backticks to enclose the code block: + + ```f(x, y); + g(f);``` + +You can specify a language for syntax highlighting with `lang=xxx`: + + lang=text + lang=html + ... + +When using fenced code blocks (triple backticks) you can simply append the +language right after the backticks, like this: ##```html## + +This will highlight the block using a highlighter for that language, if one is +available (in most cases, this means you need to configure Pygments): + + lang=html + ... + +You can also use a `COUNTEREXAMPLE` header to show that a block of code is +bad and shouldn't be copied: + + lang=text + COUNTEREXAMPLE + function f() { + global \$\$variable_variable; + } + +This produces a block like this: + + COUNTEREXAMPLE + function f() { + global \$\$variable_variable; + } + +You can use `lines=N` to limit the vertical size of a chunk of code, and +`name=some_name.ext` to give it a name. For example, this: + + lang=text + lang=html, name=example.html, lines=12, counterexample + ... + +...produces this: + + lang=html, name=example.html, lines=12, counterexample +

Apple

+

Apricot

+

Avocado

+

Banana

+

Bilberry

+

Blackberry

+

Blackcurrant

+

Blueberry

+

Currant

+

Cherry

+

Cherimoya

+

Clementine

+

Date

+

Damson

+

Durian

+

Eggplant

+

Elderberry

+

Feijoa

+

Gooseberry

+

Grape

+

Grapefruit

+

Guava

+

Huckleberry

+

Jackfruit

+

Jambul

+

Kiwi fruit

+

Kumquat

+

Legume

+

Lemon

+

Lime

+

Lychee

+

Mandarine

+

Mango

+

Mangostine

+

Melon

+ + +You can use the `NOTE:`, `WARNING:` or `IMPORTANT:` elements to call attention +to an important idea. + +For example, write this: + +``` +NOTE: Best practices in proton pack operation include not crossing the streams. +``` + +...to produce this: + +NOTE: Best practices in proton pack operation include not crossing the streams. + +Using `WARNING:` or `IMPORTANT:` at the beginning of the line changes the +color of the callout: + +WARNING: Crossing the streams can result in total protonic reversal! + +IMPORTANT: Don't cross the streams! + +In addition, you can use `(NOTE)`, `(WARNING)`, or `(IMPORTANT)` to get the +same effect but without `(NOTE)`, `(WARNING)`, or `(IMPORTANT)` appearing in +the rendered result. For example, this callout uses `(NOTE)`: + +(NOTE) Dr. Egon Spengler is the best resource for additional proton pack + questions. + + +Dividers +======== + +You can divide sections by putting three or more dashes on a line by +themselves. This creates a divider or horizontal rule similar to an `
` +tag, like this one: + +--- + +The dashes need to appear on their own line and be separated from other +content. For example, like this: + +``` +This section will be visually separated. + +--- + +On an entirely different topic, ... +``` + + += Linking URIs = + +URIs are automatically linked: http://phorge.it/ + +If you have a URI with problematic characters in it, like +"`http://comma.org/,`", you can surround it with angle brackets: + + + +This will force the parser to consume the whole URI: + +You can also use create named links, where you choose the displayed text. These +work within Phorge or on the internet at large: + + [[/herald/transcript/ | Herald Transcripts]] + [[http://www.boring-legal-documents.com/ | exciting legal documents]] + +Markdown-style links are also supported: + + [Toil](http://www.trouble.com) + += Linking to Objects = + +You can link to Phorge objects, such as Differential revisions, Diffusion +commits and Maniphest tasks, by mentioning the name of an object: + + D123 # Link to Differential revision D123 + rX123 # Link to SVN commit 123 from the "X" repository + rXaf3192cd5 # Link to Git commit "af3192cd5..." from the "X" repository. + # You must specify at least 7 characters of the hash. + T123 # Link to Maniphest task T123 + +You can also link directly to a comment in Maniphest and Differential (these +can be found on the date stamp of any transaction/comment): + + T123#412 # Link to comment id #412 of task T123 + +See the Phorge configuration setting `remarkup.ignored-object-names` to +modify this behavior. + += Embedding Objects + +You can also generate full-name references to some objects by using braces: + + {D123} # Link to Differential revision D123 with the full name + {T123} # Link to Maniphest task T123 with the full name + +These references will also show when an object changes state (for instance, a +task or revision is closed). Some types of objects support rich embedding. + +== Linking to Project Tags + +Projects can be linked to with the use of a hashtag `#`. This works by default +using the name of the Project (lowercase, underscored). Additionally you +can set multiple additional hashtags by editing the Project details. + + #qa, #quality_assurance + +== Embedding Mocks (Pholio) + +You can embed a Pholio mock by using braces to refer to it: + + {M123} + +By default the first four images from the mock set are displayed. This behavior +can be overridden with the **image** option. With the **image** option you can +provide one or more image IDs to display. + +You can set the image (or images) to display like this: + + {M123, image=12345} + {M123, image=12345 & 6789} + +== Embedding Pastes + +You can embed a Paste using braces: + + {P123} + +You can adjust the embed height with the `lines` option: + + {P123, lines=15} + +You can highlight specific lines with the `highlight` option: + + {P123, highlight=15} + {P123, highlight="23-25, 31"} + +== Embedding Images + +You can embed an image or other file by using braces to refer to it: + + {F123} + +In most interfaces, you can drag-and-drop an image from your computer into the +text area to upload and reference it. + +Some browsers (e.g. Chrome) support uploading an image data just by pasting them +from clipboard into the text area. + +You can set file display options like this: + + {F123, layout=left, float, size=full, alt="a duckling"} + +Valid options for all files are: + + - **layout** left (default), center, right, inline, link (render a link + instead of a thumbnail for images) + - **name** with `layout=link` or for non-images, use this name for the link + text + - **alt** Provide alternate text for assistive technologies. + +Image files support these options: + + - **float** If layout is set to left or right, the image will be floated so + text wraps around it. + - **size** thumb (default), full + - **width** Scale image to a specific width. + - **height** Scale image to a specific height. + +Audio and video files support these options: + + - **media**: Specify the media type as `audio` or `video`. This allows you + to disambiguate how file format which may contain either audio or video + should be rendered. + - **loop**: Loop this media. + - **autoplay**: Automatically begin playing this media. + +== Embedding Countdowns + +You can embed a countdown by using braces: + + {C123} + += Quoting Text = + +To quote text, preface it with an `>`: + + > This is quoted text. + +This appears like this: + +> This is quoted text. + += Embedding Media = + +If you set a configuration flag, you can embed media directly in text: + + - **remarkup.enable-embedded-youtube**: allows you to paste in YouTube videos + and have them render inline. + +This option is disabled by default because it has security and/or +silliness implications. Carefully read the description before enabling it. + += Image Macros = + +You can upload image macros (More Stuff -> Macro) which will replace text +strings with the image you specify. For instance, you could upload an image of a +dancing banana to create a macro named "peanutbutterjellytime", and then any +time you type that string on a separate line it will be replaced with the image +of a dancing banana. + += Memes = + +You can also use image macros in the context of memes. For example, if you +have an image macro named `grumpy`, you can create a meme by doing the +following: + + {meme, src = grumpy, above = toptextgoeshere, below = bottomtextgoeshere} + +By default, the font used to create the text for the meme is `tuffy.ttf`. For +the more authentic feel of `impact.ttf`, you simply have to place the Impact +TrueType font in the Phorge subfolder `/resources/font/`. If Remarkup +detects the presence of `impact.ttf`, it will automatically use it. + += Mentioning Users = + +In Differential and Maniphest, you can mention another user by writing: + + @username + +When you submit your comment, this will add them as a CC on the revision or task +if they aren't already CC'd. + +Icons +===== + +You can add icons to comments using the `{icon ...}` syntax. For example: + + {icon camera} + +This renders: {icon camera} + +You can select a color for icons: + + {icon camera color=blue} + +This renders: {icon camera color=blue} + +For a list of available icons and colors, check the UIExamples application. +(The icons are sourced from +[[ https://fontawesome.com/v4.7.0/icons/ | FontAwesome ]], so you can also +browse the collection there.) + +You can add `spin` to make the icon spin: + + {icon cog spin} + +This renders: {icon cog spin} + + += Phriction Documents = + +You can link to Phriction documents with a name or path: + + Make sure you sign and date your [[legal/Letter of Marque and Reprisal]]! + +By default, the link will render with the document title as the link name. +With a pipe (`|`), you can retitle the link. Use this to mislead your +opponents: + + Check out these [[legal/boring_documents/ | exciting legal documents]]! + +Links to pages which do not exist are shown in red. Links to pages which exist +but which the viewer does not have permission to see are shown with a lock +icon, and the link will not disclose the page title. + +If you begin a link path with `./` or `../`, the remainder of the path will be +evaluated relative to the current wiki page. For example, if you are writing +content for the document `fruit/` a link to `[[./guava]]` is the same as a link +to `[[fruit/guava]]` from elsewhere. + +Relative links may use `../` to transverse up the document tree. From the +`produce/vegetables/` page, you can use `[[../fruit/guava]]` to link to the +`produce/fruit/guava` page. + +Relative links do not work when used outside of wiki pages. For example, +you can't use a relative link in a comment on a task, because there is no +reasonable place for the link to start resolving from. + +When documents are moved, relative links are not automatically updated: they +are preserved as currently written. After moving a document, you may need to +review and adjust any relative links it contains. + + += Literal Blocks = + +To place text in a literal block use `%%%`: + + %%%Text that won't be processed by remarkup + [[http://www.example.com | example]] + %%% + +Remarkup will not process the text inside of literal blocks (other than to +escape HTML and preserve line breaks). + += Tables = + +Remarkup supports simple table syntax. For example, this: + +``` +| Fruit | Color | Price | Peel? +| ----- | ----- | ----- | ----- +| Apple | red | `\$0.93` | no +| Banana | yellow | `\$0.19` | **YES** +``` + +...produces this: + +| Fruit | Color | Price | Peel? +| ----- | ----- | ----- | ----- +| Apple | red | `\$0.93` | no +| Banana | yellow | `\$0.19` | **YES** + +Remarkup also supports a simplified HTML table syntax. For example, this: + +``` + + + + + + + + + + + + + + + + + + + +
FruitColorPricePeel?
Applered`\$0.93`no
Bananayellow`\$0.19`**YES**
+``` + +...produces this: + + + + + + + + + + + + + + + + + + + + +
FruitColorPricePeel?
Applered`\$0.93`no
Bananayellow`\$0.19`**YES**
+ +Some general notes about this syntax: + + - your tags must all be properly balanced; + - your tags must NOT include attributes (`` is OK, `` is + not); + - you can use other Remarkup rules (like **bold**, //italics//, etc.) inside + table cells. + +Navigation Sequences +==================== + +You can use `{nav ...}` to render a stylized navigation sequence when helping +someone to locate something. This can be useful when writing documentation. +For example, you could give someone directions to purchase lemons: + +{nav icon=home, name=Home > +Grocery Store > +Produce Section > +icon=lemon-o, name=Lemons} + +To render this example, use this markup: + +``` +{nav icon=home, name=Home > +Grocery Store > +Produce Section > +icon=lemon-o, name=Lemons} +``` + +In general: + + - Separate sections with `>`. + - Each section can just have a name to add an element to the navigation + sequence, or a list of key-value pairs. + - Supported keys are `icon`, `name`, `type` and `href`. + - The `type` option can be set to `instructions` to indicate that an element + is asking the user to make a choice or follow specific instructions. + +Keystrokes +========== + +You can use `{key ...}` to render a stylized keystroke. For example, this: + +``` +Press {key M} to view the starmap. +``` + +...renders this: + +> Press {key M} to view the starmap. + +You can also render sequences with modifier keys. This: + +``` +Use {key command option shift 3} to take a screenshot. +Press {key down down-right right LP} to activate the hadoken technique. +``` + +...renders this: + +> Use {key command option shift 3} to take a screenshot. +> Press {key down down-right right LP} to activate the hadoken technique. + + +Anchors +======== + +You can use `{anchor #xyz}` to create a document anchor and later link to +it directly with `#xyz` in the URI. + +Headers also automatically create named anchors. + +If you navigate to `#xyz` in your browser location bar, the page will scroll +to the first anchor with "xyz" as a prefix of the anchor name. + + += Fullscreen Mode = + +Remarkup editors provide a fullscreen composition mode. This can make it easier +to edit large blocks of text, or improve focus by removing distractions. You can +exit **Fullscreen** mode by clicking the button again or by pressing escape. +EOTEXT; + + $remarkup_syntax_documentation_providers = id(new PhutilClassMapQuery()) + ->setAncestorClass('RemarkupSyntaxDocumentationProvider') + ->execute(); + + // add custom Remarkup help + $content .= "\r\n\r\n"; + foreach ($remarkup_syntax_documentation_providers as $doc_provider) { + $content .= $doc_provider->getDocumentation()."\r\n\r\n"; + } + + return $content; + } +} diff --git a/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php b/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php index 48eabf1545..8294f6832e 100644 --- a/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php +++ b/src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php @@ -341,8 +341,8 @@ final class PhabricatorRepositoryPullLocalDaemon * With the `$consume` flag, an internal cursor will also be incremented so * that these messages are not returned by subsequent calls. * - * @param bool Pass `true` to consume these messages, so the process will - * not see them again. + * @param bool? $consume Pass `true` to consume these messages, so the + * process will not see them again. * @return list Pending update messages. * * @task pull diff --git a/src/applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php b/src/applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php index 8f5b346ecd..7d66868f50 100644 --- a/src/applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php +++ b/src/applications/repository/engine/PhabricatorRepositoryDiscoveryEngine.php @@ -538,7 +538,7 @@ final class PhabricatorRepositoryDiscoveryEngine * * @task internal * - * @param list List of refs. + * @param list $refs List of refs. * @return list Sorted list of refs. */ private function sortRefs(array $refs) { diff --git a/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php b/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php index 5bcf4554fd..af4cd95d0a 100644 --- a/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php +++ b/src/applications/repository/engine/PhabricatorRepositoryPullEngine.php @@ -602,22 +602,6 @@ final class PhabricatorRepositoryPullEngine return $map; } - private function loadGitLocalRefs(PhabricatorRepository $repository) { - $refs = id(new DiffusionLowLevelGitRefQuery()) - ->setRepository($repository) - ->execute(); - - $map = array(); - foreach ($refs as $ref) { - $fields = $ref->getRawFields(); - $map[idx($fields, 'refname')] = $ref->getCommitIdentifier(); - } - - ksort($map); - - return $map; - } - private function logRefDifferences(array $remote, array $local) { $all = $local + $remote; @@ -756,7 +740,7 @@ final class PhabricatorRepositoryPullEngine * error message. To prevent this, censor response bodies out of error * messages. * - * @param string Uncensored Mercurial command output. + * @param string $message Uncensored Mercurial command output. * @return string Censored Mercurial command output. */ private function censorMercurialErrorMessage($message) { diff --git a/src/applications/repository/engine/PhabricatorRepositoryRefEngine.php b/src/applications/repository/engine/PhabricatorRepositoryRefEngine.php index 60a96578a3..6cb9f27384 100644 --- a/src/applications/repository/engine/PhabricatorRepositoryRefEngine.php +++ b/src/applications/repository/engine/PhabricatorRepositoryRefEngine.php @@ -233,7 +233,7 @@ final class PhabricatorRepositoryRefEngine * point at commits which no longer exist. This can make commands issued later * fail. See T5839 for discussion. * - * @param list List of commit identifiers. + * @param list $identifiers List of commit identifiers. * @return list List with nonexistent identifiers removed. */ private function removeMissingCommits(array $identifiers) { diff --git a/src/applications/repository/graphcache/PhabricatorRepositoryGraphCache.php b/src/applications/repository/graphcache/PhabricatorRepositoryGraphCache.php index c4adc61ab0..2d9c36fae7 100644 --- a/src/applications/repository/graphcache/PhabricatorRepositoryGraphCache.php +++ b/src/applications/repository/graphcache/PhabricatorRepositoryGraphCache.php @@ -64,10 +64,10 @@ final class PhabricatorRepositoryGraphCache extends Phobject { /** * Search the graph cache for the most modification to a path. * - * @param int The commit ID to search ancestors of. - * @param int The path ID to search for changes to. - * @param float Maximum number of seconds to spend trying to satisfy this - * query using the graph cache. By default, `0.5` (500ms). + * @param int $commit_id The commit ID to search ancestors of. + * @param int $path_id The path ID to search for changes to. + * @param float $time Maximum number of seconds to spend trying to satisfy + * this query using the graph cache. By default `0.5` (500ms). * @return mixed Commit ID, or `null` if no ancestors exist, or `false` if * the graph cache was unable to determine the answer. * @task query @@ -189,7 +189,7 @@ final class PhabricatorRepositoryGraphCache extends Phobject { /** * Get the bucket key for a given commit ID. * - * @param int Commit ID. + * @param int $commit_id Commit ID. * @return int Bucket key. * @task cache */ @@ -201,7 +201,7 @@ final class PhabricatorRepositoryGraphCache extends Phobject { /** * Get the cache key for a given bucket key (from @{method:getBucketKey}). * - * @param int Bucket key. + * @param int $bucket_key Bucket key. * @return string Cache key. * @task cache */ @@ -235,9 +235,10 @@ final class PhabricatorRepositoryGraphCache extends Phobject { * Normally, this operates as a readthrough cache call. It can also be used * to force a cache update by passing the existing data to `$rebuild_data`. * - * @param int Bucket key, from @{method:getBucketKey}. - * @param mixed Current data, to force a cache rebuild of this bucket. - * @return array Data from the cache. + * @param int $bucket_key Bucket key, from @{method:getBucketKey}. + * @param mixed? $rebuild_data Current data, to force a cache rebuild of + * this bucket. + * @return array Data from the cache. * @task cache */ private function getBucketData($bucket_key, $rebuild_data = null) { @@ -287,9 +288,9 @@ final class PhabricatorRepositoryGraphCache extends Phobject { /** * Rebuild a cache bucket, amending existing data if available. * - * @param int Bucket key, from @{method:getBucketKey}. - * @param array Existing bucket data. - * @return array Rebuilt bucket data. + * @param int $bucket_key Bucket key, from @{method:getBucketKey}. + * @param array $current_data Existing bucket data. + * @return array Rebuilt bucket data. * @task cache */ private function rebuildBucket($bucket_key, array $current_data) { diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php index b2855c0c06..b9aab043ad 100644 --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -1288,7 +1288,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO /** * Determine if a protocol is SSH or SSH-like. * - * @param string A protocol string, like "http" or "ssh". + * @param string $protocol A protocol string, like "http" or "ssh". * @return bool True if the protocol is SSH-like. * @task uri */ @@ -1701,7 +1701,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO * 2037). We adjust the pull frequency based on when the most recent commit * occurred. * - * @param int The minimum update interval to use, in seconds. + * @param int? $minimum The minimum update interval to use, in seconds. * @return int Repository update interval, in seconds. */ public function loadUpdateInterval($minimum = 15) { @@ -1836,8 +1836,8 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO * with repository services. This method provides lower-level resolution of * services, returning raw URIs. * - * @param PhabricatorUser Viewing user. - * @param map Constraints on selectable services. + * @param PhabricatorUser $viewer Viewing user. + * @param map $options Constraints on selectable services. * @return string|null URI, or `null` for local repositories. */ public function getAlmanacServiceURI( @@ -2168,8 +2168,8 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO * * For lower-level service resolution, see @{method:getAlmanacServiceURI}. * - * @param PhabricatorUser Viewing user. - * @param bool `true` to throw if a client would be returned. + * @param PhabricatorUser $viewer Viewing user. + * @param bool? $never_proxy `true` to throw if a client would be returned. * @return ConduitClient|null Client, or `null` for local repositories. */ public function newConduitClient( diff --git a/src/applications/search/controller/PhabricatorApplicationSearchController.php b/src/applications/search/controller/PhabricatorApplicationSearchController.php index d8f53538e4..5178a1a1d9 100644 --- a/src/applications/search/controller/PhabricatorApplicationSearchController.php +++ b/src/applications/search/controller/PhabricatorApplicationSearchController.php @@ -346,6 +346,15 @@ final class PhabricatorApplicationSearchController $body[] = $pager_box; } } + } catch (PhabricatorTypeaheadLoginRequiredException $ex) { + + // A specific token requires login. Show login page. + $auth_class = PhabricatorAuthApplication::class; + $auth_application = PhabricatorApplication::getByClass($auth_class); + $login_controller = new PhabricatorAuthStartController(); + $this->setCurrentApplication($auth_application); + return $this->delegateToController($login_controller); + } catch (PhabricatorTypeaheadInvalidTokenException $ex) { $exec_errors[] = pht( 'This query specifies an invalid parameter. Review the '. diff --git a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php index cad397b0ac..1979ec092a 100644 --- a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php +++ b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php @@ -13,6 +13,7 @@ * @task read Reading Utilities * @task exec Paging and Executing Queries * @task render Rendering Results + * @task custom Custom Fields */ abstract class PhabricatorApplicationSearchEngine extends Phobject { @@ -125,7 +126,7 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { /** * Create a saved query object from the request. * - * @param AphrontRequest The search request. + * @param AphrontRequest $request The search request. * @return PhabricatorSavedQuery */ public function buildSavedQueryFromRequest(AphrontRequest $request) { @@ -146,7 +147,7 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { /** * Executes the saved query. * - * @param PhabricatorSavedQuery The saved query to operate on. + * @param PhabricatorSavedQuery $original The saved query to operate on. * @return PhabricatorQuery The result of the query. */ public function buildQueryFromSavedQuery(PhabricatorSavedQuery $original) { @@ -200,7 +201,7 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { * hook to keep old queries working the way users expect, by reading, * adjusting, and overwriting parameters. * - * @param PhabricatorSavedQuery Saved query which will be executed. + * @param PhabricatorSavedQuery $saved Saved query which will be executed. * @return void */ protected function willUseSavedQuery(PhabricatorSavedQuery $saved) { @@ -214,8 +215,8 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { /** * Builds the search form using the request. * - * @param AphrontFormView Form to populate. - * @param PhabricatorSavedQuery The query from which to build the form. + * @param AphrontFormView $form Form to populate. + * @param PhabricatorSavedQuery $saved Query from which to build the form. * @return void */ public function buildSearchForm( @@ -399,7 +400,7 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { * Return an application URI corresponding to the results page of a query. * Normally, this is something like `/application/query/QUERYKEY/`. * - * @param string The query key to build a URI for. + * @param string $query_key The query key to build a URI for. * @return string URI where the query can be executed. * @task uri */ @@ -729,9 +730,9 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { * links to pages (like "alincoln's open revisions") without needing to make * API calls. * - * @param AphrontRequest Request to read user PHIDs from. - * @param string Key to read in the request. - * @param list Other permitted PHID types. + * @param AphrontRequest $request Request to read user PHIDs from. + * @param string $key Key to read in the request. + * @param list? $allow_types Other permitted PHID types. * @return list List of user PHIDs and selector functions. * @task read */ @@ -781,8 +782,8 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { /** * Read a list of subscribers from a request in a flexible way. * - * @param AphrontRequest Request to read PHIDs from. - * @param string Key to read in the request. + * @param AphrontRequest $request Request to read PHIDs from. + * @param string $key Key to read in the request. * @return list List of object PHIDs. * @task read */ @@ -804,9 +805,10 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { * comma-delimited forms. Objects can be specified either by PHID or by * object name. * - * @param AphrontRequest Request to read PHIDs from. - * @param string Key to read in the request. - * @param list Optional, list of permitted PHID types. + * @param AphrontRequest $request Request to read PHIDs from. + * @param string $key Key to read in the request. + * @param list? $allow_types Optional, list of permitted PHID + * types. * @return list List of object PHIDs. * * @task read @@ -852,8 +854,8 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { * This provides flexibility when constructing URIs, especially from external * sources. * - * @param AphrontRequest Request to read strings from. - * @param string Key to read in the request. + * @param AphrontRequest $request Request to read strings from. + * @param string $key Key to read in the request. * @return list List of values. */ protected function readListFromRequest( @@ -1071,7 +1073,7 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { if ($phids) { $handles = id(new PhabricatorHandleQuery()) ->setViewer($this->requireViewer()) - ->witHPHIDs($phids) + ->withPHIDs($phids) ->execute(); } else { $handles = array(); @@ -1453,6 +1455,12 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { return $attachments; } + /** + * Render a content body (if available) to onboard new users. + * This body is usually visible when you have no elements in a list, + * or when you force the rendering on a list with the `?nux=1` URL. + * @return wild|PhutilSafeHTML|null + */ final public function renderNewUserView() { $body = $this->getNewUserBody(); @@ -1463,6 +1471,12 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { return $body; } + /** + * Get a content body to onboard new users. + * Traditionally this content is shown from an empty list, to explain + * what a certain entity does, and how to create a new one. + * @return wild|PhutilSafeHTML|null + */ protected function getNewUserHeader() { return null; } @@ -1626,4 +1640,33 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject { return $supported; } + /** + * Load from object and from storage, and updates Custom Fields instances + * that are attached to each object. + * + * @return mapPhabricatorCustomFieldList> of loaded fields. + * @task custom + */ + protected function loadCustomFields(array $objects, $role) { + assert_instances_of($objects, 'PhabricatorCustomFieldInterface'); + + $query = new PhabricatorCustomFieldStorageQuery(); + $lists = array(); + + foreach ($objects as $object) { + $field_list = PhabricatorCustomField::getObjectFields($object, $role); + $field_list->readFieldsFromObject($object); + foreach ($field_list->getFields() as $field) { + // TODO move $viewer into PhabricatorCustomFieldStorageQuery + $field->setViewer($this->viewer); + } + $lists[$object->getPHID()] = $field_list; + $query->addFields($field_list->getFields()); + } + // This updates the field_list objects. + $query->execute(); + + return $lists; + } + } diff --git a/src/applications/search/engine/PhabricatorProfileMenuEngine.php b/src/applications/search/engine/PhabricatorProfileMenuEngine.php index 8799ae8f0b..7fbfe1c2da 100644 --- a/src/applications/search/engine/PhabricatorProfileMenuEngine.php +++ b/src/applications/search/engine/PhabricatorProfileMenuEngine.php @@ -54,6 +54,7 @@ abstract class PhabricatorProfileMenuEngine extends Phobject { $custom_phid = $this->getCustomPHID(); break; case self::MODE_GLOBAL: + default: $custom_phid = null; break; } @@ -87,6 +88,9 @@ abstract class PhabricatorProfileMenuEngine extends Phobject { abstract public function getItemURI($path); abstract protected function isMenuEngineConfigurable(); + /** + * @return array of PhabricatorProfileMenuItemConfiguration objects + */ abstract protected function getBuiltinProfileItems($object); protected function getBuiltinCustomProfileItems( @@ -465,15 +469,6 @@ abstract class PhabricatorProfileMenuEngine extends Phobject { return $map; } - private function validateNavigationMenuItem($item) { - if (!($item instanceof PHUIListItemView)) { - throw new Exception( - pht( - 'Expected buildNavigationMenuItems() to return a list of '. - 'PHUIListItemView objects, but got a surprise.')); - } - } - public function getConfigureURI() { $mode = $this->getEditMode(); diff --git a/src/applications/search/field/PhabricatorSearchField.php b/src/applications/search/field/PhabricatorSearchField.php index 36db0523b7..7b420ac8e6 100644 --- a/src/applications/search/field/PhabricatorSearchField.php +++ b/src/applications/search/field/PhabricatorSearchField.php @@ -33,7 +33,7 @@ abstract class PhabricatorSearchField extends Phobject { * The key should be a short, unique (within a search engine) string which * does not contain any special characters. * - * @param string Unique key which identifies the field. + * @param string $key Unique key which identifies the field. * @return this * @task config */ @@ -59,7 +59,7 @@ abstract class PhabricatorSearchField extends Phobject { * * This should be a short text string, like "Reviewers" or "Colors". * - * @param string Short, human-readable field label. + * @param string $label Short, human-readable field label. * @return this * task config */ @@ -86,7 +86,7 @@ abstract class PhabricatorSearchField extends Phobject { * Engines do not need to do this explicitly; it will be done on their * behalf by the caller. * - * @param PhabricatorUser Viewer. + * @param PhabricatorUser $viewer Viewer. * @return this * @task config */ @@ -115,7 +115,7 @@ abstract class PhabricatorSearchField extends Phobject { * an alias like `authors` to let users write `&authors=alincoln` instead of * `&authorPHIDs=alincoln`. This is a little easier to use. * - * @param list List of aliases for this field. + * @param list $aliases List of aliases for this field. * @return this * @task config */ @@ -142,7 +142,7 @@ abstract class PhabricatorSearchField extends Phobject { * This can allow you to choose a more usable key for API endpoints. * If no key is provided, the main key is used. * - * @param string Alternate key for Conduit. + * @param string $conduit_key Alternate key for Conduit. * @return this * @task config */ @@ -170,7 +170,7 @@ abstract class PhabricatorSearchField extends Phobject { /** * Set a human-readable description for this field. * - * @param string Human-readable description. + * @param string $description Human-readable description. * @return this * @task config */ @@ -194,7 +194,7 @@ abstract class PhabricatorSearchField extends Phobject { /** * Hide this field from the web UI. * - * @param bool True to hide the field from the web UI. + * @param bool $is_hidden True to hide the field from the web UI. * @return this * @task config */ @@ -400,8 +400,8 @@ abstract class PhabricatorSearchField extends Phobject { * This provides flexibility when constructing URIs, especially from external * sources. * - * @param AphrontRequest Request to read strings from. - * @param string Key to read in the request. + * @param AphrontRequest $request Request to read strings from. + * @param string $key Key to read in the request. * @return list List of values. * @task utility */ diff --git a/src/applications/search/fulltextstorage/PhabricatorFulltextStorageEngine.php b/src/applications/search/fulltextstorage/PhabricatorFulltextStorageEngine.php index ba019ea593..3745bbf35e 100644 --- a/src/applications/search/fulltextstorage/PhabricatorFulltextStorageEngine.php +++ b/src/applications/search/fulltextstorage/PhabricatorFulltextStorageEngine.php @@ -47,7 +47,7 @@ abstract class PhabricatorFulltextStorageEngine extends Phobject { /** * Update the index for an abstract document. * - * @param PhabricatorSearchAbstractDocument Document to update. + * @param PhabricatorSearchAbstractDocument $document Document to update. * @return void */ abstract public function reindexAbstractDocument( @@ -56,7 +56,7 @@ abstract class PhabricatorFulltextStorageEngine extends Phobject { /** * Execute a search query. * - * @param PhabricatorSavedQuery A query to execute. + * @param PhabricatorSavedQuery $query A query to execute. * @return list A list of matching PHIDs. */ abstract public function executeSearch(PhabricatorSavedQuery $query); diff --git a/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php index 50d64072f5..6e84e28622 100644 --- a/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorEmailPreferencesSettingsPanel.php @@ -97,14 +97,15 @@ final class PhabricatorEmailPreferencesSettingsPanel 'count' => 0, 'name' => $name, ); + } else { + $all_tags[$tag]['count']++; } - $all_tags[$tag]['count']; } } $common_tags = array(); foreach ($all_tags as $tag => $info) { - if ($info['count'] > 1) { + if ($info['count'] > 0) { $common_tags[$tag] = $info['name']; } } diff --git a/src/applications/settings/panel/PhabricatorSettingsPanel.php b/src/applications/settings/panel/PhabricatorSettingsPanel.php index e2efd92093..ac92a134c6 100644 --- a/src/applications/settings/panel/PhabricatorSettingsPanel.php +++ b/src/applications/settings/panel/PhabricatorSettingsPanel.php @@ -222,7 +222,7 @@ abstract class PhabricatorSettingsPanel extends Phobject { * Generally, render your settings panel by returning a form, then return * a redirect when the user saves settings. * - * @param AphrontRequest Incoming request. + * @param AphrontRequest $request Incoming request. * @return wild Response to request, either as an * @{class:AphrontResponse} or something which can * be composed into a @{class:AphrontView}. @@ -234,7 +234,7 @@ abstract class PhabricatorSettingsPanel extends Phobject { /** * Get the URI for this panel. * - * @param string? Optional path to append. + * @param string? $path Optional path to append. * @return string Relative URI for the panel. * @task panel */ diff --git a/src/applications/settings/query/PhabricatorUserPreferencesQuery.php b/src/applications/settings/query/PhabricatorUserPreferencesQuery.php index 5fa9b89bdd..5795b78f27 100644 --- a/src/applications/settings/query/PhabricatorUserPreferencesQuery.php +++ b/src/applications/settings/query/PhabricatorUserPreferencesQuery.php @@ -49,7 +49,8 @@ final class PhabricatorUserPreferencesQuery * If no settings exist for a user, a new empty settings object with * appropriate defaults is returned. * - * @param bool True to generate synthetic preferences for missing users. + * @param bool $synthetic True to generate synthetic preferences for missing + * users. */ public function needSyntheticPreferences($synthetic) { $this->synthetic = $synthetic; diff --git a/src/applications/settings/storage/PhabricatorUserPreferences.php b/src/applications/settings/storage/PhabricatorUserPreferences.php index 6cceff57de..f004361a29 100644 --- a/src/applications/settings/storage/PhabricatorUserPreferences.php +++ b/src/applications/settings/storage/PhabricatorUserPreferences.php @@ -119,7 +119,7 @@ final class PhabricatorUserPreferences /** * Load or create a preferences object for the given user. * - * @param PhabricatorUser User to load or create preferences for. + * @param PhabricatorUser $user User to load or create preferences for. */ public static function loadUserPreferences(PhabricatorUser $user) { return id(new PhabricatorUserPreferencesQuery()) @@ -134,7 +134,7 @@ final class PhabricatorUserPreferences * * If no global preferences exist, an empty preferences object is returned. * - * @param PhabricatorUser Viewing user. + * @param PhabricatorUser $viewer Viewing user. */ public static function loadGlobalPreferences(PhabricatorUser $viewer) { $global = id(new PhabricatorUserPreferencesQuery()) diff --git a/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php b/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php index 7ed83fb13f..1844a5f03c 100644 --- a/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php +++ b/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php @@ -154,7 +154,7 @@ final class PhabricatorSlowvotePollController return id(new PhabricatorApplicationTransactionCommentView()) ->setUser($viewer) - ->setObjectPHID($poll->getPHID()) + ->setObject($poll) ->setDraft($draft) ->setHeaderText($add_comment_header) ->setAction($this->getApplicationURI('/comment/'.$poll->getID().'/')) diff --git a/src/applications/spaces/query/PhabricatorSpacesNamespaceQuery.php b/src/applications/spaces/query/PhabricatorSpacesNamespaceQuery.php index 6703ed664e..6de31ff5c1 100644 --- a/src/applications/spaces/query/PhabricatorSpacesNamespaceQuery.php +++ b/src/applications/spaces/query/PhabricatorSpacesNamespaceQuery.php @@ -212,7 +212,7 @@ final class PhabricatorSpacesNamespaceQuery * This is intended to simplify performing a bunch of redundant checks; you * can intentionally pass any value in (including `null`). * - * @param wild + * @param wild $object * @return phid|null */ public static function getObjectSpacePHID($object) { diff --git a/src/applications/subscriptions/editor/PhabricatorSubscriptionsEditor.php b/src/applications/subscriptions/editor/PhabricatorSubscriptionsEditor.php index 3ee4b5f6e8..a36fed1c5d 100644 --- a/src/applications/subscriptions/editor/PhabricatorSubscriptionsEditor.php +++ b/src/applications/subscriptions/editor/PhabricatorSubscriptionsEditor.php @@ -18,7 +18,7 @@ final class PhabricatorSubscriptionsEditor extends PhabricatorEditor { * (or been subscribed) to the object, and will be added even if they * had previously unsubscribed. * - * @param list List of PHIDs to explicitly subscribe. + * @param list $phids List of PHIDs to explicitly subscribe. * @return this */ public function subscribeExplicit(array $phids) { @@ -32,7 +32,7 @@ final class PhabricatorSubscriptionsEditor extends PhabricatorEditor { * implicitly subscribes them (e.g., adding a comment) but it will be * suppressed if they've previously unsubscribed from the object. * - * @param list List of PHIDs to implicitly subscribe. + * @param list $phids List of PHIDs to implicitly subscribe. * @return this */ public function subscribeImplicit(array $phids) { @@ -45,7 +45,7 @@ final class PhabricatorSubscriptionsEditor extends PhabricatorEditor { * Unsubscribe PHIDs and mark them as unsubscribed, so implicit subscriptions * will not resubscribe them. * - * @param list List of PHIDs to unsubscribe. + * @param list $phids List of PHIDs to unsubscribe. * @return this */ public function unsubscribe(array $phids) { diff --git a/src/applications/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php b/src/applications/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php index 2077160b7c..3d43d972f6 100644 --- a/src/applications/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php +++ b/src/applications/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php @@ -94,26 +94,33 @@ final class PhabricatorSubscriptionsUIEventListener } } - $mute_action = id(new PhabricatorActionView()) - ->setWorkflow(true) - ->setHref('/subscriptions/mute/'.$object->getPHID().'/') - ->setDisabled(!$user_phid); - - if (!$is_muted) { - $mute_action - ->setName(pht('Mute Notifications')) - ->setIcon('fa-volume-up'); - } else { - $mute_action - ->setName(pht('Unmute Notifications')) - ->setIcon('fa-volume-off') - ->setColor(PhabricatorActionView::RED); - } - - $actions = $event->getValue('actions'); $actions[] = $sub_action; - $actions[] = $mute_action; + + // Hide "Mute Notifications" in sidebar if not supported by Editor - T15378 + $supported_editor_transaction_types = + array_fill_keys($object->getApplicationTransactionEditor() + ->getTransactionTypesForObject($object), true); + if (array_key_exists(PhabricatorTransactions::TYPE_EDGE, + $supported_editor_transaction_types)) { + $mute_action = id(new PhabricatorActionView()) + ->setWorkflow(true) + ->setHref('/subscriptions/mute/'.$object->getPHID().'/') + ->setDisabled(!$user_phid); + + if (!$is_muted) { + $mute_action + ->setName(pht('Mute Notifications')) + ->setIcon('fa-volume-up'); + } else { + $mute_action + ->setName(pht('Unmute Notifications')) + ->setIcon('fa-volume-off') + ->setColor(PhabricatorActionView::RED); + } + $actions[] = $mute_action; + } + $event->setValue('actions', $actions); } diff --git a/src/applications/subscriptions/interface/PhabricatorSubscribableInterface.php b/src/applications/subscriptions/interface/PhabricatorSubscribableInterface.php index 1af9e7107f..6528774ee8 100644 --- a/src/applications/subscriptions/interface/PhabricatorSubscribableInterface.php +++ b/src/applications/subscriptions/interface/PhabricatorSubscribableInterface.php @@ -8,7 +8,8 @@ interface PhabricatorSubscribableInterface { * irrevocably a subscriber). This will, e.g., cause the UI to render * "Automatically Subscribed" instead of "Subscribe". * - * @param PHID PHID (presumably a user) to test for automatic subscription. + * @param PHID $phid PHID (presumably a user) to test for automatic + * subscription. * @return bool True if the object/user is automatically subscribed. */ public function isAutomaticallySubscribed($phid); diff --git a/src/applications/system/engine/PhabricatorSystemActionEngine.php b/src/applications/system/engine/PhabricatorSystemActionEngine.php index 6d6f9eacfd..106ef05e48 100644 --- a/src/applications/system/engine/PhabricatorSystemActionEngine.php +++ b/src/applications/system/engine/PhabricatorSystemActionEngine.php @@ -33,9 +33,9 @@ final class PhabricatorSystemActionEngine extends Phobject { * If any actor is exceeding their rate limit, this method throws a * @{class:PhabricatorSystemActionRateLimitException}. * - * @param list List of actors. - * @param PhabricatorSystemAction Action being taken. - * @param float Score or credit, see above. + * @param list $actors List of actors. + * @param PhabricatorSystemAction $action Action being taken. + * @param float $score Score or credit, see above. * @return void */ public static function willTakeAction( @@ -174,7 +174,7 @@ final class PhabricatorSystemActionEngine extends Phobject { * Reset all action counts for actions taken by some set of actors in the * previous action window. * - * @param list Actors to reset counts for. + * @param list $actors Actors to reset counts for. * @return int Number of actions cleared. */ public static function resetActions(array $actors) { diff --git a/src/applications/transactions/bulk/management/PhabricatorBulkManagementExportWorkflow.php b/src/applications/transactions/bulk/management/PhabricatorBulkManagementExportWorkflow.php index d80534ce73..87de9ee37b 100644 --- a/src/applications/transactions/bulk/management/PhabricatorBulkManagementExportWorkflow.php +++ b/src/applications/transactions/bulk/management/PhabricatorBulkManagementExportWorkflow.php @@ -8,7 +8,7 @@ final class PhabricatorBulkManagementExportWorkflow ->setName('export') ->setExamples('**export** [options]') ->setSynopsis( - pht('Export data to a flat file (JSON, CSV, Excel, etc).')) + pht('Export data to a flat file (JSON, CSV, Excel, etc.).')) ->setArguments( array( array( diff --git a/src/applications/transactions/constants/PhabricatorTransactions.php b/src/applications/transactions/constants/PhabricatorTransactions.php index 3e31bdb1db..1f896d662d 100644 --- a/src/applications/transactions/constants/PhabricatorTransactions.php +++ b/src/applications/transactions/constants/PhabricatorTransactions.php @@ -41,4 +41,19 @@ final class PhabricatorTransactions extends Phobject { ); } + /** + * Find the first transaction that matches a type. + * @param array $xactions + * @param string $type + * @return PhabricatorTransactions|null + */ + public static function findOneByType($xactions, $type) { + foreach ($xactions as $xaction) { + if ($xaction->getTransactionType() === $type) { + return $xaction; + } + } + return null; + } + } diff --git a/src/applications/transactions/editengine/PhabricatorEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php index f70e172a99..6b3aae2790 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngine.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php @@ -339,7 +339,17 @@ abstract class PhabricatorEditEngine return null; } - + /** + * Set default placeholder plain text in the comment textarea of the engine. + * To be overwritten by conditions defined in the child EditEngine class. + * + * @param object $object Object in which the comment textarea is displayed. + * @return string Placeholder text to display in the comment textarea. + * @task text + */ + public function getCommentFieldPlaceholderText($object) { + return ''; + } /** * Return a human-readable header describing what this engine is used to do, @@ -668,7 +678,7 @@ abstract class PhabricatorEditEngine * Initialize a new object for object creation via Conduit. * * @return object Newly initialized object. - * @param list Raw transactions. + * @param list $raw_xactions Raw transactions. * @task load */ protected function newEditableObjectFromConduit(array $raw_xactions) { @@ -688,7 +698,7 @@ abstract class PhabricatorEditEngine /** * Flag this workflow as a create or edit. * - * @param bool True if this is a create workflow. + * @param bool $is_create True if this is a create workflow. * @return this * @task load */ @@ -702,9 +712,9 @@ abstract class PhabricatorEditEngine * Try to load an object by ID, PHID, or monogram. This is done primarily * to make Conduit a little easier to use. * - * @param wild ID, PHID, or monogram. - * @param list List of required capability constants, or omit for - * defaults. + * @param wild $identifier ID, PHID, or monogram. + * @param list? $capabilities List of required capability constants, + * or omit for defaults. * @return object Corresponding editable object. * @task load */ @@ -782,9 +792,9 @@ abstract class PhabricatorEditEngine /** * Load an object by ID. * - * @param int Object ID. - * @param list List of required capability constants, or omit for - * defaults. + * @param int $id Object ID. + * @param list? $capabilities List of required capability constants, + * or omit for defaults. * @return object|null Object, or null if no such object exists. * @task load */ @@ -799,9 +809,9 @@ abstract class PhabricatorEditEngine /** * Load an object by PHID. * - * @param phid Object PHID. - * @param list List of required capability constants, or omit for - * defaults. + * @param phid $phid Object PHID. + * @param list? $capabilities List of required capability constants, + * or omit for defaults. * @return object|null Object, or null if no such object exists. * @task load */ @@ -816,9 +826,9 @@ abstract class PhabricatorEditEngine /** * Load an object given a configured query. * - * @param PhabricatorPolicyAwareQuery Configured query. - * @param list List of required capability constants, or omit for - * defaults. + * @param PhabricatorPolicyAwareQuery $query Configured query. + * @param list? $capabilities List of required capability constants, + * or omit for defaults. * @return object|null Object, or null if no such object exists. * @task load */ @@ -850,7 +860,7 @@ abstract class PhabricatorEditEngine /** * Verify that an object is appropriate for editing. * - * @param wild Loaded value. + * @param wild $object Loaded value. * @return void * @task load */ @@ -1664,10 +1674,12 @@ abstract class PhabricatorEditEngine $view = id(new PhabricatorApplicationTransactionCommentView()) ->setUser($viewer) - ->setObjectPHID($object_phid) ->setHeaderText($header_text) ->setAction($comment_uri) + ->setRequestURI(new PhutilURI($this->getObjectViewURI($object))) ->setRequiresMFA($requires_mfa) + ->setObject($object) + ->setEditEngine($this) ->setSubmitButtonName($button_text); $draft = PhabricatorVersionedDraft::loadDraft( @@ -1755,7 +1767,7 @@ abstract class PhabricatorEditEngine /** * Respond to a request for documentation on HTTP parameters. * - * @param object Editable object. + * @param object $object Editable object. * @return AphrontResponse Response object. * @task http */ @@ -1909,7 +1921,7 @@ abstract class PhabricatorEditEngine $comment_text = $request->getStr('comment'); $comment_metadata = $request->getStr('comment_metadata'); - if (strlen($comment_metadata)) { + if (phutil_nonempty_string($comment_metadata)) { $comment_metadata = phutil_json_decode($comment_metadata); } @@ -2009,7 +2021,7 @@ abstract class PhabricatorEditEngine $xactions[] = $xaction; } - if (strlen($comment_text) || !$xactions) { + if (phutil_nonempty_string($comment_text) || !$xactions) { $xactions[] = id(clone $template) ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) ->setMetadataValue('remarkup.control', $comment_metadata) @@ -2232,10 +2244,10 @@ abstract class PhabricatorEditEngine * Generate transactions which can be applied from edit actions in a Conduit * request. * - * @param ConduitAPIRequest The request. - * @param list Raw conduit transactions. - * @param list Supported edit types. - * @param PhabricatorApplicationTransaction Template transaction. + * @param ConduitAPIRequest $request The request. + * @param list $xactions Raw conduit transactions. + * @param list $types Supported edit types. + * @param PhabricatorApplicationTransaction $template Template transaction. * @return list Generated transactions. * @task conduit */ diff --git a/src/applications/transactions/editfield/PhabricatorEditField.php b/src/applications/transactions/editfield/PhabricatorEditField.php index 4c72d63214..4e17bf006c 100644 --- a/src/applications/transactions/editfield/PhabricatorEditField.php +++ b/src/applications/transactions/editfield/PhabricatorEditField.php @@ -573,8 +573,8 @@ abstract class PhabricatorEditField extends Phobject { * Most fields do not need to store these values or deal with initial value * handling. * - * @param AphrontRequest Request to read from. - * @param string Key to read. + * @param AphrontRequest $request Request to read from. + * @param string $key Key to read. * @return wild Value read from request. */ protected function getInitialValueFromSubmit(AphrontRequest $request, $key) { diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php index 1d9d3282b7..b759e58c1d 100644 --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -140,7 +140,8 @@ abstract class PhabricatorApplicationTransactionEditor * nothing (e.g., empty comment with a status change that has already been * performed by another user). * - * @param bool True to drop transactions without effect and continue. + * @param bool $continue True to drop transactions without effect and + * continue. * @return this */ public function setContinueOnNoEffect($continue) { @@ -168,8 +169,8 @@ abstract class PhabricatorApplicationTransactionEditor * (like the priority, batch, and merge editors in Maniphest), these * operations can continue to function even if an object is outdated. * - * @param bool True to continue when transactions don't completely satisfy - * all required fields. + * @param bool $continue_on_missing_fields True to continue when transactions + * don't completely satisfy all required fields. * @return this */ public function setContinueOnMissingFields($continue_on_missing_fields) { @@ -2853,11 +2854,11 @@ abstract class PhabricatorApplicationTransactionEditor * missing, by detecting that the object has no field value and there is no * transaction which sets one. * - * @param PhabricatorLiskDAO Object being edited. - * @param string Transaction type to validate. - * @param list Transactions of given type, - * which may be empty if the edit does not apply any transactions of the - * given type. + * @param PhabricatorLiskDAO $object Object being edited. + * @param string $type Transaction type to validate. + * @param list $xactions Transactions of + * given type, which may be empty if the edit does not apply any + * transactions of the given type. * @return list List of * validation errors. */ @@ -3277,9 +3278,9 @@ abstract class PhabricatorApplicationTransactionEditor * This will return `true` if the net effect of the object and transactions * is an empty field. * - * @param wild Current field value. - * @param list Transactions editing the - * field. + * @param wild $field_value Current field value. + * @param list $xactions Transactions + * editing the field. * @return bool True if the field will be an empty text field after edits. */ protected function validateIsEmptyTextField($field_value, array $xactions) { @@ -3299,7 +3300,10 @@ abstract class PhabricatorApplicationTransactionEditor /** - * When a user interacts with an object, we might want to add them to CC. + * Adds the actor as a subscriber to the object with which they interact + * @param PhabricatorLiskDAO $object on which the action is performed + * @param array $xactions Transactions to apply + * @return array Transactions to apply */ final public function applyImplicitCC( PhabricatorLiskDAO $object, @@ -3381,11 +3385,18 @@ abstract class PhabricatorApplicationTransactionEditor return $xactions; } + /** + * Whether the action implies the actor should be subscribed on the object + * @param PhabricatorLiskDAO $object on which the action is performed + * @param PhabricatorApplicationTransaction $xaction Transaction to apply + * @return bool True if the actor should be subscribed on the object + */ protected function shouldImplyCC( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { - return $xaction->isCommentTransaction(); + return ($xaction->isCommentTransaction() && + !($xaction->getComment()->getIsRemoved())); } @@ -4590,7 +4601,8 @@ abstract class PhabricatorApplicationTransactionEditor * * This method is used to load state when running worker operations. * - * @param dict Editor state, from @{method:getWorkerState}. + * @param dict $state Editor state, from + @{method:getWorkerState}. * @return this * @task workers */ @@ -4616,7 +4628,7 @@ abstract class PhabricatorApplicationTransactionEditor * Hook; set custom properties on the editor from data emitted by * @{method:getCustomWorkerState}. * - * @param dict Custom state, + * @param dict $state Custom state, * from @{method:getCustomWorkerState}. * @return this * @task workers @@ -4664,8 +4676,8 @@ abstract class PhabricatorApplicationTransactionEditor * * See @{method:getCustomWorkerStateEncoding}. * - * @param map Map of values to encode. - * @param map Map of encodings to apply. + * @param map $state Map of values to encode. + * @param map $encodings Map of encodings to apply. * @return map Map of encoded values. * @task workers */ @@ -4710,8 +4722,8 @@ abstract class PhabricatorApplicationTransactionEditor * * See @{method:getCustomWorkerStateEncoding}. * - * @param map Map of encoded values. - * @param map Map of encodings. + * @param map $state Map of encoded values. + * @param map $encodings Map of encodings. * @return map Map of decoded values. * @task workers */ @@ -4749,7 +4761,7 @@ abstract class PhabricatorApplicationTransactionEditor * If the list of PHIDs include mutually exclusive projects, remove the * conflicting projects. * - * @param list List of project PHIDs. + * @param list $phids List of project PHIDs. * @return list List with conflicts removed. */ private function applyProjectConflictRules(array $phids) { diff --git a/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php index 5229ee6bec..a1b94f419a 100644 --- a/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php +++ b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php @@ -7,6 +7,10 @@ final class PhabricatorEditEngineConfigurationEditor return PhabricatorTransactionsApplication::class; } + public function getCreateObjectTitle($author, $object) { + return pht('%s created this form.', $author); + } + public function getEditorObjectsDescription() { return pht('Edit Configurations'); } diff --git a/src/applications/transactions/engine/PhabricatorTimelineEngine.php b/src/applications/transactions/engine/PhabricatorTimelineEngine.php index 2fa0b1451d..8f32d81ff9 100644 --- a/src/applications/transactions/engine/PhabricatorTimelineEngine.php +++ b/src/applications/transactions/engine/PhabricatorTimelineEngine.php @@ -6,6 +6,7 @@ abstract class PhabricatorTimelineEngine private $viewer; private $object; private $xactions; + private $request; private $viewData; final public static function newForObject($object) { diff --git a/src/applications/transactions/engineextension/PhabricatorEditorExtension.php b/src/applications/transactions/engineextension/PhabricatorEditorExtension.php index 6ffac522c2..643ecd0350 100644 --- a/src/applications/transactions/engineextension/PhabricatorEditorExtension.php +++ b/src/applications/transactions/engineextension/PhabricatorEditorExtension.php @@ -66,18 +66,4 @@ abstract class PhabricatorEditorExtension $xaction); } - final protected function newRequiredTransasctionError( - PhabricatorApplicationTransaction $xaction, - $message) { - return $this->newError($xaction, pht('Required'), $message) - ->setIsMissingFieldError(true); - } - - final protected function newInvalidTransactionError( - PhabricatorApplicationTransaction $xaction, - $message) { - return $this->newTransactionError($xaction, pht('Invalid'), $message); - } - - } diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php index c03cefeed0..5c70f59a89 100644 --- a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php +++ b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php @@ -1475,6 +1475,8 @@ abstract class PhabricatorApplicationTransaction } else { $fragments = array(); foreach ($moves as $move) { + $to_column = $move['columnPHID']; + $board_phid = $move['boardPHID']; $fragments[] = pht( '%s (%s)', $this->renderHandleLink($board_phid), @@ -1578,6 +1580,10 @@ abstract class PhabricatorApplicationTransaction return 100; } + /** + * Whether the transaction concerns a comment (e.g. add, edit, remove) + * @return bool True if the transaction concerns a comment + */ public function isCommentTransaction() { if ($this->hasComment()) { return true; @@ -1606,6 +1612,8 @@ abstract class PhabricatorApplicationTransaction return pht('Changed Policy'); case PhabricatorTransactions::TYPE_SUBSCRIBERS: return pht('Changed Subscribers'); + case PhabricatorTransactions::TYPE_CREATE: + return pht('Created'); default: return pht('Updated'); } @@ -1688,7 +1696,7 @@ abstract class PhabricatorApplicationTransaction * Should this transaction be visually grouped with an existing transaction * group? * - * @param list List of transactions. + * @param list $group List of transactions. * @return bool True to display in a group with the other transactions. */ public function shouldDisplayGroupWith(array $group) { diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransactionComment.php b/src/applications/transactions/storage/PhabricatorApplicationTransactionComment.php index 00cad18d2d..126510a4b3 100644 --- a/src/applications/transactions/storage/PhabricatorApplicationTransactionComment.php +++ b/src/applications/transactions/storage/PhabricatorApplicationTransactionComment.php @@ -74,6 +74,10 @@ abstract class PhabricatorApplicationTransactionComment return PhabricatorContentSource::newFromSerialized($this->contentSource); } + /** + * Whether the transaction removes the comment + * @return bool True if the transaction removes the comment + */ public function getIsRemoved() { return ($this->getIsDeleted() == 2); } diff --git a/src/applications/transactions/storage/PhabricatorModularTransactionType.php b/src/applications/transactions/storage/PhabricatorModularTransactionType.php index e2f0a02239..1d449ab868 100644 --- a/src/applications/transactions/storage/PhabricatorModularTransactionType.php +++ b/src/applications/transactions/storage/PhabricatorModularTransactionType.php @@ -428,8 +428,9 @@ abstract class PhabricatorModularTransactionType * additional capability or policy requirement above and beyond CAN_EDIT, it * is usually better implemented as a validation check. * - * @param object Object being edited. - * @param PhabricatorApplicationTransaction Transaction being applied. + * @param object $object Object being edited. + * @param PhabricatorApplicationTransaction $xaction Transaction being + * applied. * @return null|const|list A capability constant (or list of * capability constants) which the actor must have on the object. You can * return `null` as a shorthand for "no capabilities are required". diff --git a/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php b/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php index 2e469c5e0a..684c348914 100644 --- a/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php +++ b/src/applications/transactions/view/PhabricatorApplicationTransactionCommentView.php @@ -15,11 +15,12 @@ final class PhabricatorApplicationTransactionCommentView private $draft; private $requestURI; private $showPreview = true; - private $objectPHID; + private $object; private $headerText; private $noPermission; private $fullWidth; private $infoView; + private $editEngine; private $editEngineLock; private $noBorder; private $requiresMFA; @@ -30,13 +31,19 @@ final class PhabricatorApplicationTransactionCommentView private $commentActionGroups = array(); private $transactionTimeline; - public function setObjectPHID($object_phid) { - $this->objectPHID = $object_phid; + /** + * Set object in which this comment textarea field is displayed + */ + public function setObject($object) { + $this->object = $object; return $this; } - public function getObjectPHID() { - return $this->objectPHID; + /** + * Get object in which this comment textarea is displayed + */ + public function getObject() { + return $this->object; } public function setShowPreview($show_preview) { @@ -150,6 +157,15 @@ final class PhabricatorApplicationTransactionCommentView return $this->noPermission; } + public function setEditEngine(PhabricatorEditEngine $edit_engine) { + $this->editEngine = $edit_engine; + return $this; + } + + public function getEditEngine() { + return $this->editEngine; + } + public function setEditEngineLock(PhabricatorEditEngineLock $lock) { $this->editEngineLock = $lock; return $this; @@ -295,6 +311,15 @@ final class PhabricatorApplicationTransactionCommentView private function renderCommentPanel() { $viewer = $this->getViewer(); + $engine = $this->getEditEngine(); + // In a few rare cases PhabricatorApplicationTransactionCommentView gets + // initiated in a View or Controller class. Don't crash in that case. + if ($engine) { + $placeholder_text = $engine + ->getCommentFieldPlaceholderText($this->getObject()); + } else { + $placeholder_text = ''; + } $remarkup_control = id(new PhabricatorRemarkupControl()) ->setViewer($viewer) @@ -302,6 +327,7 @@ final class PhabricatorApplicationTransactionCommentView ->addClass('phui-comment-fullwidth-control') ->addClass('phui-comment-textarea-control') ->setCanPin(true) + ->setPlaceholder($placeholder_text) ->setName('comment'); $draft_comment = ''; @@ -331,7 +357,7 @@ final class PhabricatorApplicationTransactionCommentView } $remarkup_control->setRemarkupMetadata($draft_metadata); - if (!$this->getObjectPHID()) { + if (!$this->getObject()->getPHID()) { throw new PhutilInvalidStateException('setObjectPHID', 'render'); } @@ -345,7 +371,7 @@ final class PhabricatorApplicationTransactionCommentView ->setFullWidth($this->fullWidth) ->setMetadata( array( - 'objectPHID' => $this->getObjectPHID(), + 'objectPHID' => $this->getObject()->getPHID(), )) ->setAction($this->getAction()) ->setID($this->getFormID()) diff --git a/src/applications/transactions/xaction/PhabricatorCoreCreateTransaction.php b/src/applications/transactions/xaction/PhabricatorCoreCreateTransaction.php index 0c8c337193..15becceab9 100644 --- a/src/applications/transactions/xaction/PhabricatorCoreCreateTransaction.php +++ b/src/applications/transactions/xaction/PhabricatorCoreCreateTransaction.php @@ -27,4 +27,13 @@ final class PhabricatorCoreCreateTransaction return $editor->getCreateObjectTitleForFeed($author, $object); } + public function getActionStrength() { + // The creation feed is supposed to be "more important" than other things. + // So a Task is first created and then closed, and not vice-versa. + // The default null was causing weirdnesses in Maniphest and Phriction. + // See ManiphestTaskTitleTransaction#getActionStrength() + // See PhrictionDocumentTitleTransaction#getActionStrength() + return 140; + } + } diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php index 8a396c1ed3..338c4db564 100644 --- a/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php +++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadCompositeDatasource.php @@ -304,6 +304,15 @@ abstract class PhabricatorTypeaheadCompositeDatasource return parent::evaluateFunction($function, $argv); } + protected function isFunctionWithLoginRequired($function) { + foreach ($this->getUsableDatasources() as $source) { + if ($source->isFunctionWithLoginRequired($function)) { + return true; + } + } + return parent::isFunctionWithLoginRequired($function); + } + public function renderFunctionTokens($function, array $argv_list) { foreach ($this->getUsableDatasources() as $source) { if ($source->canEvaluateFunction($function)) { diff --git a/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php b/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php index a35a8e8f0f..11c94d1933 100644 --- a/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php +++ b/src/applications/typeahead/datasource/PhabricatorTypeaheadDatasource.php @@ -212,7 +212,7 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject { * For datasources backed by database objects, this is often much less * efficient than filtering at the query level. * - * @param list List of typeahead results. + * @param list $results List of typeahead results. * @return list Filtered results. */ protected function filterResultsAgainstTokens(array $results) { @@ -365,6 +365,19 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject { } + /** + * Check if this datasource requires a logged-in viewer. + * @task functions + * @param string $function Function name. + * @return bool + */ + protected function isFunctionWithLoginRequired($function) { + // This is just a default. + // Make sure to override this method to require login. + return false; + } + + /** * @task functions */ @@ -498,6 +511,18 @@ abstract class PhabricatorTypeaheadDatasource extends Phobject { if (!$this->canEvaluateFunction($function)) { if (!$allow_partial) { + + if ($this->isFunctionWithLoginRequired($function)) { + if (!$this->getViewer() || !$this->getViewer()->isLoggedIn()) { + throw new PhabricatorTypeaheadLoginRequiredException( + pht( + 'This datasource ("%s") requires to be logged-in to use the '. + 'function "%s(...)".', + get_class($this), + $function)); + } + } + throw new PhabricatorTypeaheadInvalidTokenException( pht( 'This datasource ("%s") can not evaluate the function "%s(...)".', diff --git a/src/applications/typeahead/exception/PhabricatorTypeaheadLoginRequiredException.php b/src/applications/typeahead/exception/PhabricatorTypeaheadLoginRequiredException.php new file mode 100644 index 0000000000..3b29df5df8 --- /dev/null +++ b/src/applications/typeahead/exception/PhabricatorTypeaheadLoginRequiredException.php @@ -0,0 +1,6 @@ +`. You should not need to explicitly use -@{function@arcanist:phutil_escape_html} anywhere. +@{function:phutil_escape_html} anywhere. If you need to apply a string function (such as `trim()`) to safe HTML, use -@{method@arcanist:PhutilSafeHTML::applyFunction}. +@{method:PhutilSafeHTML::applyFunction}. -If you need to extract the content of a @{class@arcanist:PhutilSafeHTML} +If you need to extract the content of a @{class:PhutilSafeHTML} object, you should call `getHTMLContent()`, not cast it to a string. Eventually, we would like to remove the string cast entirely. -Functions @{function@arcanist:phutil_tag} and @{function@arcanist:hsprintf} +Functions @{function:phutil_tag} and @{function:hsprintf} are not safe if you pass the user input for the tag or attribute name. All the following examples are dangerous: diff --git a/src/docs/user/configuration/configuration_guide.diviner b/src/docs/user/configuration/configuration_guide.diviner index c5aa82af86..978506fb30 100644 --- a/src/docs/user/configuration/configuration_guide.diviner +++ b/src/docs/user/configuration/configuration_guide.diviner @@ -49,7 +49,7 @@ this: DocumentRoot /path/to/phorge/webroot RewriteEngine on - RewriteRule ^(.*)$ /index.php?__path__=$1 [B,L,QSA] + RewriteRule ^(.*)$ /index.php?__path__=$1 [B,L,QSA,UnsafeAllow3F] If Apache isn't currently configured to serve documents out of the directory diff --git a/src/docs/user/configuration/configuring_file_domain.diviner b/src/docs/user/configuration/configuring_file_domain.diviner index 1fc4ef3213..b06ff6cc6b 100644 --- a/src/docs/user/configuration/configuring_file_domain.diviner +++ b/src/docs/user/configuration/configuring_file_domain.diviner @@ -66,11 +66,10 @@ Approach: Cloudflare ======== WARNING: You should review all your Cloudflare settings, and be very -sure to turn off all JavaScript, HTML, CSS minification and -optimization features, including systems like "Rocket Loader". These -features will break Phorge in strange and mysterious ways that -are unpredictable. Only allow Cloudflare to cache files, and never -optimize them. +sure to turn off all optimization features, including systems like +"Rocket Loader". These features will break Phorge in strange and +mysterious ways that are unpredictable. Only allow Cloudflare to cache files, +and never optimize them. [[ https://www.cloudflare.com | Cloudflare ]] is a general-purpose CDN service. diff --git a/src/docs/user/configuration/custom_fields.diviner b/src/docs/user/configuration/custom_fields.diviner index 5879931317..fb036d11d9 100644 --- a/src/docs/user/configuration/custom_fields.diviner +++ b/src/docs/user/configuration/custom_fields.diviner @@ -119,6 +119,12 @@ When defining custom fields using a configuration option like above the control when rendered on the edit view. - **placeholder**: A placeholder text that appears on text boxes. Only supported in text, int and remarkup fields (optional). + - **list**: If set to `icon`, `attribute` or `byline`, the value of the field + will be shown in list-view. + - **list.icon**: If `list` is set to `icon`, use this icon. These are the + same icons that can be used in the `{icon}` syntax for Remarkup. + - **list.label**: When rendering value in a list, use this label (instead of + `name`). - **copy**: If true, this field's value will be copied when an object is created using another object as a template. - **limit**: For control types which use a tokenizer control to let the user diff --git a/src/infrastructure/cache/PhutilInRequestKeyValueCache.php b/src/infrastructure/cache/PhutilInRequestKeyValueCache.php index 2c303e860f..19edc81e2a 100644 --- a/src/infrastructure/cache/PhutilInRequestKeyValueCache.php +++ b/src/infrastructure/cache/PhutilInRequestKeyValueCache.php @@ -26,7 +26,7 @@ final class PhutilInRequestKeyValueCache extends PhutilKeyValueCache { * When too many keys are inserted, the oldest keys are removed from the * cache. Setting a limit of `0` disables the cache. * - * @param int Maximum number of items to store in the cache. + * @param int $limit Maximum number of items to store in the cache. * @return this */ public function setLimit($limit) { diff --git a/src/infrastructure/cache/PhutilKeyValueCache.php b/src/infrastructure/cache/PhutilKeyValueCache.php index 8260df1ef2..e65eb9675c 100644 --- a/src/infrastructure/cache/PhutilKeyValueCache.php +++ b/src/infrastructure/cache/PhutilKeyValueCache.php @@ -30,9 +30,9 @@ abstract class PhutilKeyValueCache extends Phobject { * Get a single key from cache. See @{method:getKeys} to get multiple keys at * once. * - * @param string Key to retrieve. - * @param wild Optional value to return if the key is not found. By - * default, returns null. + * @param string $key Key to retrieve. + * @param wild? $default Optional value to return if the key is not + * found. By default, returns null. * @return wild Cache value (on cache hit) or default value (on cache * miss). * @task kvimpl @@ -49,9 +49,9 @@ abstract class PhutilKeyValueCache extends Phobject { * * See @{method:setKeys} for a description of TTLs. * - * @param string Key to set. - * @param wild Value to set. - * @param int|null Optional TTL. + * @param string $key Key to set. + * @param wild $value Value to set. + * @param int|null? $ttl Optional TTL. * @return this * @task kvimpl */ @@ -64,7 +64,7 @@ abstract class PhutilKeyValueCache extends Phobject { * Delete a key from the cache. See @{method:deleteKeys} to delete multiple * keys at once. * - * @param string Key to delete. + * @param string $key Key to delete. * @return this * @task kvimpl */ @@ -76,7 +76,7 @@ abstract class PhutilKeyValueCache extends Phobject { /** * Get data from the cache. * - * @param list List of cache keys to retrieve. + * @param list $keys List of cache keys to retrieve. * @return dict Dictionary of keys that were found in the * cache. Keys not present in the cache are * omitted, so you can detect a cache miss. @@ -92,8 +92,8 @@ abstract class PhutilKeyValueCache extends Phobject { * after a specified number of seconds. By default, there is no expiration * policy and data will persist in cache indefinitely. * - * @param dict Map of cache keys to values. - * @param int|null TTL for cache keys, in seconds. + * @param dict $keys Map of cache keys to values. + * @param int|null? $ttl TTL for cache keys, in seconds. * @return this * @task kvimpl */ @@ -103,7 +103,7 @@ abstract class PhutilKeyValueCache extends Phobject { /** * Delete a list of keys from the cache. * - * @param list List of keys to delete. + * @param list $keys List of keys to delete. * @return this * @task kvimpl */ diff --git a/src/infrastructure/cache/PhutilKeyValueCacheProfiler.php b/src/infrastructure/cache/PhutilKeyValueCacheProfiler.php index 9e2271b530..f5d6979c92 100644 --- a/src/infrastructure/cache/PhutilKeyValueCacheProfiler.php +++ b/src/infrastructure/cache/PhutilKeyValueCacheProfiler.php @@ -17,7 +17,7 @@ final class PhutilKeyValueCacheProfiler extends PhutilKeyValueCacheProxy { /** * Set a profiler for cache operations. * - * @param PhutilServiceProfiler Service profiler. + * @param PhutilServiceProfiler $profiler Service profiler. * @return this * @task kvimpl */ diff --git a/src/infrastructure/cache/PhutilKeyValueCacheStack.php b/src/infrastructure/cache/PhutilKeyValueCacheStack.php index 76ea643be4..eab1ff86c5 100644 --- a/src/infrastructure/cache/PhutilKeyValueCacheStack.php +++ b/src/infrastructure/cache/PhutilKeyValueCacheStack.php @@ -38,7 +38,8 @@ final class PhutilKeyValueCacheStack extends PhutilKeyValueCache { /** * Set the caches which comprise this stack. * - * @param list Ordered list of key-value caches. + * @param list $caches Ordered list of key-value + * caches. * @return this * @task config */ @@ -62,7 +63,7 @@ final class PhutilKeyValueCacheStack extends PhutilKeyValueCache { * // TTL does not persist; this will use no TTL. * $stack->getKey('hedgehog'); * - * @param int TTL in seconds. + * @param int $ttl TTL in seconds. * @return this * * @task config diff --git a/src/infrastructure/cache/PhutilMemcacheKeyValueCache.php b/src/infrastructure/cache/PhutilMemcacheKeyValueCache.php index d095eff706..918971ac28 100644 --- a/src/infrastructure/cache/PhutilMemcacheKeyValueCache.php +++ b/src/infrastructure/cache/PhutilMemcacheKeyValueCache.php @@ -100,7 +100,7 @@ final class PhutilMemcacheKeyValueCache extends PhutilKeyValueCache { * ), * )); * - * @param list List of server specifications. + * @param list $servers List of server specifications. * @return this * @task memcache */ diff --git a/src/infrastructure/contentsource/PhabricatorContentSource.php b/src/infrastructure/contentsource/PhabricatorContentSource.php index de36f50b1f..19f954e0d8 100644 --- a/src/infrastructure/contentsource/PhabricatorContentSource.php +++ b/src/infrastructure/contentsource/PhabricatorContentSource.php @@ -22,10 +22,10 @@ abstract class PhabricatorContentSource extends Phobject { /** * Construct a new content source object. * - * @param const The source type constant to build a source for. - * @param array Source parameters. - * @param bool True to suppress errors and force construction of a source - * even if the source type is not valid. + * @param const $source The source type constant to build a source for. + * @param array? $params Source parameters. + * @param bool? $force True to suppress errors and force construction of a + * source even if the source type is not valid. * @return PhabricatorContentSource New source object. */ final public static function newForSource( diff --git a/src/infrastructure/customfield/field/PhabricatorCustomField.php b/src/infrastructure/customfield/field/PhabricatorCustomField.php index 4e55348c36..c513b59c82 100644 --- a/src/infrastructure/customfield/field/PhabricatorCustomField.php +++ b/src/infrastructure/customfield/field/PhabricatorCustomField.php @@ -243,7 +243,7 @@ abstract class PhabricatorCustomField extends Phobject { * For general implementations, the general field implementation can return * multiple field instances here. * - * @param object The object to create fields for. + * @param object $object The object to create fields for. * @return list List of fields. * @task core */ @@ -382,8 +382,9 @@ abstract class PhabricatorCustomField extends Phobject { * Set the proxy implementation for this field. See @{method:canSetProxy} for * discussion of field proxies. * - * @param PhabricatorCustomField Field implementation. + * @param PhabricatorCustomField $proxy Field implementation. * @return this + * @task proxy */ final public function setProxy(PhabricatorCustomField $proxy) { if (!$this->canSetProxy()) { @@ -400,19 +401,28 @@ abstract class PhabricatorCustomField extends Phobject { * @{method:canSetProxy}. * * @return PhabricatorCustomField|null Proxy field, if one is set. + * @task proxy */ final public function getProxy() { return $this->proxy; } - + /** + * @task proxy + */ + public function __clone() { + if ($this->proxy) { + $this->proxy = clone $this->proxy; + } + } /* -( Contextual Data )---------------------------------------------------- */ /** * Sets the object this field belongs to. * - * @param PhabricatorCustomFieldInterface The object this field belongs to. + * @param PhabricatorCustomFieldInterface $object The object this field + * belongs to. * @return this * @task context */ @@ -431,7 +441,8 @@ abstract class PhabricatorCustomField extends Phobject { /** * Read object data into local field storage, if applicable. * - * @param PhabricatorCustomFieldInterface The object this field belongs to. + * @param PhabricatorCustomFieldInterface $object The object this field + * belongs to. * @return this * @task context */ @@ -598,7 +609,7 @@ abstract class PhabricatorCustomField extends Phobject { * need to undo whatever serialization you applied in * @{method:getValueForStorage}. * - * @param string|null Serialized field representation (from + * @param string|null $value Serialized field representation (from * @{method:getValueForStorage}) or null if no value has * ever been stored. * @return this @@ -720,7 +731,7 @@ abstract class PhabricatorCustomField extends Phobject { /** * Build and populate storage for a string index. * - * @param string String to index. + * @param string $value String to index. * @return PhabricatorCustomFieldStringIndexStorage Populated storage. * @task appsearch */ @@ -739,7 +750,7 @@ abstract class PhabricatorCustomField extends Phobject { /** * Build and populate storage for a numeric index. * - * @param string Numeric value to index. + * @param string $value Numeric value to index. * @return PhabricatorCustomFieldNumericIndexStorage Populated storage. * @task appsearch */ @@ -758,8 +769,9 @@ abstract class PhabricatorCustomField extends Phobject { * Read a query value from a request, for storage in a saved query. Normally, * this method should, e.g., read a string out of the request. * - * @param PhabricatorApplicationSearchEngine Engine building the query. - * @param AphrontRequest Request to read from. + * @param PhabricatorApplicationSearchEngine $engine Engine building the + * query. + * @param AphrontRequest $request Request to read from. * @return wild * @task appsearch */ @@ -780,9 +792,10 @@ abstract class PhabricatorCustomField extends Phobject { * use `with...()` methods to apply filters or other constraints to the * query. * - * @param PhabricatorApplicationSearchEngine Engine executing the query. - * @param PhabricatorCursorPagedPolicyAwareQuery Query to constrain. - * @param wild Constraint provided by the user. + * @param PhabricatorApplicationSearchEngine $engine Engine executing the + * query. + * @param PhabricatorCursorPagedPolicyAwareQuery $query Query to constrain. + * @param wild $value Constraint provided by the user. * @return void * @task appsearch */ @@ -803,9 +816,10 @@ abstract class PhabricatorCustomField extends Phobject { /** * Append search controls to the interface. * - * @param PhabricatorApplicationSearchEngine Engine constructing the form. - * @param AphrontFormView The form to update. - * @param wild Value from the saved query. + * @param PhabricatorApplicationSearchEngine $engine Engine constructing the + * form. + * @param AphrontFormView $form The form to update. + * @param wild $value Value from the saved query. * @return void * @task appsearch */ @@ -827,7 +841,7 @@ abstract class PhabricatorCustomField extends Phobject { /** - * Appearing in ApplicationTrasactions allows a field to be edited using + * Appearing in ApplicationTransactions allows a field to be edited using * standard workflows. * * @return bool True to appear in ApplicationTransactions. @@ -991,12 +1005,12 @@ abstract class PhabricatorCustomField extends Phobject { * when a transaction would set a field to an invalid value, or when a field * is required but no transactions provide value. * - * @param PhabricatorLiskDAO Editor applying the transactions. - * @param string Transaction type. This type is always + * @param PhabricatorLiskDAO $editor Editor applying the transactions. + * @param string $type Transaction type. This type is always * `PhabricatorTransactions::TYPE_CUSTOMFIELD`, it is provided for * convenience when constructing exceptions. - * @param list Transactions being applied, - * which may be empty if this field is not being edited. + * @param list $xactions Transactions + * being applied, which may be empty if this field is not being edited. * @return list Validation * errors. * @@ -1557,7 +1571,7 @@ abstract class PhabricatorCustomField extends Phobject { /** * Get the Herald value type for the given condition. * - * @param const Herald condition constant. + * @param const $condition Herald condition constant. * @return const|null Herald value type, or null to use the default. * @task herald */ diff --git a/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php b/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php index 60a5171cdd..e5f5c1698f 100644 --- a/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php +++ b/src/infrastructure/customfield/field/PhabricatorCustomFieldList.php @@ -46,7 +46,8 @@ final class PhabricatorCustomFieldList extends Phobject { /** * Read stored values for all fields which support storage. * - * @param PhabricatorCustomFieldInterface Object to read field values for. + * @param PhabricatorCustomFieldInterface $object Object to read field values + * for. * @return void */ public function readFieldsFromStorage( @@ -200,6 +201,19 @@ final class PhabricatorCustomFieldList extends Phobject { } } + public function addFieldsToListViewItem( + PhabricatorCustomFieldInterface $object, + PhabricatorUser $viewer, + PHUIObjectItemView $view) { + + foreach ($this->fields as $field) { + if ($field->shouldAppearInListView()) { + $field->setViewer($viewer); + $field->renderOnListItem($view); + } + } + } + public function buildFieldTransactionsFromRequest( PhabricatorApplicationTransaction $template, AphrontRequest $request) { diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php index 87cde00801..8ed6e0f42a 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomField.php @@ -301,6 +301,10 @@ abstract class PhabricatorStandardCustomField } public function renderPropertyViewValue(array $handles) { + return $this->renderValue(); + } + + protected function renderValue() { // If your field needs to render anything more complicated then a string, // then you should override this method. $value_str = phutil_string_cast($this->getFieldValue()); @@ -311,6 +315,77 @@ abstract class PhabricatorStandardCustomField return null; } + public function shouldAppearInListView() { + return $this->getFieldConfigValue('list', false); + } + + public function getStyleForListItemView() { + return $this->getFieldConfigValue('list'); + } + + public function renderListItemValue() { + return $this->renderValue(); + } + + private function isValue($something) { + if (is_object($something)) { + return true; + } + return phutil_nonempty_scalar($something); + } + + public function getValueForListItem() { + $style = $this->getStyleForListItemView(); + $value = $this->renderListItemValue(); + if (!$this->isValue($value) || !$style) { + return null; + } + switch ($style) { + case 'icon': + // maybe expose 'list.icon.alt' for hover stuff? + // also icon's "label", and other features supported by + // PHUIObjectItemView::addIcon(). + return 'fa-'.$this->getFieldConfigValue('list.icon'); + case 'attribute': + case 'byline': + $label = $this->getFieldConfigValue( + 'list.label', + $this->getFieldName()); + if (phutil_nonempty_string($label)) { + return pht('%s: %s', $label, $value); + } + return $value; + default: + throw new Exception( + pht( + "Unknown field list-item view style '%s'; valid styles are ". + "'%s', '%s'and '%s'.", + $style, + 'icon', + 'attribute', + 'byline')); + } + } + + public function renderOnListItem(PHUIObjectItemView $view) { + $value = $this->getValueForListItem(); + if (!$this->isValue($value)) { + return; + } + + switch ($this->getStyleForListItemView()) { + case 'icon': + $view->addIcon($value); + break; + case 'attribute': + $view->addAttribute($value); + break; + case 'byline': + $view->addByline($value); + break; + } + } + public function shouldAppearInApplicationSearch() { return $this->getFieldConfigValue('search', false); } diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBlueprints.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBlueprints.php index ad2bb62d81..95c3c1a3e2 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBlueprints.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBlueprints.php @@ -24,7 +24,7 @@ final class PhabricatorStandardCustomFieldBlueprints $new); } - public function renderPropertyViewValue(array $handles) { + protected function renderValue() { $value = $this->getFieldValue(); if (!$value) { return phutil_tag('em', array(), pht('No authorized blueprints.')); diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php index f1d1371a7d..a6bdbeb955 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php @@ -36,7 +36,7 @@ final class PhabricatorStandardCustomFieldBool } public function setValueFromStorage($value) { - if (strlen($value)) { + if (phutil_nonempty_scalar($value)) { $value = (bool)$value; } else { $value = null; @@ -90,7 +90,7 @@ final class PhabricatorStandardCustomFieldBool (bool)$this->getFieldValue()); } - public function renderPropertyViewValue(array $handles) { + protected function renderValue() { $value = $this->getFieldValue(); if ($value) { return $this->getString('view.yes', pht('Yes')); diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php index c2f958f228..feb3732791 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php @@ -53,10 +53,10 @@ final class PhabricatorStandardCustomFieldCredential return array(); } - public function renderPropertyViewValue(array $handles) { + protected function renderValue() { $value = $this->getFieldValue(); if ($value) { - return $handles[$value]->renderLink(); + return $this->getViewer()->renderHandle($value); } return null; } diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php index 5988970ca6..389b440e46 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php @@ -52,7 +52,7 @@ final class PhabricatorStandardCustomFieldDate $this->setFieldValue($value); } - public function renderPropertyViewValue(array $handles) { + protected function renderValue() { $value = $this->getFieldValue(); if (!$value) { return null; @@ -177,6 +177,12 @@ final class PhabricatorStandardCustomFieldDate $xaction->renderHandleLink($author_phid), $this->getFieldName(), $new_date); + } else if (!$new && $old) { + return pht( + '%s removed %s which was set to %s.', + $xaction->renderHandleLink($author_phid), + $this->getFieldName(), + $old_date); } else if (!$new) { return pht( '%s removed %s.', diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldHeader.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldHeader.php index beb92f9aed..2bd8fea40f 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldHeader.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldHeader.php @@ -26,7 +26,7 @@ final class PhabricatorStandardCustomFieldHeader return 'header'; } - public function renderPropertyViewValue(array $handles) { + protected function renderValue() { return $this->getFieldName(); } diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldLink.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldLink.php index a5a1af5013..833b110968 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldLink.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldLink.php @@ -18,7 +18,7 @@ final class PhabricatorStandardCustomFieldLink return $indexes; } - public function renderPropertyViewValue(array $handles) { + protected function renderValue() { $value = $this->getFieldValue(); if (!phutil_nonempty_string($value)) { diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php index e34c3913f0..153facd89f 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldPHIDs.php @@ -72,15 +72,14 @@ abstract class PhabricatorStandardCustomFieldPHIDs return array(); } - public function renderPropertyViewValue(array $handles) { + protected function renderValue() { $value = $this->getFieldValue(); if (!$value) { return null; } - $handles = mpull($handles, 'renderHovercardLink'); - $handles = phutil_implode_html(', ', $handles); - return $handles; + return $this->getViewer()->renderHandleList($value) + ->setAsInline(true); } public function getRequiredHandlePHIDsForEdit() { diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php index 54f54c7503..cda1595aeb 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldRemarkup.php @@ -27,7 +27,7 @@ final class PhabricatorStandardCustomFieldRemarkup ); } - public function renderPropertyViewValue(array $handles) { + protected function renderValue() { $value = $this->getFieldValue(); if (!phutil_nonempty_string($value)) { diff --git a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php index 958d65d7e7..505eb19c5e 100644 --- a/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php +++ b/src/infrastructure/customfield/standard/PhabricatorStandardCustomFieldSelect.php @@ -72,7 +72,7 @@ final class PhabricatorStandardCustomFieldSelect ->setOptions($this->getOptions()); } - public function renderPropertyViewValue(array $handles) { + protected function renderValue() { if (!phutil_nonempty_string($this->getFieldValue())) { return null; } diff --git a/src/infrastructure/customfield/storage/PhabricatorCustomFieldStorage.php b/src/infrastructure/customfield/storage/PhabricatorCustomFieldStorage.php index cf0140a38a..111a16c968 100644 --- a/src/infrastructure/customfield/storage/PhabricatorCustomFieldStorage.php +++ b/src/infrastructure/customfield/storage/PhabricatorCustomFieldStorage.php @@ -44,7 +44,7 @@ abstract class PhabricatorCustomFieldStorage * The keys in the result should correspond to the keys in the input. The * fields in the list may belong to different objects. * - * @param map Map of fields. + * @param map $fields Map of fields. * @return map Map of available field data. */ final public function loadStorageSourceData(array $fields) { diff --git a/src/infrastructure/daemon/PhabricatorDaemon.php b/src/infrastructure/daemon/PhabricatorDaemon.php index f42a59f134..0e203566f8 100644 --- a/src/infrastructure/daemon/PhabricatorDaemon.php +++ b/src/infrastructure/daemon/PhabricatorDaemon.php @@ -24,7 +24,7 @@ abstract class PhabricatorDaemon extends PhutilDaemon { * Format a command so it executes as the daemon user, if a daemon user is * defined. This wraps the provided command in `sudo -u ...`, roughly. * - * @param PhutilCommandString Command to execute. + * @param PhutilCommandString $command Command to execute. * @return PhutilCommandString `sudo` version of the command. */ public static function sudoCommandAsDaemonUser($command) { diff --git a/src/infrastructure/daemon/PhutilDaemon.php b/src/infrastructure/daemon/PhutilDaemon.php index 701b3d7a27..ccc1a02fec 100644 --- a/src/infrastructure/daemon/PhutilDaemon.php +++ b/src/infrastructure/daemon/PhutilDaemon.php @@ -31,9 +31,6 @@ * into an autoscale pool, this will cause the pool to automatically scale up * when busy and down when idle. * - * See @{class:PhutilHighIntensityIntervalDaemon} for an example of a simple - * autoscaling daemon. - * * Launching a daemon which does not make these callbacks into an autoscale * pool will have no effect. * diff --git a/src/infrastructure/daemon/PhutilDaemonHandle.php b/src/infrastructure/daemon/PhutilDaemonHandle.php index 3792c3b773..a44ba7b7cb 100644 --- a/src/infrastructure/daemon/PhutilDaemonHandle.php +++ b/src/infrastructure/daemon/PhutilDaemonHandle.php @@ -328,8 +328,8 @@ final class PhutilDaemonHandle extends Phobject { /** * Dispatch an event to event listeners. * - * @param string Event type. - * @param dict Event parameters. + * @param string $type Event type. + * @param dict? $params Event parameters. * @return void */ private function dispatchEvent($type, array $params = array()) { diff --git a/src/infrastructure/daemon/PhutilDaemonOverseerModule.php b/src/infrastructure/daemon/PhutilDaemonOverseerModule.php index 3e2cdaad3e..9c746d17a1 100644 --- a/src/infrastructure/daemon/PhutilDaemonOverseerModule.php +++ b/src/infrastructure/daemon/PhutilDaemonOverseerModule.php @@ -47,8 +47,8 @@ abstract class PhutilDaemonOverseerModule extends Phobject { * return; * } * - * @param string Throttle key. - * @param float Duration in seconds. + * @param string $name Throttle key. + * @param float $duration Duration in seconds. * @return bool True to throttle the check. */ protected function shouldThrottle($name, $duration) { diff --git a/src/infrastructure/daemon/workers/PhabricatorTriggerDaemon.php b/src/infrastructure/daemon/workers/PhabricatorTriggerDaemon.php index 9561f3d18a..736f17f81b 100644 --- a/src/infrastructure/daemon/workers/PhabricatorTriggerDaemon.php +++ b/src/infrastructure/daemon/workers/PhabricatorTriggerDaemon.php @@ -122,7 +122,7 @@ final class PhabricatorTriggerDaemon * Process all of the triggers which have been updated since the last time * the daemon ran, scheduling them into the event table. * - * @param int Cursor for the next version update to process. + * @param int $cursor Cursor for the next version update to process. * @return void */ private function scheduleTriggers($cursor) { @@ -326,7 +326,7 @@ final class PhabricatorTriggerDaemon /** * Run the garbage collector for up to a specified number of seconds. * - * @param int Number of seconds the GC may run for. + * @param int $duration Number of seconds the GC may run for. * @return int Number of seconds remaining in the time budget. * @task garbage */ diff --git a/src/infrastructure/daemon/workers/PhabricatorWorker.php b/src/infrastructure/daemon/workers/PhabricatorWorker.php index c62fde447f..69815557ec 100644 --- a/src/infrastructure/daemon/workers/PhabricatorWorker.php +++ b/src/infrastructure/daemon/workers/PhabricatorWorker.php @@ -68,12 +68,12 @@ abstract class PhabricatorWorker extends Phobject { * retrying. For most tasks you can leave this at `null`, which will give you * a short default retry period (currently 60 seconds). * - * @param PhabricatorWorkerTask The task itself. This object is probably - * useful mostly to examine the failure count - * if you want to implement staggered retries, - * or to examine the execution exception if - * you want to react to different failures in - * different ways. + * @param PhabricatorWorkerTask $task The task itself. This object is + * probably useful mostly to examine the + * failure count if you want to implement + * staggered retries, or to examine the + * execution exception if you want to react to + * different failures in different ways. * @return int|null Number of seconds to wait between retries, * or null for a default retry period * (currently 60 seconds). @@ -230,9 +230,9 @@ abstract class PhabricatorWorker extends Phobject { * * The followup task will be queued only if this task completes cleanly. * - * @param string Task class to queue. - * @param array Data for the followup task. - * @param array Options for the followup task. + * @param string $class Task class to queue. + * @param array $data Data for the followup task. + * @param array? $options Options for the followup task. * @return this */ final protected function queueTask( @@ -261,8 +261,7 @@ abstract class PhabricatorWorker extends Phobject { * this method to force the queue to flush before failing (for example, if * you are using queues to improve locking behavior). * - * @param map Optional default options. - * @return this + * @param map? $defaults Optional default options. */ final public function flushTaskQueue($defaults = array()) { foreach ($this->getQueuedTasks() as $task) { @@ -287,7 +286,7 @@ abstract class PhabricatorWorker extends Phobject { * This method does not provide any assurances about when these tasks will * execute, or even guarantee that it will have any effect at all. * - * @param list List of task IDs to try to awaken. + * @param list $ids List of task IDs to try to awaken. * @return void */ final public static function awakenTaskIDs(array $ids) { diff --git a/src/infrastructure/daemon/workers/action/PhabricatorTriggerAction.php b/src/infrastructure/daemon/workers/action/PhabricatorTriggerAction.php index 2390a284ba..329c75dfc6 100644 --- a/src/infrastructure/daemon/workers/action/PhabricatorTriggerAction.php +++ b/src/infrastructure/daemon/workers/action/PhabricatorTriggerAction.php @@ -28,7 +28,7 @@ abstract class PhabricatorTriggerAction extends Phobject { /** * Validate action configuration. * - * @param map Map of action properties. + * @param map $properties Map of action properties. * @return void */ abstract public function validateProperties(array $properties); @@ -62,10 +62,10 @@ abstract class PhabricatorTriggerAction extends Phobject { * may be simplest to pass the trigger time to the task and then make the * decision to discard the action there. * - * @param int|null Last time the event occurred, or null if it has never - * triggered before. - * @param int The scheduled time for the current action. This may be - * significantly different from the current time. + * @param int|null $last_epoch Last time the event occurred, or null if it + * has never triggered before. + * @param int $this_epoch The scheduled time for the current action. This + * may be significantly different from the current time. * @return void */ abstract public function execute($last_epoch, $this_epoch); diff --git a/src/infrastructure/daemon/workers/clock/PhabricatorTriggerClock.php b/src/infrastructure/daemon/workers/clock/PhabricatorTriggerClock.php index 400e6fad8c..b33ea0ade3 100644 --- a/src/infrastructure/daemon/workers/clock/PhabricatorTriggerClock.php +++ b/src/infrastructure/daemon/workers/clock/PhabricatorTriggerClock.php @@ -32,7 +32,7 @@ abstract class PhabricatorTriggerClock extends Phobject { /** * Validate clock configuration. * - * @param map Map of clock properties. + * @param map $properties Map of clock properties. * @return void */ abstract public function validateProperties(array $properties); @@ -64,9 +64,10 @@ abstract class PhabricatorTriggerClock extends Phobject { * week to 3 minutes from now, the clock may reschedule the notification to * occur 12 minutes ago. This will cause it to execute immediately. * - * @param int|null Last time the event occurred, or null if it has never - * triggered before. - * @param bool True if this is a reschedule after a successful trigger. + * @param int|null $last_epoch Last time the event occurred, or null if it + * has never triggered before. + * @param bool $is_reschedule True if this is a reschedule after a successful + * trigger. * @return int|null Next event, or null to decline to reschedule. */ abstract public function getNextEventEpoch($last_epoch, $is_reschedule); diff --git a/src/infrastructure/daemon/workers/query/PhabricatorWorkerLeaseQuery.php b/src/infrastructure/daemon/workers/query/PhabricatorWorkerLeaseQuery.php index 0163143ae7..697d89a168 100644 --- a/src/infrastructure/daemon/workers/query/PhabricatorWorkerLeaseQuery.php +++ b/src/infrastructure/daemon/workers/query/PhabricatorWorkerLeaseQuery.php @@ -30,7 +30,7 @@ final class PhabricatorWorkerLeaseQuery extends PhabricatorQuery { * This can be used to show which tasks are coming up next without altering * the queue's behavior. * - * @param bool True to skip the lease acquisition step. + * @param bool $skip True to skip the lease acquisition step. */ public function setSkipLease($skip) { $this->skipLease = $skip; @@ -58,8 +58,8 @@ final class PhabricatorWorkerLeaseQuery extends PhabricatorQuery { * leasing using @{method:setSkipLease}. These options are intended for use * when displaying task status information. * - * @param mixed `true` to select only leased tasks, `false` to select only - * unleased tasks (default), or `null` to select both. + * @param mixed $leased `true` to select only leased tasks, `false` to select + * only unleased tasks (default), or `null` to select both. * @return this */ public function withLeasedTasks($leased) { diff --git a/src/infrastructure/daemon/workers/query/PhabricatorWorkerTriggerQuery.php b/src/infrastructure/daemon/workers/query/PhabricatorWorkerTriggerQuery.php index 8ca12d60e4..fa2f04f5a1 100644 --- a/src/infrastructure/daemon/workers/query/PhabricatorWorkerTriggerQuery.php +++ b/src/infrastructure/daemon/workers/query/PhabricatorWorkerTriggerQuery.php @@ -59,7 +59,7 @@ final class PhabricatorWorkerTriggerQuery * triggers which have been scheduled to execute. You should not use this * ordering when querying for specific triggers, e.g. by ID or PHID. * - * @param const Result order. + * @param const $order Result order. * @return this */ public function setOrder($order) { diff --git a/src/infrastructure/daemon/workers/storage/PhabricatorWorkerTrigger.php b/src/infrastructure/daemon/workers/storage/PhabricatorWorkerTrigger.php index 7b9a24d7e0..70a6c6d0f5 100644 --- a/src/infrastructure/daemon/workers/storage/PhabricatorWorkerTrigger.php +++ b/src/infrastructure/daemon/workers/storage/PhabricatorWorkerTrigger.php @@ -67,9 +67,10 @@ final class PhabricatorWorkerTrigger * is changed (usually because of an application edit). The `$is_reschedule` * parameter distinguishes between these cases. * - * @param int|null Epoch of the most recent successful event execution. - * @param bool `true` if we're trying to reschedule the event after - * execution; `false` if this is in response to a trigger update. + * @param int|null $last_epoch Epoch of the most recent successful event + * execution. + * @param bool $is_reschedule `true` if we're trying to reschedule the event + * after execution; `false` if this is in response to a trigger update. * @return int|null Return an epoch to schedule the next event execution, * or `null` to stop the event from executing again. */ @@ -81,10 +82,10 @@ final class PhabricatorWorkerTrigger /** * Execute the event. * - * @param int|null Epoch of previous execution, or null if this is the first - * execution. - * @param int Scheduled epoch of this execution. This may not be the same - * as the current time. + * @param int|null $last_event Epoch of previous execution, or null if this + * is the first execution. + * @param int $this_event Scheduled epoch of this execution. This may not be + * the same as the current time. * @return void */ public function executeTrigger($last_event, $this_event) { diff --git a/src/infrastructure/diff/PhabricatorDifferenceEngine.php b/src/infrastructure/diff/PhabricatorDifferenceEngine.php index 35aa63a1b8..3326a939d1 100644 --- a/src/infrastructure/diff/PhabricatorDifferenceEngine.php +++ b/src/infrastructure/diff/PhabricatorDifferenceEngine.php @@ -21,7 +21,7 @@ final class PhabricatorDifferenceEngine extends Phobject { /** * Set the name to identify the old file with. Primarily cosmetic. * - * @param string Old file name. + * @param string $old_name Old file name. * @return this * @task config */ @@ -34,7 +34,7 @@ final class PhabricatorDifferenceEngine extends Phobject { /** * Set the name to identify the new file with. Primarily cosmetic. * - * @param string New file name. + * @param string $new_name New file name. * @return this * @task config */ @@ -62,8 +62,8 @@ final class PhabricatorDifferenceEngine extends Phobject { * @{method:generateChangesetFromFileContent}, but may be useful if you need * to use a custom parser configuration, as with Diffusion. * - * @param string Entire previous file content. - * @param string Entire current file content. + * @param string $old Entire previous file content. + * @param string $new Entire current file content. * @return string Raw diff between the two files. * @task diff */ @@ -132,8 +132,8 @@ final class PhabricatorDifferenceEngine extends Phobject { * principally useful because you can feed the output to * @{class:DifferentialChangesetParser} in order to render it. * - * @param string Entire previous file content. - * @param string Entire current file content. + * @param string $old Entire previous file content. + * @param string $new Entire current file content. * @return @{class:DifferentialChangeset} Synthetic changeset. * @task diff */ diff --git a/src/infrastructure/diff/interface/PhabricatorInlineComment.php b/src/infrastructure/diff/interface/PhabricatorInlineComment.php index 613dfb08aa..4e7b86d30a 100644 --- a/src/infrastructure/diff/interface/PhabricatorInlineComment.php +++ b/src/infrastructure/diff/interface/PhabricatorInlineComment.php @@ -101,6 +101,11 @@ abstract class PhabricatorInlineComment abstract public function supportsHiding(); abstract public function isHidden(); + /** + * @return Phobject + */ + abstract public function getTransactionCommentForSave(); + public function isDraft() { return !$this->getTransactionPHID(); } diff --git a/src/infrastructure/edges/editor/PhabricatorEdgeEditor.php b/src/infrastructure/edges/editor/PhabricatorEdgeEditor.php index 588b5267b4..8ab8ddce58 100644 --- a/src/infrastructure/edges/editor/PhabricatorEdgeEditor.php +++ b/src/infrastructure/edges/editor/PhabricatorEdgeEditor.php @@ -45,10 +45,10 @@ final class PhabricatorEdgeEditor extends Phobject { * - `inverse_data` Optional, data to write on the inverse edge. If not * provided, `data` will be written. * - * @param phid Source object PHID. - * @param const Edge type constant. - * @param phid Destination object PHID. - * @param map Options map (see documentation). + * @param phid $src Source object PHID. + * @param const $type Edge type constant. + * @param phid $dst Destination object PHID. + * @param map? $options Options map (see documentation). * @return this * * @task edit @@ -67,9 +67,9 @@ final class PhabricatorEdgeEditor extends Phobject { * will be ignored. Edges are added after edges are removed, so the effect of * a remove plus an add is to overwrite. * - * @param phid Source object PHID. - * @param const Edge type constant. - * @param phid Destination object PHID. + * @param phid $src Source object PHID. + * @param const $type Edge type constant. + * @param phid $dst Destination object PHID. * @return this * * @task edit @@ -90,7 +90,6 @@ final class PhabricatorEdgeEditor extends Phobject { * (e.g., adds followed by removals) if their outcomes are not dependent, * since transactions will not be held open as long. * - * @return this * @task edit */ public function save() { diff --git a/src/infrastructure/edges/query/PhabricatorEdgeQuery.php b/src/infrastructure/edges/query/PhabricatorEdgeQuery.php index f63e827431..eeb6123abf 100644 --- a/src/infrastructure/edges/query/PhabricatorEdgeQuery.php +++ b/src/infrastructure/edges/query/PhabricatorEdgeQuery.php @@ -40,7 +40,7 @@ final class PhabricatorEdgeQuery extends PhabricatorQuery { * Find edges originating at one or more source PHIDs. You MUST provide this * to execute an edge query. * - * @param list List of source PHIDs. + * @param list $source_phids List of source PHIDs. * @return this * * @task config @@ -61,7 +61,7 @@ final class PhabricatorEdgeQuery extends PhabricatorQuery { /** * Find edges terminating at one or more destination PHIDs. * - * @param list List of destination PHIDs. + * @param list $dest_phids List of destination PHIDs. * @return this * */ @@ -74,7 +74,7 @@ final class PhabricatorEdgeQuery extends PhabricatorQuery { /** * Find edges of specific types. * - * @param list List of PhabricatorEdgeConfig type constants. + * @param list $types List of PhabricatorEdgeConfig type constants. * @return this * * @task config @@ -88,7 +88,7 @@ final class PhabricatorEdgeQuery extends PhabricatorQuery { /** * Configure the order edge results are returned in. * - * @param const Order constant. + * @param const $order Order constant. * @return this * * @task config @@ -102,7 +102,7 @@ final class PhabricatorEdgeQuery extends PhabricatorQuery { /** * When loading edges, also load edge data. * - * @param bool True to load edge data. + * @param bool $need True to load edge data. * @return this * * @task config @@ -121,8 +121,8 @@ final class PhabricatorEdgeQuery extends PhabricatorQuery { * edge type. Equivalent to building a full query, but simplifies a common * use case. * - * @param phid Source PHID. - * @param const Edge type. + * @param phid $src_phid Source PHID. + * @param const $edge_type Edge type. * @return list List of destination PHIDs. */ public static function loadDestinationPHIDs($src_phid, $edge_type) { @@ -139,9 +139,9 @@ final class PhabricatorEdgeQuery extends PhabricatorQuery { * if the edge does not exist or does not have metadata. Builds * and immediately executes a full query. * - * @param phid Source PHID. - * @param const Edge type. - * @param phid Destination PHID. + * @param phid $src_phid Source PHID. + * @param const $edge_type Edge type. + * @param phid $dest_phid Destination PHID. * @return wild Edge annotation (or null). */ public static function loadSingleEdgeData($src_phid, $edge_type, $dest_phid) { @@ -256,8 +256,8 @@ final class PhabricatorEdgeQuery extends PhabricatorQuery { * $object->attachHandles(array_select_keys($handles, $dst_phids)); * } * - * @param list? List of PHIDs to select, or empty to select all. - * @param list? List of edge types to select, or empty to select all. + * @param list? $src_phids List of PHIDs to select, or empty to select all. + * @param list? $types List of edge types to select, or empty to select all. * @return list List of matching destination PHIDs. */ public function getDestinationPHIDs( diff --git a/src/infrastructure/env/PhabricatorEnv.php b/src/infrastructure/env/PhabricatorEnv.php index 2cbb5b86ea..6c799e198c 100644 --- a/src/infrastructure/env/PhabricatorEnv.php +++ b/src/infrastructure/env/PhabricatorEnv.php @@ -646,7 +646,7 @@ final class PhabricatorEnv extends Phobject { * NOTE: This method is generally intended to reject URIs which it may be * unsafe to put in an "href" link attribute. * - * @param string URI to test. + * @param string $uri URI to test. * @return bool True if the URI identifies a web resource. * @task uri */ @@ -662,7 +662,7 @@ final class PhabricatorEnv extends Phobject { * NOTE: This method is generally intended to reject URIs which it may be * unsafe to issue a "Location:" redirect to. * - * @param string URI to test. + * @param string $uri URI to test. * @return bool True if the URI identifies a local page. * @task uri */ @@ -704,7 +704,7 @@ final class PhabricatorEnv extends Phobject { /** * Detect if a URI identifies some valid linkable remote resource. * - * @param string URI to test. + * @param string $uri URI to test. * @return bool True if a URI identifies a remote resource with an allowed * protocol. * @task uri @@ -726,7 +726,7 @@ final class PhabricatorEnv extends Phobject { * A valid linkable remote resource can be safely linked or redirected to. * This is primarily a protocol whitelist check. * - * @param string URI to test. + * @param string $raw_uri URI to test. * @return void * @task uri */ @@ -766,8 +766,8 @@ final class PhabricatorEnv extends Phobject { /** * Detect if a URI identifies a valid fetchable remote resource. * - * @param string URI to test. - * @param list Allowed protocols. + * @param string $uri URI to test. + * @param list $protocols Allowed protocols. * @return bool True if the URI is a valid fetchable remote resource. * @task uri */ @@ -789,8 +789,8 @@ final class PhabricatorEnv extends Phobject { * originating on this server. This is a primarily an address check against * the outbound address blacklist. * - * @param string URI to test. - * @param list Allowed protocols. + * @param string $raw_uri URI to test. + * @param list $protocols Allowed protocols. * @return pair Pre-resolved URI and domain. * @task uri */ @@ -861,7 +861,7 @@ final class PhabricatorEnv extends Phobject { /** * Determine if an IP address is in the outbound address blacklist. * - * @param string IP address. + * @param string $address IP address. * @return bool True if the address is blacklisted. */ public static function isBlacklistedOutboundAddress($address) { diff --git a/src/infrastructure/env/PhabricatorScopedEnv.php b/src/infrastructure/env/PhabricatorScopedEnv.php index 3bec720ae4..80a9e5893a 100644 --- a/src/infrastructure/env/PhabricatorScopedEnv.php +++ b/src/infrastructure/env/PhabricatorScopedEnv.php @@ -17,8 +17,8 @@ final class PhabricatorScopedEnv extends Phobject { /** * Override a configuration key in this scope, setting it to a new value. * - * @param string Key to override. - * @param wild New value. + * @param string $key Key to override. + * @param wild $value New value. * @return this * * @task override diff --git a/src/infrastructure/markup/PhabricatorMarkupEngine.php b/src/infrastructure/markup/PhabricatorMarkupEngine.php index 84503048d0..89f03977f8 100644 --- a/src/infrastructure/markup/PhabricatorMarkupEngine.php +++ b/src/infrastructure/markup/PhabricatorMarkupEngine.php @@ -56,10 +56,11 @@ final class PhabricatorMarkupEngine extends Phobject { * Convenience method for pushing a single object through the markup * pipeline. * - * @param PhabricatorMarkupInterface The object to render. - * @param string The field to render. - * @param PhabricatorUser User viewing the markup. - * @param object A context object for policy checks + * @param PhabricatorMarkupInterface $object The object to render. + * @param string $field The field to render. + * @param PhabricatorUser $viewer User viewing the markup. + * @param object? $context_object A context object for + * policy checks. * @return string Marked up output. * @task markup */ @@ -81,8 +82,8 @@ final class PhabricatorMarkupEngine extends Phobject { * Queue an object for markup generation when @{method:process} is * called. You can retrieve the output later with @{method:getOutput}. * - * @param PhabricatorMarkupInterface The object to render. - * @param string The field to render. + * @param PhabricatorMarkupInterface $object The object to render. + * @param string $field The field to render. * @return this * @task markup */ @@ -175,8 +176,8 @@ final class PhabricatorMarkupEngine extends Phobject { * @{method:addObject}. Before you can call this method, you must call * @{method:process}. * - * @param PhabricatorMarkupInterface The object to retrieve. - * @param string The field to retrieve. + * @param PhabricatorMarkupInterface $object The object to retrieve. + * @param string $field The field to retrieve. * @return string Processed output. * @task markup */ @@ -191,10 +192,11 @@ final class PhabricatorMarkupEngine extends Phobject { /** * Retrieve engine metadata for a given field. * - * @param PhabricatorMarkupInterface The object to retrieve. - * @param string The field to retrieve. - * @param string The engine metadata field to retrieve. - * @param wild Optional default value. + * @param PhabricatorMarkupInterface $object The object to retrieve. + * @param string $field The field to retrieve. + * @param string $metadata_key The engine metadata field + * to retrieve. + * @param wild? $default Optional default value. * @task markup */ public function getEngineMetadata( @@ -316,7 +318,7 @@ final class PhabricatorMarkupEngine extends Phobject { /** * Set the viewing user. Used to implement object permissions. * - * @param PhabricatorUser The viewing user. + * @param PhabricatorUser $viewer The viewing user. * @return this * @task markup */ @@ -328,7 +330,7 @@ final class PhabricatorMarkupEngine extends Phobject { /** * Set the context object. Used to implement object permissions. * - * @param The object in which context this remarkup is used. + * @param $object The object in which context this remarkup is used. * @return this * @task markup */ @@ -670,7 +672,7 @@ final class PhabricatorMarkupEngine extends Phobject { * * TODO: We could do a better job of this. * - * @param string Remarkup corpus to summarize. + * @param string $corpus Remarkup corpus to summarize. * @return string Summarized corpus. */ public static function summarize($corpus) { diff --git a/src/infrastructure/markup/PhabricatorMarkupInterface.php b/src/infrastructure/markup/PhabricatorMarkupInterface.php index f28c7ab50b..fdc39407b3 100644 --- a/src/infrastructure/markup/PhabricatorMarkupInterface.php +++ b/src/infrastructure/markup/PhabricatorMarkupInterface.php @@ -28,7 +28,7 @@ interface PhabricatorMarkupInterface { * * "{$phid}:{$field}" * - * @param string Field name. + * @param string $field Field name. * @return string Cache key up to 125 characters. * * @task markup @@ -39,7 +39,7 @@ interface PhabricatorMarkupInterface { /** * Build the engine the field should use. * - * @param string Field name. + * @param string $field Field name. * @return PhutilRemarkupEngine Markup engine to use. * @task markup */ @@ -49,7 +49,7 @@ interface PhabricatorMarkupInterface { /** * Return the contents of the specified field. * - * @param string Field name. + * @param string $field Field name. * @return string The raw markup contained in the field. * @task markup */ @@ -60,9 +60,9 @@ interface PhabricatorMarkupInterface { * Callback for final postprocessing of output. Normally, you can return * the output unmodified. * - * @param string Field name. - * @param string The finalized output of the engine. - * @param string The engine which generated the output. + * @param string $field Field name. + * @param string $output The finalized output of the engine. + * @param string $engine The engine which generated the output. * @return string Final output. * @task markup */ diff --git a/src/infrastructure/markup/PhutilMarkupEngine.php b/src/infrastructure/markup/PhutilMarkupEngine.php index e00ef15844..7f650a4417 100644 --- a/src/infrastructure/markup/PhutilMarkupEngine.php +++ b/src/infrastructure/markup/PhutilMarkupEngine.php @@ -8,8 +8,8 @@ abstract class PhutilMarkupEngine extends Phobject { * documentation for specific rules and blocks for what options are available * for configuration. * - * @param string Key to set in the configuration dictionary. - * @param string Value to set. + * @param string $key Key to set in the configuration dictionary. + * @param string $value Value to set. * @return this */ abstract public function setConfig($key, $value); @@ -21,8 +21,9 @@ abstract class PhutilMarkupEngine extends Phobject { * text; consult the documentation for specific rules and blocks to see what * metadata may be available in your configuration. * - * @param string Key to retrieve from metadata. - * @param mixed Default value to return if the key is not available. + * @param string $key Key to retrieve from metadata. + * @param mixed? $default Default value to return if the key is not + * available. * @return mixed Metadata property, or default value. */ abstract public function getTextMetadata($key, $default = null); diff --git a/src/infrastructure/markup/blockrule/PhutilRemarkupCodeBlockRule.php b/src/infrastructure/markup/blockrule/PhutilRemarkupCodeBlockRule.php index 8763111dd1..8cfc5060e7 100644 --- a/src/infrastructure/markup/blockrule/PhutilRemarkupCodeBlockRule.php +++ b/src/infrastructure/markup/blockrule/PhutilRemarkupCodeBlockRule.php @@ -350,7 +350,7 @@ final class PhutilRemarkupCodeBlockRule extends PhutilRemarkupBlockRule { /** * Get the extension from a filename. - * @param string "/path/to/something.name" + * @param string $name "/path/to/something.name" * @return null|string ".name" */ private function guessFilenameExtension($name) { diff --git a/src/infrastructure/markup/interface/RemarkupSyntaxDocumentationProvider.php b/src/infrastructure/markup/interface/RemarkupSyntaxDocumentationProvider.php new file mode 100644 index 0000000000..1a0e849f05 --- /dev/null +++ b/src/infrastructure/markup/interface/RemarkupSyntaxDocumentationProvider.php @@ -0,0 +1,5 @@ +getEngine(); - $is_anchor = false; - if (strncmp($link, '/', 1) == 0) { + $uri = new PhutilURIHelper($link); + $is_anchor = $uri->isAnchor(); + $starts_with_slash = $uri->isStartingWithSlash(); + if ($starts_with_slash) { $base = phutil_string_cast($engine->getConfig('uri.base')); $base = rtrim($base, '/'); $link = $base.$link; - } else if (strncmp($link, '#', 1) == 0) { + } else if ($is_anchor) { $here = $engine->getConfig('uri.here'); $link = $here.$link; - - $is_anchor = true; } if ($engine->isTextMode()) { @@ -76,7 +76,13 @@ final class PhutilRemarkupDocumentLinkRule extends PhutilRemarkupRule { return $name; } - $same_window = $engine->getConfig('uri.same-window', false); + // Check if this link points to Phorge itself. Micro-optimized. + $is_self = $is_anchor || $starts_with_slash || $uri->isSelf(); + + // For historical reasons, links opened in a different tab + // for most links as default. + // Now internal resources keep internal link, as default. + $same_window = $engine->getConfig('uri.same-window', $is_self); if ($same_window) { $target = null; } else { @@ -92,7 +98,7 @@ final class PhutilRemarkupDocumentLinkRule extends PhutilRemarkupRule { 'a', array( 'href' => $link, - 'class' => 'remarkup-link', + 'class' => $this->getRemarkupLinkClass($is_self), 'target' => $target, 'rel' => 'noreferrer', ), diff --git a/src/infrastructure/markup/markuprule/PhutilRemarkupHyperlinkRule.php b/src/infrastructure/markup/markuprule/PhutilRemarkupHyperlinkRule.php index 560aa180c3..dbfca5e5a9 100644 --- a/src/infrastructure/markup/markuprule/PhutilRemarkupHyperlinkRule.php +++ b/src/infrastructure/markup/markuprule/PhutilRemarkupHyperlinkRule.php @@ -116,7 +116,9 @@ final class PhutilRemarkupHyperlinkRule extends PhutilRemarkupRule { $engine = $this->getEngine(); - $same_window = $engine->getConfig('uri.same-window', false); + $uri = new PhutilURIHelper($link); + $is_self = $uri->isSelf(); + $same_window = $engine->getConfig('uri.same-window', $is_self); if ($same_window) { $target = null; } else { @@ -127,7 +129,7 @@ final class PhutilRemarkupHyperlinkRule extends PhutilRemarkupRule { 'a', array( 'href' => $link, - 'class' => 'remarkup-link', + 'class' => $this->getRemarkupLinkClass($is_self), 'target' => $target, 'rel' => 'noreferrer', ), diff --git a/src/infrastructure/markup/markuprule/PhutilRemarkupRule.php b/src/infrastructure/markup/markuprule/PhutilRemarkupRule.php index 4a3c1460e9..6513d0f66c 100644 --- a/src/infrastructure/markup/markuprule/PhutilRemarkupRule.php +++ b/src/infrastructure/markup/markuprule/PhutilRemarkupRule.php @@ -20,9 +20,10 @@ abstract class PhutilRemarkupRule extends Phobject { /** * Check input whether to apply RemarkupRule. If true, apply formatting. - * @param string|PhutilSafeHTML String to check and potentially format. + * @param string|PhutilSafeHTML $text String to check and potentially + * format. * @return string|PhutilSafeHTML Unchanged input if no match, or input after - * matching the formatting rule and applying the formatting. + * matching the formatting rule and applying the formatting. */ abstract public function apply($text); @@ -59,9 +60,9 @@ abstract class PhutilRemarkupRule extends Phobject { * This method acts as @{function:phutil_tag}, but checks attributes before * using them. * - * @param string Tag name. - * @param dict Tag attributes. - * @param wild Tag content. + * @param string $name Tag name. + * @param dict $attrs Tag attributes. + * @param wild? $content Tag content. * @return PhutilSafeHTML Tag object. */ protected function newTag($name, array $attrs, $content = null) { @@ -85,7 +86,7 @@ abstract class PhutilRemarkupRule extends Phobject { * Normally, you can call @{method:newTag} rather than calling this method * directly. @{method:newTag} will check attributes for you. * - * @param wild Ostensibly flat text. + * @param wild $text Ostensibly flat text. * @return string Flat text. */ protected function assertFlatText($text) { @@ -104,7 +105,7 @@ abstract class PhutilRemarkupRule extends Phobject { /** * Check whether text is flat (contains no replacement tokens) or not. * - * @param wild Ostensibly flat text. + * @param wild $text Ostensibly flat text. * @return bool True if the text is flat. */ protected function isFlatText($text) { @@ -112,4 +113,20 @@ abstract class PhutilRemarkupRule extends Phobject { return (strpos($text, PhutilRemarkupBlockStorage::MAGIC_BYTE) === false); } + /** + * Get the CSS class="" attribute for a Remarkup link. + * It's just "remarkup-link" for all cases, plus the possibility for + * designers to style external links differently. + * @param boolean $is_internal Whenever the link was internal or not. + * @return string + */ + protected function getRemarkupLinkClass($is_internal) { + // Allow developers to style esternal links differently + $classes = array('remarkup-link'); + if (!$is_internal) { + $classes[] = 'remarkup-link-ext'; + } + return implode(' ', $classes); + } + } diff --git a/src/infrastructure/markup/render.php b/src/infrastructure/markup/render.php index 84c3616fe8..eaa4e067b2 100644 --- a/src/infrastructure/markup/render.php +++ b/src/infrastructure/markup/render.php @@ -16,9 +16,9 @@ * trusted blindly, and not escaped. You should not pass user data in these * parameters. * - * @param string The name of the tag, like `a` or `div`. - * @param map A map of tag attributes. - * @param wild Content to put in the tag. + * @param string $tag The name of the tag, like `a` or `div`. + * @param map? $attributes A map of tag attributes. + * @param wild? $content Content to put in the tag. * @return PhutilSafeHTML Tag object. */ function phutil_tag($tag, array $attributes = array(), $content = null) { diff --git a/src/infrastructure/markup/rule/PhabricatorKeyboardRemarkupRule.php b/src/infrastructure/markup/rule/PhabricatorKeyboardRemarkupRule.php index c5344a692c..a28735cfe9 100644 --- a/src/infrastructure/markup/rule/PhabricatorKeyboardRemarkupRule.php +++ b/src/infrastructure/markup/rule/PhabricatorKeyboardRemarkupRule.php @@ -22,7 +22,7 @@ final class PhabricatorKeyboardRemarkupRule extends PhutilRemarkupRule { foreach ($keys as $k => $v) { $v = trim($v, " \n"); $v = preg_replace('/\\\\(.)/', '\\1', $v); - if (!strlen($v)) { + if ($v === '') { unset($keys[$k]); continue; } diff --git a/src/infrastructure/markup/rule/PhabricatorObjectRemarkupRule.php b/src/infrastructure/markup/rule/PhabricatorObjectRemarkupRule.php index b0399527b0..c0311ea236 100644 --- a/src/infrastructure/markup/rule/PhabricatorObjectRemarkupRule.php +++ b/src/infrastructure/markup/rule/PhabricatorObjectRemarkupRule.php @@ -258,7 +258,7 @@ abstract class PhabricatorObjectRemarkupRule extends PhutilRemarkupRule { * This is intended to make it easy to write unit tests for object remarkup * rules. Production code is not normally expected to call this method. * - * @param string Text to match rules against. + * @param string $text Text to match rules against. * @return wild Matches, suitable for writing unit tests against. */ public function extractReferences($text) { diff --git a/src/infrastructure/parser/PhutilURIHelper.php b/src/infrastructure/parser/PhutilURIHelper.php new file mode 100644 index 0000000000..2ead1b1403 --- /dev/null +++ b/src/infrastructure/parser/PhutilURIHelper.php @@ -0,0 +1,78 @@ +uriStr = phutil_string_cast($uri); + + // A PhutilURI may be useful. If available, import that as-is. + // Note that the constructor PhutilURI(string) is a bit expensive. + if ($uri instanceof PhutilURI) { + $this->phutilUri = $uri; + } + } + + /** + * Check if the URI points to Phorge itself. + * @return bool + */ + public function isSelf() { + // The backend prefers a PhutilURI object, if available. + $uri = $this->phutilUri ? $this->phutilUri : $this->uriStr; + return PhabricatorEnv::isSelfURI($uri); + } + + /** + * Check whenever an URI is just a simple fragment without path and protocol. + * @return bool + */ + public function isAnchor() { + return $this->isStartingWithChar('#'); + } + + /** + * Check whenever an URI starts with a slash (no protocol, etc.) + * @return bool + */ + public function isStartingWithSlash() { + return $this->isStartingWithChar('/'); + } + + /** + * A sane default. + */ + public function __toString() { + return $this->uriStr; + } + + /** + * Check whenever the URI starts with the provided character. + * @param string $char String that MUST have length of 1. + * @return bool + */ + private function isStartingWithChar($char) { + return strncmp($this->uriStr, $char, 1) === 0; + } + +} diff --git a/src/infrastructure/parser/__tests__/PhutilURIHelperTestCase.php b/src/infrastructure/parser/__tests__/PhutilURIHelperTestCase.php new file mode 100644 index 0000000000..4c3a43323c --- /dev/null +++ b/src/infrastructure/parser/__tests__/PhutilURIHelperTestCase.php @@ -0,0 +1,63 @@ +getDomain(); + $tests[] = array('base uri', $base, true, false, false); + $tests[] = array('base uri anchor', "{$base}#asd", true, false, false); + } + + foreach ($tests as $test) { + $name = $test[0]; + $uri = $test[1]; + $is_self = $test[2]; + $is_anchor = $test[3]; + $is_slash = $test[4]; + + // Test input variants for the constructor of PhutilURIHelper. + $uri_variants = array( + $uri, + new PhutilURI($uri), + ); + foreach ($uri_variants as $variant_uri) { + + $test_name = pht("test %s value '%s' (from '%s' type %s)", + $name, $variant_uri, $uri, phutil_describe_type($variant_uri)); + + $uri = new PhutilURIHelper($variant_uri); + + $this->assertEqual($is_self, $uri->isSelf(), + pht('%s - points to myself', $test_name)); + + $this->assertEqual($is_anchor, $uri->isAnchor(), + pht('%s - is just an anchor', $test_name)); + + $this->assertEqual($is_slash, $uri->isStartingWithSlash(), + pht('%s - is starting with slash', $test_name)); + } + } + } +} diff --git a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php index cdd941a5f4..7d57df7c35 100644 --- a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php +++ b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php @@ -718,9 +718,9 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery * * This method will then return a composable clause for inclusion in WHERE. * - * @param AphrontDatabaseConnection Connection query will execute on. - * @param list Column description dictionaries. - * @param map Additional construction options. + * @param AphrontDatabaseConnection $conn Connection query will execute on. + * @param list $columns Column description dictionaries. + * @param map $options Additional construction options. * @return string Query clause. * @task paging */ @@ -884,7 +884,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery * across individual orderable columns. This offers greater control but is * also more involved. * - * @param string Key of a builtin order supported by this query. + * @param string $order Key of a builtin order supported by this query. * @return this * @task order */ @@ -917,7 +917,8 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery * This is a high-level method which works alongside @{method:setOrder}. For * lower-level control over order vectors, use @{method:setOrderVector}. * - * @param PhabricatorQueryOrderVector|list List of order keys. + * @param PhabricatorQueryOrderVector|list $vector List of order + * keys. * @return this * @task order */ @@ -1041,7 +1042,8 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery * To set an order vector, specify a list of order keys as provided by * @{method:getOrderableColumns}. * - * @param PhabricatorQueryOrderVector|list List of order keys. + * @param PhabricatorQueryOrderVector|list $vector List of order + * keys. * @return this * @task order */ @@ -1353,8 +1355,9 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery * - Find users with shirt sizes "X" or "XL". * - Find shoes with size "13". * - * @param PhabricatorCustomFieldIndexStorage Table where the index is stored. - * @param string|list One or more values to filter by. + * @param PhabricatorCustomFieldIndexStorage $index Table where the index is + * stored. + * @param string|list $value One or more values to filter by. * @return this * @task appsearch */ @@ -1403,9 +1406,10 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery * `5` will match fields with values `3`, `4`, or `5`. Providing `null` for * either end of the range will leave that end of the constraint open. * - * @param PhabricatorCustomFieldIndexStorage Table where the index is stored. - * @param int|null Minimum permissible value, inclusive. - * @param int|null Maximum permissible value, inclusive. + * @param PhabricatorCustomFieldIndexStorage $index Table where the index is + * stored. + * @param int|null $min Minimum permissible value, inclusive. + * @param int|null $max Maximum permissible value, inclusive. * @return this * @task appsearch */ @@ -1449,7 +1453,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery * See @{method:getPrimaryTableAlias} if the column needs to be qualified with * a table alias. * - * @param AphrontDatabaseConnection Connection executing queries. + * @param AphrontDatabaseConnection $conn Connection executing queries. * @return PhutilQueryString Column name. * @task appsearch */ @@ -1509,7 +1513,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery /** * Construct a GROUP BY clause appropriate for ApplicationSearch constraints. * - * @param AphrontDatabaseConnection Connection executing the query. + * @param AphrontDatabaseConnection $conn Connection executing the query. * @return string Group clause. * @task appsearch */ @@ -1531,7 +1535,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery * Construct a JOIN clause appropriate for applying ApplicationSearch * constraints. * - * @param AphrontDatabaseConnection Connection executing the query. + * @param AphrontDatabaseConnection $conn Connection executing the query. * @return string Join clause. * @task appsearch */ @@ -1653,7 +1657,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery * Construct a WHERE clause appropriate for applying ApplicationSearch * constraints. * - * @param AphrontDatabaseConnection Connection executing the query. + * @param AphrontDatabaseConnection $conn Connection executing the query. * @return list Where clause parts. * @task appsearch */ @@ -2583,9 +2587,9 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery * Convenience method for specifying edge logic constraints with a list of * PHIDs. * - * @param const Edge constant. - * @param const Constraint operator. - * @param list List of PHIDs. + * @param const $edge_type Edge constant. + * @param const $operator Constraint operator. + * @param list $phids List of PHIDs. * @return this * @task edgelogic */ @@ -3091,7 +3095,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery * Queries are always constrained to include only results from spaces the * viewer has access to. * - * @param list + * @param list $space_phids * @task spaces */ public function withSpacePHIDs(array $space_phids) { @@ -3135,7 +3139,7 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery * viewer has access to see with any explicit constraint on spaces added by * @{method:withSpacePHIDs}. * - * @param AphrontDatabaseConnection Database connection. + * @param AphrontDatabaseConnection $conn Database connection. * @return string Part of a WHERE clause. * @task spaces */ diff --git a/src/infrastructure/query/policy/PhabricatorPolicyAwareQuery.php b/src/infrastructure/query/policy/PhabricatorPolicyAwareQuery.php index c43edaefcb..1faf45d63d 100644 --- a/src/infrastructure/query/policy/PhabricatorPolicyAwareQuery.php +++ b/src/infrastructure/query/policy/PhabricatorPolicyAwareQuery.php @@ -57,7 +57,7 @@ abstract class PhabricatorPolicyAwareQuery extends PhabricatorOffsetPagedQuery { * according to the viewer's capabilities. You must set a viewer to execute * a policy query. * - * @param PhabricatorUser The viewing user. + * @param PhabricatorUser $viewer The viewing user. * @return this * @task config */ @@ -473,8 +473,8 @@ abstract class PhabricatorPolicyAwareQuery extends PhabricatorOffsetPagedQuery { * automatically populated as a side effect of objects surviving policy * filtering. * - * @param map Objects to add to the query - * workspace. + * @param map $objects Objects to add to + * the query workspace. * @return this * @task workspace */ @@ -506,7 +506,7 @@ abstract class PhabricatorPolicyAwareQuery extends PhabricatorOffsetPagedQuery { * searches both the current query's workspace and the workspaces of parent * queries. * - * @param list List of PHIDs to retrieve. + * @param list $phids List of PHIDs to retrieve. * @return this * @task workspace */ @@ -536,7 +536,7 @@ abstract class PhabricatorPolicyAwareQuery extends PhabricatorOffsetPagedQuery { * PHIDs which are "in flight" are actively being queried for. Using this * list can prevent infinite query loops by aborting queries which cycle. * - * @param list List of PHIDs which are now in flight. + * @param list $phids List of PHIDs which are now in flight. * @return this */ public function putPHIDsInFlight(array $phids) { @@ -606,7 +606,7 @@ abstract class PhabricatorPolicyAwareQuery extends PhabricatorOffsetPagedQuery { * return new results. Generally, you should adjust a cursor position based * on the provided result page. * - * @param list The current page of results. + * @param list $page The current page of results. * @return void * @task policyimpl */ @@ -627,7 +627,7 @@ abstract class PhabricatorPolicyAwareQuery extends PhabricatorOffsetPagedQuery { * This method will only be called if data is available. Implementations * do not need to handle the case of no results specially. * - * @param list Results from `loadPage()`. + * @param list $page Results from `loadPage()`. * @return list Objects for policy filtering. * @task policyimpl */ @@ -650,7 +650,7 @@ abstract class PhabricatorPolicyAwareQuery extends PhabricatorOffsetPagedQuery { * This method will only be called if data is available. Implementations do * not need to handle the case of no results specially. * - * @param list Results from @{method:willFilterPage()}. + * @param list $page Results from @{method:willFilterPage()}. * @return list Objects after additional * non-policy processing. */ @@ -665,8 +665,8 @@ abstract class PhabricatorPolicyAwareQuery extends PhabricatorOffsetPagedQuery { * filtered for policy reasons. The query should remove them from any cached * or partial result sets. * - * @param list List of objects that should not be returned by alternate - * result mechanisms. + * @param list $results List of objects that should not be returned by + * alternate result mechanisms. * @return void * @task policyimpl */ @@ -680,7 +680,7 @@ abstract class PhabricatorPolicyAwareQuery extends PhabricatorOffsetPagedQuery { * used by @{class:PhabricatorCursorPagedPolicyAwareQuery} to reverse results * that are queried during reverse paging. * - * @param list Query results. + * @param list $results Query results. * @return list Final results. * @task policyimpl */ diff --git a/src/infrastructure/storage/future/QueryFuture.php b/src/infrastructure/storage/future/QueryFuture.php index 9878581214..0f1fc3b9d4 100644 --- a/src/infrastructure/storage/future/QueryFuture.php +++ b/src/infrastructure/storage/future/QueryFuture.php @@ -51,7 +51,7 @@ final class QueryFuture extends Future { } public function isReady() { - if ($this->result !== null || $this->exception) { + if ($this->canResolve()) { return true; } @@ -105,7 +105,7 @@ final class QueryFuture extends Future { $this->processResults($this->conn->resolveAsyncQueries($conns, $asyncs)); - if ($this->result !== null || $this->exception) { + if ($this->canResolve()) { return true; } return false; diff --git a/src/infrastructure/storage/lisk/LiskDAO.php b/src/infrastructure/storage/lisk/LiskDAO.php index 2e81b4641a..1915bd71ee 100644 --- a/src/infrastructure/storage/lisk/LiskDAO.php +++ b/src/infrastructure/storage/lisk/LiskDAO.php @@ -221,7 +221,8 @@ abstract class LiskDAO extends Phobject * return a new connection. Lisk handles connection caching and management; * do not perform caching deeper in the stack. * - * @param string Mode, either 'r' (reading) or 'w' (reading and writing). + * @param string $mode Mode, either 'r' (reading) or 'w' (reading and + * writing). * @return AphrontDatabaseConnection New database connection. * @task conn */ @@ -245,7 +246,7 @@ abstract class LiskDAO extends Phobject /** * Get an existing, cached connection for this object. * - * @param mode Connection mode. + * @param mode $mode Connection mode. * @return AphrontDatabaseConnection|null Connection, if it exists in cache. * @task conn */ @@ -261,8 +262,9 @@ abstract class LiskDAO extends Phobject /** * Store a connection in the connection cache. * - * @param mode Connection mode. - * @param AphrontDatabaseConnection Connection to cache. + * @param mode $mode Connection mode. + * @param AphrontDatabaseConnection $connection Connection to cache. + * @param bool? $force_unique * @return this * @task conn */ @@ -291,7 +293,8 @@ abstract class LiskDAO extends Phobject * This overrides all connection management and forces the object to use * a specific connection when interacting with the database. * - * @param AphrontDatabaseConnection Connection to force this object to use. + * @param AphrontDatabaseConnection $connection Connection to force this + * object to use. * @task conn */ public function setForcedConnection(AphrontDatabaseConnection $connection) { @@ -397,12 +400,12 @@ abstract class LiskDAO extends Phobject /** - * Determine the setting of a configuration option for this class of objects. + * Determine the setting of a configuration option for this class of objects. * - * @param const Option name, one of the CONFIG_* constants. - * @return mixed Option value, if configured (null if unavailable). + * @param const $option_name Option name, one of the CONFIG_* constants. + * @return mixed Option value, if configured (null if unavailable). * - * @task config + * @task config */ public function getConfigOption($option_name) { $options = $this->getLiskMetadata('config'); @@ -426,7 +429,7 @@ abstract class LiskDAO extends Phobject * * $dog = id(new Dog())->load($dog_id); * - * @param int Numeric ID identifying the object to load. + * @param int $id Numeric ID identifying the object to load. * @return obj|null Identified object, or null if it does not exist. * * @task load @@ -468,7 +471,7 @@ abstract class LiskDAO extends Phobject * * The pattern and arguments are as per queryfx(). * - * @param string queryfx()-style SQL WHERE clause. + * @param string $pattern queryfx()-style SQL WHERE clause. * @param ... Zero or more conversions. * @return dict Dictionary of matching objects, keyed on ID. * @@ -489,7 +492,7 @@ abstract class LiskDAO extends Phobject * query. See loadAllWhere(). This method is similar, but returns a single * result instead of a list. * - * @param string queryfx()-style SQL WHERE clause. + * @param string $pattern queryfx()-style SQL WHERE clause. * @param ... Zero or more conversions. * @return obj|null Matching object, or null if no object matches. * @@ -574,8 +577,8 @@ abstract class LiskDAO extends Phobject * convenient to pull data from elsewhere directly (e.g., a complicated * join via @{method:queryData}) and then load from an array representation. * - * @param dict Dictionary of properties, which should be equivalent to - * selecting a row from the table or calling + * @param dict $row Dictionary of properties, which should be equivalent + * to selecting a row from the table or calling * @{method:getProperties}. * @return this * @@ -649,7 +652,7 @@ abstract class LiskDAO extends Phobject * * This is a lot messier than @{method:loadAllWhere}, but more flexible. * - * @param list List of property dictionaries. + * @param list $rows List of property dictionaries. * @return dict List of constructed objects, keyed on ID. * * @task load @@ -690,7 +693,7 @@ abstract class LiskDAO extends Phobject * Set unique ID identifying this object. You normally don't need to call this * method unless with `IDS_MANUAL`. * - * @param mixed Unique ID. + * @param mixed $id Unique ID. * @return this * @task save */ @@ -723,7 +726,7 @@ abstract class LiskDAO extends Phobject /** * Test if a property exists. * - * @param string Property name. + * @param string $property Property name. * @return bool True if the property exists. * @task info */ @@ -798,9 +801,9 @@ abstract class LiskDAO extends Phobject /** * Get or build the database connection for this object. * - * @param string 'r' for read, 'w' for read/write. - * @param bool True to force a new connection. The connection will not - * be retrieved from or saved into the connection cache. + * @param string $mode 'r' for read, 'w' for read/write. + * @param bool? $force_new True to force a new connection. The connection + * will not be retrieved from or saved into the connection cache. * @return AphrontDatabaseConnection Lisk connection object. * * @task info @@ -1038,7 +1041,8 @@ abstract class LiskDAO extends Phobject /** * Internal implementation of INSERT and REPLACE. * - * @param const Either "INSERT" or "REPLACE", to force the desired mode. + * @param const $mode Either "INSERT" or "REPLACE", to force the desired + * mode. * @return this * * @task save @@ -1272,7 +1276,7 @@ abstract class LiskDAO extends Phobject * Reads the value from a field. Override this method for custom behavior * of @{method:getField} instead of overriding getField directly. * - * @param string Canonical field name + * @param string $field Canonical field name * @return mixed Value of the field * * @task hook @@ -1288,8 +1292,8 @@ abstract class LiskDAO extends Phobject * Writes a value to a field. Override this method for custom behavior of * setField($value) instead of overriding setField directly. * - * @param string Canonical field name - * @param mixed Value to write + * @param string $field Canonical field name + * @param mixed $value Value to write * * @task hook */ @@ -1476,7 +1480,8 @@ abstract class LiskDAO extends Phobject * Long-running processes can use this method to clean up connections which * have not been used recently. * - * @param int Close connections with no activity for this many seconds. + * @param int $idle_window Close connections with no activity for this many + * seconds. * @return void */ public static function closeInactiveConnections($idle_window) { @@ -1576,8 +1581,8 @@ abstract class LiskDAO extends Phobject /** * Black magic. Builds implied get*() and set*() for all properties. * - * @param string Method name. - * @param list Argument vector. + * @param string $method Method name. + * @param list $args Argument vector. * @return mixed get*() methods return the property value. set*() methods * return $this. * @task util @@ -1652,8 +1657,10 @@ abstract class LiskDAO extends Phobject /** * Increments a named counter and returns the next value. * - * @param AphrontDatabaseConnection Database where the counter resides. - * @param string Counter name to create or increment. + * @param AphrontDatabaseConnection $conn_w Database where the counter + * resides. + * @param string $counter_name Counter name to create + * or increment. * @return int Next counter value. * * @task util @@ -1686,8 +1693,9 @@ abstract class LiskDAO extends Phobject /** * Returns the current value of a named counter. * - * @param AphrontDatabaseConnection Database where the counter resides. - * @param string Counter name to read. + * @param AphrontDatabaseConnection $conn_r Database where the counter + * resides. + * @param string $counter_name Counter name to read. * @return int|null Current value, or `null` if the counter does not exist. * * @task util @@ -1714,8 +1722,10 @@ abstract class LiskDAO extends Phobject * * If the counter does not exist, it is created. * - * @param AphrontDatabaseConnection Database where the counter resides. - * @param string Counter name to create or overwrite. + * @param AphrontDatabaseConnection $conn_w Database where the counter + * resides. + * @param string $counter_name Counter name to create or overwrite. + * @param int $counter_value * @return void * * @task util diff --git a/src/infrastructure/storage/lisk/LiskMigrationIterator.php b/src/infrastructure/storage/lisk/LiskMigrationIterator.php index edd31c8123..2d2f8c98e9 100644 --- a/src/infrastructure/storage/lisk/LiskMigrationIterator.php +++ b/src/infrastructure/storage/lisk/LiskMigrationIterator.php @@ -26,6 +26,7 @@ final class LiskMigrationIterator extends PhutilBufferedIterator { $this->cursor = 0; } + #[\ReturnTypeWillChange] public function key() { return $this->current()->getID(); } diff --git a/src/infrastructure/storage/lisk/LiskRawMigrationIterator.php b/src/infrastructure/storage/lisk/LiskRawMigrationIterator.php index a7edbd7f91..3d8954a269 100644 --- a/src/infrastructure/storage/lisk/LiskRawMigrationIterator.php +++ b/src/infrastructure/storage/lisk/LiskRawMigrationIterator.php @@ -16,6 +16,7 @@ final class LiskRawMigrationIterator extends PhutilBufferedIterator { $this->cursor = 0; } + #[\ReturnTypeWillChange] public function key() { return idx($this->current(), $this->column); } diff --git a/src/infrastructure/storage/lisk/PhabricatorQueryIterator.php b/src/infrastructure/storage/lisk/PhabricatorQueryIterator.php index cc88678cdf..70e55509b9 100644 --- a/src/infrastructure/storage/lisk/PhabricatorQueryIterator.php +++ b/src/infrastructure/storage/lisk/PhabricatorQueryIterator.php @@ -18,6 +18,7 @@ final class PhabricatorQueryIterator extends PhutilBufferedIterator { $this->pager = $pager; } + #[\ReturnTypeWillChange] public function key() { return $this->current()->getID(); } diff --git a/src/infrastructure/util/PhabricatorGlobalLock.php b/src/infrastructure/util/PhabricatorGlobalLock.php index 2dd38a50c7..c82c2f0604 100644 --- a/src/infrastructure/util/PhabricatorGlobalLock.php +++ b/src/infrastructure/util/PhabricatorGlobalLock.php @@ -88,7 +88,7 @@ final class PhabricatorGlobalLock extends PhutilLock { * (somewhat arbitrarily). In most cases this is fine, but this method can * be used to lock on a specific connection. * - * @param AphrontDatabaseConnection + * @param AphrontDatabaseConnection $conn * @return this */ public function setExternalConnection(AphrontDatabaseConnection $conn) { diff --git a/src/infrastructure/util/PhabricatorHash.php b/src/infrastructure/util/PhabricatorHash.php index 8916b37a0e..2b3fe8ab4c 100644 --- a/src/infrastructure/util/PhabricatorHash.php +++ b/src/infrastructure/util/PhabricatorHash.php @@ -11,7 +11,8 @@ final class PhabricatorHash extends Phobject { * Because a SHA1 collision is now known, this method should be considered * weak. Callers should prefer @{method:digestWithNamedKey}. * - * @param string Input string. + * @param string $string Input string. + * @param string? $key * @return string 32-byte hexadecimal SHA1+HMAC hash. */ public static function weakDigest($string, $key = null) { @@ -38,7 +39,7 @@ final class PhabricatorHash extends Phobject { * This method emphasizes compactness, and should not be used for security * related hashing (for general purpose hashing, see @{method:digest}). * - * @param string Input string. + * @param string $string Input string. * @return string 12-byte, case-sensitive, mostly-alphanumeric hash of * the string. */ @@ -72,7 +73,7 @@ final class PhabricatorHash extends Phobject { * much stuff we're breaking by switching to it. For additional discussion, * see T13045. * - * @param string Input string. + * @param string $string Input string. * @return string 12-byte, case-sensitive, purely-alphanumeric hash of * the string. */ @@ -149,8 +150,8 @@ final class PhabricatorHash extends Phobject { * maintaining a high degree of collision resistance and a moderate degree * of human readability. * - * @param string The string to shorten. - * @param int Maximum length of the result. + * @param string $string The string to shorten. + * @param int $length Maximum length of the result. * @return string String shortened in a collision-resistant way. */ public static function digestToLength($string, $length) { diff --git a/src/infrastructure/util/password/PhabricatorPasswordHasher.php b/src/infrastructure/util/password/PhabricatorPasswordHasher.php index fe35d2c296..ac7c9963e9 100644 --- a/src/infrastructure/util/password/PhabricatorPasswordHasher.php +++ b/src/infrastructure/util/password/PhabricatorPasswordHasher.php @@ -101,7 +101,7 @@ abstract class PhabricatorPasswordHasher extends Phobject { /** * Produce a password hash. * - * @param PhutilOpaqueEnvelope Text to be hashed. + * @param PhutilOpaqueEnvelope $envelope Text to be hashed. * @return PhutilOpaqueEnvelope Hashed text. * @task hasher */ @@ -114,8 +114,8 @@ abstract class PhabricatorPasswordHasher extends Phobject { * The default implementation checks for equality; if a hasher embeds salt in * hashes it should override this method and perform a salt-aware comparison. * - * @param PhutilOpaqueEnvelope Password to compare. - * @param PhutilOpaqueEnvelope Bare password hash. + * @param PhutilOpaqueEnvelope $password Password to compare. + * @param PhutilOpaqueEnvelope $hash Bare password hash. * @return bool True if the passwords match. * @task hasher */ @@ -137,7 +137,7 @@ abstract class PhabricatorPasswordHasher extends Phobject { * have (for example) an internal cost function may be able to upgrade an * existing hash to a stronger one with a higher cost. * - * @param PhutilOpaqueEnvelope Bare hash. + * @param PhutilOpaqueEnvelope $hash Bare hash. * @return bool True if the hash can be upgraded without * changing the algorithm (for example, to a * higher cost). @@ -154,7 +154,7 @@ abstract class PhabricatorPasswordHasher extends Phobject { /** * Get the hash of a password for storage. * - * @param PhutilOpaqueEnvelope Password text. + * @param PhutilOpaqueEnvelope $envelope Password text. * @return PhutilOpaqueEnvelope Hashed text. * @task hashing */ @@ -349,7 +349,7 @@ abstract class PhabricatorPasswordHasher extends Phobject { /** * Generate a new hash for a password, using the best available hasher. * - * @param PhutilOpaqueEnvelope Password to hash. + * @param PhutilOpaqueEnvelope $password Password to hash. * @return PhutilOpaqueEnvelope Hashed password, using best available * hasher. * @task hashing @@ -364,8 +364,8 @@ abstract class PhabricatorPasswordHasher extends Phobject { /** * Compare a password to a stored hash. * - * @param PhutilOpaqueEnvelope Password to compare. - * @param PhutilOpaqueEnvelope Stored password hash. + * @param PhutilOpaqueEnvelope $password Password to compare. + * @param PhutilOpaqueEnvelope $hash Stored password hash. * @return bool True if the passwords match. * @task hashing */ @@ -383,7 +383,7 @@ abstract class PhabricatorPasswordHasher extends Phobject { /** * Get the human-readable algorithm name for a given hash. * - * @param PhutilOpaqueEnvelope Storage hash. + * @param PhutilOpaqueEnvelope $hash Storage hash. * @return string Human-readable algorithm name. */ public static function getCurrentAlgorithmName(PhutilOpaqueEnvelope $hash) { diff --git a/src/view/AphrontView.php b/src/view/AphrontView.php index 669742f29b..f1773c7579 100644 --- a/src/view/AphrontView.php +++ b/src/view/AphrontView.php @@ -16,7 +16,7 @@ abstract class AphrontView extends Phobject /** * Set the user viewing this element. * - * @param PhabricatorUser Viewing user. + * @param PhabricatorUser $viewer Viewing user. * @return this */ public function setViewer(PhabricatorUser $viewer) { @@ -100,7 +100,7 @@ abstract class AphrontView extends Phobject * This method will only work if the view supports children, which is * determined by @{method:canAppendChild}. * - * @param wild Something renderable. + * @param wild $child Something renderable. * @return this */ final public function appendChild($child) { @@ -152,7 +152,7 @@ abstract class AphrontView extends Phobject * NOTE: Because View children are not rendered, a View which renders down * to nothing will not be reduced by this method. * - * @param list Renderable children. + * @param list $children Renderable children. * @return list Reduced list of children. * @task children */ diff --git a/src/view/control/AphrontCursorPagerView.php b/src/view/control/AphrontCursorPagerView.php index cdb9562624..cf4563fb9e 100644 --- a/src/view/control/AphrontCursorPagerView.php +++ b/src/view/control/AphrontCursorPagerView.php @@ -72,8 +72,9 @@ final class AphrontCursorPagerView extends AphrontView { public function sliceResults(array $results) { if (count($results) > $this->getPageSize()) { - $offset = ($this->beforeID ? count($results) - $this->getPageSize() : 0); - $results = array_slice($results, $offset, $this->getPageSize(), true); + $page_size = (int)$this->getPageSize(); + $offset = ($this->beforeID ? count($results) - $page_size : 0); + $results = array_slice($results, $offset, $page_size, true); $this->moreResults = true; } return $results; diff --git a/src/view/control/AphrontTableView.php b/src/view/control/AphrontTableView.php index ab1f6be0ed..697e895c3d 100644 --- a/src/view/control/AphrontTableView.php +++ b/src/view/control/AphrontTableView.php @@ -105,7 +105,7 @@ final class AphrontTableView extends AphrontView { * * list($sort, $reverse) = AphrontTableView::parseSortParam($sort_param); * - * @param string Sort request parameter. + * @param string $sort Sort request parameter. * @return pair Sort value, sort direction. */ public static function parseSort($sort) { diff --git a/src/view/control/AphrontTypeaheadTemplateView.php b/src/view/control/AphrontTypeaheadTemplateView.php index e536a7a7cf..08c7140edd 100644 --- a/src/view/control/AphrontTypeaheadTemplateView.php +++ b/src/view/control/AphrontTypeaheadTemplateView.php @@ -34,12 +34,6 @@ final class AphrontTypeaheadTemplateView extends AphrontView { $id = $this->id; $name = $this->getName(); - $values = nonempty($this->getValue(), array()); - - $tokens = array(); - foreach ($values as $key => $value) { - $tokens[] = $this->renderToken($key, $value); - } $input = javelin_tag( 'input', diff --git a/src/view/form/AphrontFormView.php b/src/view/form/AphrontFormView.php index 3022a91f9c..de1143ab85 100644 --- a/src/view/form/AphrontFormView.php +++ b/src/view/form/AphrontFormView.php @@ -120,7 +120,7 @@ final class AphrontFormView extends AphrontView { * controls. It will propagate some information from the form to the * control to simplify rendering. * - * @param AphrontFormControl Control to append. + * @param AphrontFormControl $control Control to append. * @return this */ public function appendControl(AphrontFormControl $control) { diff --git a/src/view/form/control/AphrontFormControl.php b/src/view/form/control/AphrontFormControl.php index 83e0207241..af317d257c 100644 --- a/src/view/form/control/AphrontFormControl.php +++ b/src/view/form/control/AphrontFormControl.php @@ -59,7 +59,7 @@ abstract class AphrontFormControl extends AphrontView { /** * Set the Caption * The Caption shows a tip usually nearby the related input field. - * @param string|PhutilSafeHTML|null + * @param string|PhutilSafeHTML|null $caption * @return self */ public function setCaption($caption) { diff --git a/src/view/form/control/AphrontFormDateControlValue.php b/src/view/form/control/AphrontFormDateControlValue.php index 873ad44fe4..72221dd287 100644 --- a/src/view/form/control/AphrontFormDateControlValue.php +++ b/src/view/form/control/AphrontFormDateControlValue.php @@ -207,10 +207,14 @@ final class AphrontFormDateControlValue extends Phobject { $datetime->format($this->getDateFormat()), $datetime->format($this->getTimeFormat()), ); - - return array($date, $time); } + /** + * Create a DateTime object including timezone + * @param string $date Date, like "2024-08-20" or "2024-07-1" or such + * @param string|null $time Time, like "12:00 AM" or such + * @return DateTime|null + */ private function newDateTime($date, $time) { $date = $this->getStandardDateFormat($date); $time = $this->getStandardTimeFormat($time); @@ -282,10 +286,16 @@ final class AphrontFormDateControlValue extends Phobject { } } + /** + * @return DateTime|null + */ public function getDateTime() { return $this->newDateTime($this->valueDate, $this->valueTime); } + /** + * @return DateTimeZone + */ private function getTimezone() { if ($this->zone) { return $this->zone; diff --git a/src/view/form/control/PhabricatorRemarkupControl.php b/src/view/form/control/PhabricatorRemarkupControl.php index ba7950bcc9..2a77d12b82 100644 --- a/src/view/form/control/PhabricatorRemarkupControl.php +++ b/src/view/form/control/PhabricatorRemarkupControl.php @@ -255,7 +255,7 @@ final class PhabricatorRemarkupControl $actions['fa-book'] = array( 'tip' => pht('Help'), 'align' => 'right', - 'href' => PhabricatorEnv::getDoclink('Remarkup Reference'), + 'href' => '/reference/remarkup/', ); $mode_actions = array(); diff --git a/src/view/page/PhabricatorStandardPageView.php b/src/view/page/PhabricatorStandardPageView.php index 005ede23a7..84581e991c 100644 --- a/src/view/page/PhabricatorStandardPageView.php +++ b/src/view/page/PhabricatorStandardPageView.php @@ -378,9 +378,8 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView /** * Insert a HTML element into of the page to render. - * Used by PhameBlogViewController. * - * @param PhutilSafeHTML HTML header to add + * @param PhutilSafeHTML $html HTML header to add */ public function addHeadItem($html) { if ($html instanceof PhutilSafeHTML) { diff --git a/src/view/page/menu/PhabricatorMainMenuView.php b/src/view/page/menu/PhabricatorMainMenuView.php index f0b2bbbe83..8db27628d9 100644 --- a/src/view/page/menu/PhabricatorMainMenuView.php +++ b/src/view/page/menu/PhabricatorMainMenuView.php @@ -293,35 +293,14 @@ final class PhabricatorMainMenuView extends AphrontView { } private function renderPhabricatorLogo() { - $custom_header = PhabricatorCustomLogoConfigType::getLogoImagePHID(); - $logo_style = array(); + $custom_header = PhabricatorCustomLogoConfigType::getLogoImagePHID(); if ($custom_header) { - $cache = PhabricatorCaches::getImmutableCache(); - $cache_key_logo = 'ui.custom-header.logo-phid.v3.'.$custom_header; - - $logo_uri = $cache->getKey($cache_key_logo); - if (!$logo_uri) { - // NOTE: If the file policy has been changed to be restrictive, we'll - // miss here and just show the default logo. The cache will fill later - // when someone who can see the file loads the page. This might be a - // little spooky, see T11982. - $files = id(new PhabricatorFileQuery()) - ->setViewer($this->getViewer()) - ->withPHIDs(array($custom_header)) - ->execute(); - $file = head($files); - if ($file) { - $logo_uri = $file->getViewURI(); - $cache->setKey($cache_key_logo, $logo_uri); - } - } - - if ($logo_uri) { - $logo_style[] = 'background-size: 40px 40px;'; - $logo_style[] = 'background-position: 0 0;'; - $logo_style[] = 'background-image: url('.$logo_uri.')'; - } + $viewer = $this->getViewer(); + $logo_uri = PhabricatorCustomLogoConfigType::getLogoURI($viewer); + $logo_style[] = 'background-size: 40px 40px;'; + $logo_style[] = 'background-position: 0 0;'; + $logo_style[] = 'background-image: url('.$logo_uri.')'; } $logo_node = phutil_tag( @@ -331,7 +310,6 @@ final class PhabricatorMainMenuView extends AphrontView { 'style' => implode(' ', $logo_style), )); - $wordmark_text = PhabricatorCustomLogoConfigType::getLogoWordmark(); if (!phutil_nonempty_string($wordmark_text)) { $wordmark_text = PlatformSymbols::getPlatformServerName(); diff --git a/src/view/phui/PHUIBoxView.php b/src/view/phui/PHUIBoxView.php index 7068f8811c..2fb7f68802 100644 --- a/src/view/phui/PHUIBoxView.php +++ b/src/view/phui/PHUIBoxView.php @@ -6,6 +6,7 @@ final class PHUIBoxView extends AphrontTagView { private $padding = array(); private $border = false; private $color; + private $collapsible; const BLUE = 'phui-box-blue'; const GREY = 'phui-box-grey'; @@ -30,6 +31,18 @@ final class PHUIBoxView extends AphrontTagView { return $this; } + /** + * Render PHUIBoxView as a
instead of a
HTML tag. + * To be used for collapse/expand in combination with PHUIHeaderView. + * + * @param bool $collapsible True to wrap in instead of
HTML + * tag. + */ + public function setCollapsible($collapsible) { + $this->collapsible = $collapsible; + return $this; + } + protected function getTagAttributes() { require_celerity_resource('phui-box-css'); $outer_classes = array(); @@ -51,10 +64,20 @@ final class PHUIBoxView extends AphrontTagView { $outer_classes[] = $this->color; } - return array('class' => $outer_classes); + $tag_classes = array('class' => $outer_classes); + + if ($this->collapsible) { + $attribute = array('open' => ''); // expand column by default + $tag_classes = array_merge($tag_classes, $attribute); + } + + return $tag_classes; } protected function getTagName() { + if ($this->collapsible) { + return 'details'; + } return 'div'; } diff --git a/src/view/phui/PHUICrumbView.php b/src/view/phui/PHUICrumbView.php index 1e5dad2ec6..a8c0a7e57a 100644 --- a/src/view/phui/PHUICrumbView.php +++ b/src/view/phui/PHUICrumbView.php @@ -23,7 +23,7 @@ final class PHUICrumbView extends AphrontView { * Make this crumb always visible, even on devices where it would normally * be hidden. * - * @param bool True to make the crumb always visible. + * @param bool $always_visible True to make the crumb always visible. * @return this */ public function setAlwaysVisible($always_visible) { diff --git a/src/view/phui/PHUICrumbsView.php b/src/view/phui/PHUICrumbsView.php index fb1a0ae9b4..bd202ed5a7 100644 --- a/src/view/phui/PHUICrumbsView.php +++ b/src/view/phui/PHUICrumbsView.php @@ -15,8 +15,8 @@ final class PHUICrumbsView extends AphrontView { * Convenience method for adding a simple crumb with just text, or text and * a link. * - * @param string Text of the crumb. - * @param string? Optional href for the crumb. + * @param string $text Text of the crumb. + * @param string? $href Optional href for the crumb. * @return this */ public function addTextCrumb($text, $href = null) { diff --git a/src/view/phui/PHUIHeaderView.php b/src/view/phui/PHUIHeaderView.php index 465768ae16..8b8774c4cf 100644 --- a/src/view/phui/PHUIHeaderView.php +++ b/src/view/phui/PHUIHeaderView.php @@ -24,6 +24,7 @@ final class PHUIHeaderView extends AphrontTagView { private $href; private $actionList; private $actionListID; + private $collapsible; public function setHeader($header) { $this->header = $header; @@ -90,6 +91,18 @@ final class PHUIHeaderView extends AphrontTagView { return $this; } + /** + * Render PHUIHeaderView as a instead of a
HTML tag. + * To be used for collapse/expand in combination with PHUIBoxView. + * + * @param bool $collapsible True to wrap in instead of
HTML + * tag. + */ + public function setCollapsible($collapsible) { + $this->collapsible = $collapsible; + return $this; + } + public function setPolicyObject(PhabricatorPolicyInterface $object) { $this->policyObject = $object; return $this; @@ -156,6 +169,9 @@ final class PHUIHeaderView extends AphrontTagView { } protected function getTagName() { + if ($this->collapsible) { + return 'summary'; + } return 'div'; } diff --git a/src/view/phui/PHUIPagerView.php b/src/view/phui/PHUIPagerView.php index 2bb3a8276e..a3821bb4a8 100644 --- a/src/view/phui/PHUIPagerView.php +++ b/src/view/phui/PHUIPagerView.php @@ -95,7 +95,7 @@ final class PHUIPagerView extends AphrontView { * $pager->getPageSize() + 1); * $results = $pager->sliceResults($results); * - * @param list Result array. + * @param list $results Result array. * @return list One page of results. */ public function sliceResults(array $results) { diff --git a/src/view/phui/PHUIWorkpanelView.php b/src/view/phui/PHUIWorkpanelView.php index 911d38c2e3..ad8c77fd07 100644 --- a/src/view/phui/PHUIWorkpanelView.php +++ b/src/view/phui/PHUIWorkpanelView.php @@ -81,7 +81,8 @@ final class PHUIWorkpanelView extends AphrontTagView { $header = id(new PHUIHeaderView()) ->setHeader($this->header) - ->setSubheader($this->subheader); + ->setSubheader($this->subheader) + ->setCollapsible(true); foreach ($this->headerActions as $action) { $header->addActionItem($action); @@ -112,6 +113,7 @@ final class PHUIWorkpanelView extends AphrontTagView { $view = id(new PHUIBoxView()) ->setColor(PHUIBoxView::GREY) ->addClass('phui-workpanel-view-inner') + ->setCollapsible(true) ->appendChild( array( $header, diff --git a/src/view/phui/calendar/PHUICalendarMonthView.php b/src/view/phui/calendar/PHUICalendarMonthView.php index 752a9afa15..be5e50f667 100644 --- a/src/view/phui/calendar/PHUICalendarMonthView.php +++ b/src/view/phui/calendar/PHUICalendarMonthView.php @@ -193,18 +193,6 @@ final class PHUICalendarMonthView extends AphrontView { return $box; } - private function getMaxDailyEventsForWeek($week_of_cell_lists) { - $max_count = 0; - - foreach ($week_of_cell_lists as $cell_list) { - if ($cell_list['count'] > $max_count) { - $max_count = $cell_list['count']; - } - } - - return $max_count; - } - private function getEventListCell($event_list) { $list = $event_list['list']; $class = $event_list['class']; diff --git a/src/view/viewutils.php b/src/view/viewutils.php index 956f6ca2c2..47a6f0178d 100644 --- a/src/view/viewutils.php +++ b/src/view/viewutils.php @@ -104,9 +104,9 @@ function phabricator_datetimezone($epoch, $user) { * @{function:phabricator_date}, @{function:phabricator_time}, or * @{function:phabricator_datetime}. * - * @param int Unix epoch timestamp. - * @param PhabricatorUser User viewing the timestamp. - * @param string Date format, as per DateTime class. + * @param int $epoch Unix epoch timestamp. + * @param PhabricatorUser $user User viewing the timestamp. + * @param string $format Date format, as per DateTime class. * @return string Formatted, local date/time. */ function phabricator_format_local_time($epoch, $user, $format) { diff --git a/src/view/widget/AphrontStackTraceView.php b/src/view/widget/AphrontStackTraceView.php index 15b39be878..263e6e0d74 100644 --- a/src/view/widget/AphrontStackTraceView.php +++ b/src/view/widget/AphrontStackTraceView.php @@ -27,13 +27,15 @@ final class AphrontStackTraceView extends AphrontView { foreach ($trace as $part) { $lib = null; $file = idx($part, 'file'); - $relative = $file; - foreach ($libraries as $library) { - $root = phutil_get_library_root($library); - if (Filesystem::isDescendant($file, $root)) { - $lib = $library; - $relative = Filesystem::readablePath($file, $root); - break; + if ($file !== null && $file !== '') { + $relative = $file; + foreach ($libraries as $library) { + $root = phutil_get_library_root($library); + if (Filesystem::isDescendant($file, $root)) { + $lib = $library; + $relative = Filesystem::readablePath($file, $root); + break; + } } } diff --git a/support/startup/PhabricatorClientLimit.php b/support/startup/PhabricatorClientLimit.php index c43e4b42c0..ea2f39d217 100644 --- a/support/startup/PhabricatorClientLimit.php +++ b/support/startup/PhabricatorClientLimit.php @@ -170,7 +170,7 @@ abstract class PhabricatorClientLimit { /** * Get the APC key for a given bucket. * - * @param int Bucket to get the key for. + * @param int $bucket_id Bucket to get the key for. * @return string APC key for the bucket. */ private function getBucketCacheKey($bucket_id) { @@ -182,9 +182,8 @@ abstract class PhabricatorClientLimit { /** * Add points to the rate limit score for some client. * - * @param string Some key which identifies the client making the request. - * @param float The cost for this request; more points pushes them toward - * the limit faster. + * @param float $score The cost for this request; more points pushes them + * toward the limit faster. * @return this */ private function addScore($score) { diff --git a/support/startup/PhabricatorStartup.php b/support/startup/PhabricatorStartup.php index 10c6295587..38c8af13b1 100644 --- a/support/startup/PhabricatorStartup.php +++ b/support/startup/PhabricatorStartup.php @@ -112,7 +112,7 @@ final class PhabricatorStartup { /** - * @param float Request start time, from `microtime(true)`. + * @param float $start_time Request start time, from `microtime(true)`. * @task hook */ public static function didStartup($start_time) { @@ -255,7 +255,7 @@ final class PhabricatorStartup { * The limit is implemented with a tick function, so enabling it implies * some accounting overhead. * - * @param int Time limit in seconds. + * @param int $limit Time limit in seconds. * @return void */ public static function setDebugTimeLimit($limit) { @@ -312,12 +312,12 @@ final class PhabricatorStartup { * Fatal the request completely in response to an exception, sending a plain * text message to the client. Calls @{method:didFatal} internally. * - * @param string Brief description of the exception context, like + * @param string $note Brief description of the exception context, like * `"Rendering Exception"`. - * @param Throwable The exception itself. - * @param bool True if it's okay to show the exception's stack trace - * to the user. The trace will always be logged. - * @return exit This method **does not return**. + * @param Throwable $ex The exception itself. + * @param bool $show_trace True if it's okay to show the exception's + * stack trace to the user. The trace will always be + * logged. * * @task apocalypse */ @@ -343,11 +343,11 @@ final class PhabricatorStartup { /** * Fatal the request completely, sending a plain text message to the client. * - * @param string Plain text message to send to the client. - * @param string Plain text message to send to the error log. If not - * provided, the client message is used. You can pass a more - * detailed message here (e.g., with stack traces) to avoid - * showing it to users. + * @param string $message Plain text message to send to the client. + * @param string? $log_message Plain text message to send to the error log. + * If not provided, the client message is used. You can pass + * a more detailed message here (e.g., with stack traces) to + * avoid showing it to users. * @return exit This method **does not return**. * * @task apocalypse @@ -514,7 +514,7 @@ final class PhabricatorStartup { * Adjustments here primarily impact the environment as seen by subprocesses. * The environment is forwarded explicitly by @{class:ExecFuture}. * - * @param map Input `$_ENV`. + * @param map $env Input `$_ENV`. * @return map Suitable `$_ENV`. * @task validation */ @@ -654,7 +654,7 @@ final class PhabricatorStartup { /** * Add a new client limits. * - * @param PhabricatorClientLimit New limit. + * @param PhabricatorClientLimit $limit New limit. * @return PhabricatorClientLimit The limit. */ public static function addRateLimit(PhabricatorClientLimit $limit) { @@ -699,7 +699,8 @@ final class PhabricatorStartup { /** * Tear down rate limiting and allow limits to score the request. * - * @param map Additional, freeform request state. + * @param map $request_state Additional, freeform request + * state. * @return void * @task ratelimit */ @@ -746,7 +747,7 @@ final class PhabricatorStartup { * time and record it with @{method:recordStartupPhase} after the class is * available. * - * @param string Phase name. + * @param string $phase Phase name. * @task phases */ public static function beginStartupPhase($phase) { @@ -762,8 +763,8 @@ final class PhabricatorStartup { * record a time before the class loads, then hand it over once the class * becomes available. * - * @param string Phase name. - * @param float Phase start time, from `microtime(true)`. + * @param string $phase Phase name. + * @param float $time Phase start time, from `microtime(true)`. * @task phases */ public static function recordStartupPhase($phase, $time) { diff --git a/support/startup/preamble-utils.php b/support/startup/preamble-utils.php index 8dd3b502d6..c04f06b900 100644 --- a/support/startup/preamble-utils.php +++ b/support/startup/preamble-utils.php @@ -4,7 +4,7 @@ * Parse the "X_FORWARDED_FOR" HTTP header to determine the original client * address. * - * @param int Number of devices to trust. + * @param int? $layers Number of devices to trust. * @return void */ function preamble_trust_x_forwarded_for_header($layers = 1) { diff --git a/webroot/rsrc/css/application/differential/changeset-view.css b/webroot/rsrc/css/application/differential/changeset-view.css index a127d14648..f41bd8a167 100644 --- a/webroot/rsrc/css/application/differential/changeset-view.css +++ b/webroot/rsrc/css/application/differential/changeset-view.css @@ -340,7 +340,7 @@ td.cov-I { .differential-file-icon-header .phui-icon-view { display: inline-block; - margin: 0 6px 2px 0; + margin: 0 0 2px 0; vertical-align: middle; font-size: 14px; } @@ -349,6 +349,15 @@ td.cov-I { display: none; } +.differential-changeset-path-copy-button { + margin-right: 0; + padding-right: 10px; +} + +.device-phone .differential-changeset-path-copy-button { + display: none; +} + .differential-changeset-buttons { float: right; margin-top: 16px; diff --git a/webroot/rsrc/css/phui/phui-badge.css b/webroot/rsrc/css/phui/phui-badge.css index 1538e5de4d..a40c25390a 100644 --- a/webroot/rsrc/css/phui/phui-badge.css +++ b/webroot/rsrc/css/phui/phui-badge.css @@ -75,7 +75,7 @@ /* Firefox Fix */ .phui-badge-card-front { transform: rotateY(0deg); - -webkit-trasform: rotateY(0deg); + -webkit-transform: rotateY(0deg); } .phui-badge-front-view, diff --git a/webroot/rsrc/css/phui/phui-header-view.css b/webroot/rsrc/css/phui/phui-header-view.css index 7131db5640..0e418059ec 100644 --- a/webroot/rsrc/css/phui/phui-header-view.css +++ b/webroot/rsrc/css/phui/phui-header-view.css @@ -56,6 +56,27 @@ body .phui-header-shell.phui-bleed-header border-top-width: 0; } +details > summary.phui-header-shell { + cursor: pointer; + list-style: none; +} + +details > summary.phui-header-shell::marker { + content: none; +} + +.device-phone details > summary.phui-header-shell::after, +.device-tablet details > summary.phui-header-shell::after { + font-family: FontAwesome; + content: "\f061"; +} + +.device-phone details[open] > summary.phui-header-shell::after, +.device-tablet details[open] > summary.phui-header-shell::after { + font-family: FontAwesome; + content: "\f063"; +} + .phui-property-list-view + .diviner-document-section { margin-top: -1px; } diff --git a/webroot/rsrc/externals/d3/LICENSE b/webroot/rsrc/externals/d3/LICENSE index 1d9d875edb..894ddc6549 100644 --- a/webroot/rsrc/externals/d3/LICENSE +++ b/webroot/rsrc/externals/d3/LICENSE @@ -1,4 +1,4 @@ -Copyright 2010-2017 Mike Bostock +Copyright 2010-2020 Mike Bostock All rights reserved. Redistribution and use in source and binary forms, with or without modification, diff --git a/webroot/rsrc/externals/d3/README.md b/webroot/rsrc/externals/d3/README.md index 696df69ce1..7405831905 100644 --- a/webroot/rsrc/externals/d3/README.md +++ b/webroot/rsrc/externals/d3/README.md @@ -6,10 +6,10 @@ ## Resources +* [Introduction](https://observablehq.com/@d3/learn-d3) * [API Reference](https://github.com/d3/d3/blob/master/API.md) -* [Release Notes](https://github.com/d3/d3/releases) -* [Gallery](https://github.com/d3/d3/wiki/Gallery) -* [Examples](https://bl.ocks.org/mbostock) +* [Releases](https://github.com/d3/d3/releases) +* [Examples](https://observablehq.com/@d3/gallery) * [Wiki](https://github.com/d3/d3/wiki) ## Installing @@ -17,19 +17,19 @@ If you use npm, `npm install d3`. Otherwise, download the [latest release](https://github.com/d3/d3/releases/latest). The released bundle supports anonymous AMD, CommonJS, and vanilla environments. You can load directly from [d3js.org](https://d3js.org), [CDNJS](https://cdnjs.com/libraries/d3), or [unpkg](https://unpkg.com/d3/). For example: ```html - + ``` For the minified version: ```html - + ``` You can also use the standalone D3 microlibraries. For example, [d3-selection](https://github.com/d3/d3-selection): ```html - + ``` D3 is written using [ES2015 modules](http://www.2ality.com/2014/09/es6-modules-final.html). Create a [custom bundle using Rollup](https://bl.ocks.org/mbostock/bb09af4c39c79cffcde4), Webpack, or your preferred bundler. To import D3 into an ES2015 application, either import specific symbols from specific D3 modules: @@ -47,11 +47,11 @@ import * as d3 from "d3"; In Node: ```js -var d3 = require("d3"); +const d3 = require("d3"); ``` You can also require individual modules and combine them into a `d3` object using [Object.assign](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign): ```js -var d3 = Object.assign({}, require("d3-format"), require("d3-geo"), require("d3-geo-projection")); +const d3 = Object.assign({}, require("d3-format"), require("d3-geo"), require("d3-geo-projection")); ``` diff --git a/webroot/rsrc/externals/d3/d3.min.js b/webroot/rsrc/externals/d3/d3.min.js index dfbfa98c1a..034ff8d5c7 100644 --- a/webroot/rsrc/externals/d3/d3.min.js +++ b/webroot/rsrc/externals/d3/d3.min.js @@ -2,5 +2,5 @@ * @provides d3 * @do-not-minify */ -// https://d3js.org v5.9.2 Copyright 2019 Mike Bostock -!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n(t.d3=t.d3||{})}(this,function(t){"use strict";function n(t,n){return tn?1:t>=n?0:NaN}function e(t){var e;return 1===t.length&&(e=t,t=function(t,r){return n(e(t),r)}),{left:function(n,e,r,i){for(null==r&&(r=0),null==i&&(i=n.length);r>>1;t(n[o],e)<0?r=o+1:i=o}return r},right:function(n,e,r,i){for(null==r&&(r=0),null==i&&(i=n.length);r>>1;t(n[o],e)>0?i=o:r=o+1}return r}}}var r=e(n),i=r.right,o=r.left;function a(t,n){return[t,n]}function u(t){return null===t?NaN:+t}function c(t,n){var e,r,i=t.length,o=0,a=-1,c=0,f=0;if(null==n)for(;++a1)return f/(o-1)}function f(t,n){var e=c(t,n);return e?Math.sqrt(e):e}function s(t,n){var e,r,i,o=t.length,a=-1;if(null==n){for(;++a=e)for(r=i=e;++ae&&(r=e),i=e)for(r=i=e;++ae&&(r=e),i0)return[t];if((r=n0)for(t=Math.ceil(t/a),n=Math.floor(n/a),o=new Array(i=Math.ceil(n-t+1));++u=0?(o>=y?10:o>=_?5:o>=b?2:1)*Math.pow(10,i):-Math.pow(10,-i)/(o>=y?10:o>=_?5:o>=b?2:1)}function w(t,n,e){var r=Math.abs(n-t)/Math.max(0,e),i=Math.pow(10,Math.floor(Math.log(r)/Math.LN10)),o=r/i;return o>=y?i*=10:o>=_?i*=5:o>=b&&(i*=2),n=1)return+e(t[r-1],r-1,t);var r,i=(r-1)*n,o=Math.floor(i),a=+e(t[o],o,t);return a+(+e(t[o+1],o+1,t)-a)*(i-o)}}function A(t,n){var e,r,i=t.length,o=-1;if(null==n){for(;++o=e)for(r=e;++or&&(r=e)}else for(;++o=e)for(r=e;++or&&(r=e);return r}function T(t){for(var n,e,r,i=t.length,o=-1,a=0;++o=0;)for(n=(r=t[i]).length;--n>=0;)e[--a]=r[n];return e}function S(t,n){var e,r,i=t.length,o=-1;if(null==n){for(;++o=e)for(r=e;++oe&&(r=e)}else for(;++o=e)for(r=e;++oe&&(r=e);return r}function k(t){if(!(i=t.length))return[];for(var n=-1,e=S(t,E),r=new Array(e);++n=0&&(n=t.slice(e+1),t=t.slice(0,e)),t&&!r.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:n}})),a=-1,u=o.length;if(!(arguments.length<2)){if(null!=n&&"function"!=typeof n)throw new Error("invalid callback: "+n);for(;++a0)for(var e,r,i=new Array(e),o=0;o=0&&"xmlns"!==(n=t.slice(0,e))&&(t=t.slice(e+1)),V.hasOwnProperty(n)?{space:V[n],local:t}:t}function W(t){var n=$(t);return(n.local?function(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}:function(t){return function(){var n=this.ownerDocument,e=this.namespaceURI;return e===G&&n.documentElement.namespaceURI===G?n.createElement(t):n.createElementNS(e,t)}})(n)}function Z(){}function Q(t){return null==t?Z:function(){return this.querySelector(t)}}function J(){return[]}function K(t){return null==t?J:function(){return this.querySelectorAll(t)}}function tt(t){return function(){return this.matches(t)}}function nt(t){return new Array(t.length)}function et(t,n){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=n}et.prototype={constructor:et,appendChild:function(t){return this._parent.insertBefore(t,this._next)},insertBefore:function(t,n){return this._parent.insertBefore(t,n)},querySelector:function(t){return this._parent.querySelector(t)},querySelectorAll:function(t){return this._parent.querySelectorAll(t)}};var rt="$";function it(t,n,e,r,i,o){for(var a,u=0,c=n.length,f=o.length;un?1:t>=n?0:NaN}function ut(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function ct(t,n){return t.style.getPropertyValue(n)||ut(t).getComputedStyle(t,null).getPropertyValue(n)}function ft(t){return t.trim().split(/^|\s+/)}function st(t){return t.classList||new lt(t)}function lt(t){this._node=t,this._names=ft(t.getAttribute("class")||"")}function ht(t,n){for(var e=st(t),r=-1,i=n.length;++r=0&&(this._names.splice(n,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var wt={};(t.event=null,"undefined"!=typeof document)&&("onmouseenter"in document.documentElement||(wt={mouseenter:"mouseover",mouseleave:"mouseout"}));function Mt(t,n,e){return t=Nt(t,n,e),function(n){var e=n.relatedTarget;e&&(e===this||8&e.compareDocumentPosition(this))||t.call(this,n)}}function Nt(n,e,r){return function(i){var o=t.event;t.event=i;try{n.call(this,this.__data__,e,r)}finally{t.event=o}}}function At(t){return function(){var n=this.__on;if(n){for(var e,r=0,i=-1,o=n.length;r=x&&(x=m+1);!(b=y[x])&&++x=0;)(r=i[o])&&(a&&4^r.compareDocumentPosition(a)&&a.parentNode.insertBefore(r,a),a=r);return this},sort:function(t){function n(n,e){return n&&e?t(n.__data__,e.__data__):!n-!e}t||(t=at);for(var e=this._groups,r=e.length,i=new Array(r),o=0;o1?this.each((null==n?function(t){return function(){this.style.removeProperty(t)}}:"function"==typeof n?function(t,n,e){return function(){var r=n.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,e)}}:function(t,n,e){return function(){this.style.setProperty(t,n,e)}})(t,n,null==e?"":e)):ct(this.node(),t)},property:function(t,n){return arguments.length>1?this.each((null==n?function(t){return function(){delete this[t]}}:"function"==typeof n?function(t,n){return function(){var e=n.apply(this,arguments);null==e?delete this[t]:this[t]=e}}:function(t,n){return function(){this[t]=n}})(t,n)):this.node()[t]},classed:function(t,n){var e=ft(t+"");if(arguments.length<2){for(var r=st(this.node()),i=-1,o=e.length;++i=0&&(n=t.slice(e+1),t=t.slice(0,e)),{type:t,name:n}})}(t+""),a=o.length;if(!(arguments.length<2)){for(u=n?Tt:At,null==e&&(e=!1),r=0;r>8&15|n>>4&240,n>>4&15|240&n,(15&n)<<4|15&n,1):(n=rn.exec(t))?dn(parseInt(n[1],16)):(n=on.exec(t))?new yn(n[1],n[2],n[3],1):(n=an.exec(t))?new yn(255*n[1]/100,255*n[2]/100,255*n[3]/100,1):(n=un.exec(t))?pn(n[1],n[2],n[3],n[4]):(n=cn.exec(t))?pn(255*n[1]/100,255*n[2]/100,255*n[3]/100,n[4]):(n=fn.exec(t))?bn(n[1],n[2]/100,n[3]/100,1):(n=sn.exec(t))?bn(n[1],n[2]/100,n[3]/100,n[4]):ln.hasOwnProperty(t)?dn(ln[t]):"transparent"===t?new yn(NaN,NaN,NaN,0):null}function dn(t){return new yn(t>>16&255,t>>8&255,255&t,1)}function pn(t,n,e,r){return r<=0&&(t=n=e=NaN),new yn(t,n,e,r)}function vn(t){return t instanceof Jt||(t=hn(t)),t?new yn((t=t.rgb()).r,t.g,t.b,t.opacity):new yn}function gn(t,n,e,r){return 1===arguments.length?vn(t):new yn(t,n,e,null==r?1:r)}function yn(t,n,e,r){this.r=+t,this.g=+n,this.b=+e,this.opacity=+r}function _n(t){return((t=Math.max(0,Math.min(255,Math.round(t)||0)))<16?"0":"")+t.toString(16)}function bn(t,n,e,r){return r<=0?t=n=e=NaN:e<=0||e>=1?t=n=NaN:n<=0&&(t=NaN),new xn(t,n,e,r)}function mn(t,n,e,r){return 1===arguments.length?function(t){if(t instanceof xn)return new xn(t.h,t.s,t.l,t.opacity);if(t instanceof Jt||(t=hn(t)),!t)return new xn;if(t instanceof xn)return t;var n=(t=t.rgb()).r/255,e=t.g/255,r=t.b/255,i=Math.min(n,e,r),o=Math.max(n,e,r),a=NaN,u=o-i,c=(o+i)/2;return u?(a=n===o?(e-r)/u+6*(e0&&c<1?0:a,new xn(a,u,c,t.opacity)}(t):new xn(t,n,e,null==r?1:r)}function xn(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function wn(t,n,e){return 255*(t<60?n+(e-n)*t/60:t<180?e:t<240?n+(e-n)*(240-t)/60:n)}Zt(Jt,hn,{displayable:function(){return this.rgb().displayable()},hex:function(){return this.rgb().hex()},toString:function(){return this.rgb()+""}}),Zt(yn,gn,Qt(Jt,{brighter:function(t){return t=null==t?1/.7:Math.pow(1/.7,t),new yn(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?.7:Math.pow(.7,t),new yn(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return 0<=this.r&&this.r<=255&&0<=this.g&&this.g<=255&&0<=this.b&&this.b<=255&&0<=this.opacity&&this.opacity<=1},hex:function(){return"#"+_n(this.r)+_n(this.g)+_n(this.b)},toString:function(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?")":", "+t+")")}})),Zt(xn,mn,Qt(Jt,{brighter:function(t){return t=null==t?1/.7:Math.pow(1/.7,t),new xn(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?.7:Math.pow(.7,t),new xn(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),n=isNaN(t)||isNaN(this.s)?0:this.s,e=this.l,r=e+(e<.5?e:1-e)*n,i=2*e-r;return new yn(wn(t>=240?t-240:t+120,i,r),wn(t,i,r),wn(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1}}));var Mn=Math.PI/180,Nn=180/Math.PI,An=.96422,Tn=1,Sn=.82521,kn=4/29,En=6/29,Cn=3*En*En,Pn=En*En*En;function zn(t){if(t instanceof Dn)return new Dn(t.l,t.a,t.b,t.opacity);if(t instanceof Fn){if(isNaN(t.h))return new Dn(t.l,0,0,t.opacity);var n=t.h*Mn;return new Dn(t.l,Math.cos(n)*t.c,Math.sin(n)*t.c,t.opacity)}t instanceof yn||(t=vn(t));var e,r,i=On(t.r),o=On(t.g),a=On(t.b),u=qn((.2225045*i+.7168786*o+.0606169*a)/Tn);return i===o&&o===a?e=r=u:(e=qn((.4360747*i+.3850649*o+.1430804*a)/An),r=qn((.0139322*i+.0971045*o+.7141733*a)/Sn)),new Dn(116*u-16,500*(e-u),200*(u-r),t.opacity)}function Rn(t,n,e,r){return 1===arguments.length?zn(t):new Dn(t,n,e,null==r?1:r)}function Dn(t,n,e,r){this.l=+t,this.a=+n,this.b=+e,this.opacity=+r}function qn(t){return t>Pn?Math.pow(t,1/3):t/Cn+kn}function Ln(t){return t>En?t*t*t:Cn*(t-kn)}function Un(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function On(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function Bn(t){if(t instanceof Fn)return new Fn(t.h,t.c,t.l,t.opacity);if(t instanceof Dn||(t=zn(t)),0===t.a&&0===t.b)return new Fn(NaN,0,t.l,t.opacity);var n=Math.atan2(t.b,t.a)*Nn;return new Fn(n<0?n+360:n,Math.sqrt(t.a*t.a+t.b*t.b),t.l,t.opacity)}function Yn(t,n,e,r){return 1===arguments.length?Bn(t):new Fn(t,n,e,null==r?1:r)}function Fn(t,n,e,r){this.h=+t,this.c=+n,this.l=+e,this.opacity=+r}Zt(Dn,Rn,Qt(Jt,{brighter:function(t){return new Dn(this.l+18*(null==t?1:t),this.a,this.b,this.opacity)},darker:function(t){return new Dn(this.l-18*(null==t?1:t),this.a,this.b,this.opacity)},rgb:function(){var t=(this.l+16)/116,n=isNaN(this.a)?t:t+this.a/500,e=isNaN(this.b)?t:t-this.b/200;return new yn(Un(3.1338561*(n=An*Ln(n))-1.6168667*(t=Tn*Ln(t))-.4906146*(e=Sn*Ln(e))),Un(-.9787684*n+1.9161415*t+.033454*e),Un(.0719453*n-.2289914*t+1.4052427*e),this.opacity)}})),Zt(Fn,Yn,Qt(Jt,{brighter:function(t){return new Fn(this.h,this.c,this.l+18*(null==t?1:t),this.opacity)},darker:function(t){return new Fn(this.h,this.c,this.l-18*(null==t?1:t),this.opacity)},rgb:function(){return zn(this).rgb()}}));var In=-.14861,jn=1.78277,Hn=-.29227,Xn=-.90649,Gn=1.97294,Vn=Gn*Xn,$n=Gn*jn,Wn=jn*Hn-Xn*In;function Zn(t,n,e,r){return 1===arguments.length?function(t){if(t instanceof Qn)return new Qn(t.h,t.s,t.l,t.opacity);t instanceof yn||(t=vn(t));var n=t.r/255,e=t.g/255,r=t.b/255,i=(Wn*r+Vn*n-$n*e)/(Wn+Vn-$n),o=r-i,a=(Gn*(e-i)-Hn*o)/Xn,u=Math.sqrt(a*a+o*o)/(Gn*i*(1-i)),c=u?Math.atan2(a,o)*Nn-120:NaN;return new Qn(c<0?c+360:c,u,i,t.opacity)}(t):new Qn(t,n,e,null==r?1:r)}function Qn(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function Jn(t,n,e,r,i){var o=t*t,a=o*t;return((1-3*t+3*o-a)*n+(4-6*o+3*a)*e+(1+3*t+3*o-3*a)*r+a*i)/6}function Kn(t){var n=t.length-1;return function(e){var r=e<=0?e=0:e>=1?(e=1,n-1):Math.floor(e*n),i=t[r],o=t[r+1],a=r>0?t[r-1]:2*i-o,u=r180||e<-180?e-360*Math.round(e/360):e):ne(isNaN(t)?n:t)}function ie(t){return 1==(t=+t)?oe:function(n,e){return e-n?function(t,n,e){return t=Math.pow(t,e),n=Math.pow(n,e)-t,e=1/e,function(r){return Math.pow(t+r*n,e)}}(n,e,t):ne(isNaN(n)?e:n)}}function oe(t,n){var e=n-t;return e?ee(t,e):ne(isNaN(t)?n:t)}Zt(Qn,Zn,Qt(Jt,{brighter:function(t){return t=null==t?1/.7:Math.pow(1/.7,t),new Qn(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?.7:Math.pow(.7,t),new Qn(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=isNaN(this.h)?0:(this.h+120)*Mn,n=+this.l,e=isNaN(this.s)?0:this.s*n*(1-n),r=Math.cos(t),i=Math.sin(t);return new yn(255*(n+e*(In*r+jn*i)),255*(n+e*(Hn*r+Xn*i)),255*(n+e*(Gn*r)),this.opacity)}}));var ae=function t(n){var e=ie(n);function r(t,n){var r=e((t=gn(t)).r,(n=gn(n)).r),i=e(t.g,n.g),o=e(t.b,n.b),a=oe(t.opacity,n.opacity);return function(n){return t.r=r(n),t.g=i(n),t.b=o(n),t.opacity=a(n),t+""}}return r.gamma=t,r}(1);function ue(t){return function(n){var e,r,i=n.length,o=new Array(i),a=new Array(i),u=new Array(i);for(e=0;eo&&(i=n.slice(o,i),u[a]?u[a]+=i:u[++a]=i),(e=e[0])===(r=r[0])?u[a]?u[a]+=r:u[++a]=r:(u[++a]=null,c.push({i:a,x:he(e,r)})),o=ve.lastIndex;return o180?n+=360:n-t>180&&(t+=360),o.push({i:e.push(i(e)+"rotate(",null,r)-2,x:he(t,n)})):n&&e.push(i(e)+"rotate("+n+r)}(o.rotate,a.rotate,u,c),function(t,n,e,o){t!==n?o.push({i:e.push(i(e)+"skewX(",null,r)-2,x:he(t,n)}):n&&e.push(i(e)+"skewX("+n+r)}(o.skewX,a.skewX,u,c),function(t,n,e,r,o,a){if(t!==e||n!==r){var u=o.push(i(o)+"scale(",null,",",null,")");a.push({i:u-4,x:he(t,e)},{i:u-2,x:he(n,r)})}else 1===e&&1===r||o.push(i(o)+"scale("+e+","+r+")")}(o.scaleX,o.scaleY,a.scaleX,a.scaleY,u,c),o=a=null,function(t){for(var n,e=-1,r=c.length;++e=0&&n._call.call(null,t),n=n._next;--Ge}function ar(){Qe=(Ze=Ke.now())+Je,Ge=Ve=0;try{or()}finally{Ge=0,function(){var t,n,e=He,r=1/0;for(;e;)e._call?(r>e._time&&(r=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:He=n);Xe=t,cr(r)}(),Qe=0}}function ur(){var t=Ke.now(),n=t-Ze;n>We&&(Je-=n,Ze=t)}function cr(t){Ge||(Ve&&(Ve=clearTimeout(Ve)),t-Qe>24?(t<1/0&&(Ve=setTimeout(ar,t-Ke.now()-Je)),$e&&($e=clearInterval($e))):($e||(Ze=Ke.now(),$e=setInterval(ur,We)),Ge=1,tr(ar)))}function fr(t,n,e){var r=new rr;return n=null==n?0:+n,r.restart(function(e){r.stop(),t(e+n)},n,e),r}rr.prototype=ir.prototype={constructor:rr,restart:function(t,n,e){if("function"!=typeof t)throw new TypeError("callback is not a function");e=(null==e?nr():+e)+(null==n?0:+n),this._next||Xe===this||(Xe?Xe._next=this:He=this,Xe=this),this._call=t,this._time=e,cr()},stop:function(){this._call&&(this._call=null,this._time=1/0,cr())}};var sr=I("start","end","cancel","interrupt"),lr=[],hr=0,dr=1,pr=2,vr=3,gr=4,yr=5,_r=6;function br(t,n,e,r,i,o){var a=t.__transition;if(a){if(e in a)return}else t.__transition={};!function(t,n,e){var r,i=t.__transition;function o(c){var f,s,l,h;if(e.state!==dr)return u();for(f in i)if((h=i[f]).name===e.name){if(h.state===vr)return fr(o);h.state===gr?(h.state=_r,h.timer.stop(),h.on.call("interrupt",t,t.__data__,h.index,h.group),delete i[f]):+fhr)throw new Error("too late; already scheduled");return e}function xr(t,n){var e=wr(t,n);if(e.state>vr)throw new Error("too late; already running");return e}function wr(t,n){var e=t.__transition;if(!e||!(e=e[n]))throw new Error("transition not found");return e}function Mr(t,n){var e,r,i,o=t.__transition,a=!0;if(o){for(i in n=null==n?null:n+"",o)(e=o[i]).name===n?(r=e.state>pr&&e.state=0&&(t=t.slice(0,n)),!t||"start"===t})}(n)?mr:xr;return function(){var a=o(this,t),u=a.on;u!==r&&(i=(r=u).copy()).on(n,e),a.on=i}}(e,t,n))},attr:function(t,n){var e=$(t),r="transform"===e?ke:Ar;return this.attrTween(t,"function"==typeof n?(e.local?function(t,n,e){var r,i,o;return function(){var a,u,c=e(this);if(null!=c)return(a=this.getAttributeNS(t.space,t.local))===(u=c+"")?null:a===r&&u===i?o:(i=u,o=n(r=a,c));this.removeAttributeNS(t.space,t.local)}}:function(t,n,e){var r,i,o;return function(){var a,u,c=e(this);if(null!=c)return(a=this.getAttribute(t))===(u=c+"")?null:a===r&&u===i?o:(i=u,o=n(r=a,c));this.removeAttribute(t)}})(e,r,Nr(this,"attr."+t,n)):null==n?(e.local?function(t){return function(){this.removeAttributeNS(t.space,t.local)}}:function(t){return function(){this.removeAttribute(t)}})(e):(e.local?function(t,n,e){var r,i,o=e+"";return function(){var a=this.getAttributeNS(t.space,t.local);return a===o?null:a===r?i:i=n(r=a,e)}}:function(t,n,e){var r,i,o=e+"";return function(){var a=this.getAttribute(t);return a===o?null:a===r?i:i=n(r=a,e)}})(e,r,n))},attrTween:function(t,n){var e="attr."+t;if(arguments.length<2)return(e=this.tween(e))&&e._value;if(null==n)return this.tween(e,null);if("function"!=typeof n)throw new Error;var r=$(t);return this.tween(e,(r.local?function(t,n){var e,r;function i(){var i=n.apply(this,arguments);return i!==r&&(e=(r=i)&&function(t,n){return function(e){this.setAttributeNS(t.space,t.local,n(e))}}(t,i)),e}return i._value=n,i}:function(t,n){var e,r;function i(){var i=n.apply(this,arguments);return i!==r&&(e=(r=i)&&function(t,n){return function(e){this.setAttribute(t,n(e))}}(t,i)),e}return i._value=n,i})(r,n))},style:function(t,n,e){var r="transform"==(t+="")?Se:Ar;return null==n?this.styleTween(t,function(t,n){var e,r,i;return function(){var o=ct(this,t),a=(this.style.removeProperty(t),ct(this,t));return o===a?null:o===e&&a===r?i:i=n(e=o,r=a)}}(t,r)).on("end.style."+t,Sr(t)):"function"==typeof n?this.styleTween(t,function(t,n,e){var r,i,o;return function(){var a=ct(this,t),u=e(this),c=u+"";return null==u&&(this.style.removeProperty(t),c=u=ct(this,t)),a===c?null:a===r&&c===i?o:(i=c,o=n(r=a,u))}}(t,r,Nr(this,"style."+t,n))).each(function(t,n){var e,r,i,o,a="style."+n,u="end."+a;return function(){var c=xr(this,t),f=c.on,s=null==c.value[a]?o||(o=Sr(n)):void 0;f===e&&i===s||(r=(e=f).copy()).on(u,i=s),c.on=r}}(this._id,t)):this.styleTween(t,function(t,n,e){var r,i,o=e+"";return function(){var a=ct(this,t);return a===o?null:a===r?i:i=n(r=a,e)}}(t,r,n),e).on("end.style."+t,null)},styleTween:function(t,n,e){var r="style."+(t+="");if(arguments.length<2)return(r=this.tween(r))&&r._value;if(null==n)return this.tween(r,null);if("function"!=typeof n)throw new Error;return this.tween(r,function(t,n,e){var r,i;function o(){var o=n.apply(this,arguments);return o!==i&&(r=(i=o)&&function(t,n,e){return function(r){this.style.setProperty(t,n(r),e)}}(t,o,e)),r}return o._value=n,o}(t,n,null==e?"":e))},text:function(t){return this.tween("text","function"==typeof t?function(t){return function(){var n=t(this);this.textContent=null==n?"":n}}(Nr(this,"text",t)):function(t){return function(){this.textContent=t}}(null==t?"":t+""))},remove:function(){return this.on("end.remove",(t=this._id,function(){var n=this.parentNode;for(var e in this.__transition)if(+e!==t)return;n&&n.removeChild(this)}));var t},tween:function(t,n){var e=this._id;if(t+="",arguments.length<2){for(var r,i=wr(this.node(),e).tween,o=0,a=i.length;o0&&(r=o-p),M<0?h=d-v:M>0&&(a=u-v),x=vi,L.attr("cursor",xi.selection),B());break;default:return}di()},!0).on("keyup.brush",function(){switch(t.event.keyCode){case 16:P&&(y=_=P=!1,B());break;case 18:x===yi&&(w<0?s=l:w>0&&(r=o),M<0?h=d:M>0&&(a=u),x=gi,B());break;case 32:x===vi&&(t.event.altKey?(w&&(s=l-p*w,r=o+p*w),M&&(h=d-v*M,a=u+v*M),x=yi):(w<0?s=l:w>0&&(r=o),M<0?h=d:M>0&&(a=u),x=gi),L.attr("cursor",xi[m]),B());break;default:return}di()},!0).on("mousemove.brush",O,!0).on("mouseup.brush",Y,!0);It(t.event.view)}hi(),Mr(b),c.call(b),D.start()}function O(){var t=Ot(b);!P||y||_||(Math.abs(t[0]-R[0])>Math.abs(t[1]-R[1])?_=!0:y=!0),R=t,g=!0,di(),B()}function B(){var t;switch(p=R[0]-z[0],v=R[1]-z[1],x){case vi:case pi:w&&(p=Math.max(S-r,Math.min(E-s,p)),o=r+p,l=s+p),M&&(v=Math.max(k-a,Math.min(C-h,v)),u=a+v,d=h+v);break;case gi:w<0?(p=Math.max(S-r,Math.min(E-r,p)),o=r+p,l=s):w>0&&(p=Math.max(S-s,Math.min(E-s,p)),o=r,l=s+p),M<0?(v=Math.max(k-a,Math.min(C-a,v)),u=a+v,d=h):M>0&&(v=Math.max(k-h,Math.min(C-h,v)),u=a,d=h+v);break;case yi:w&&(o=Math.max(S,Math.min(E,r-p*w)),l=Math.max(S,Math.min(E,s+p*w))),M&&(u=Math.max(k,Math.min(C,a-v*M)),d=Math.max(k,Math.min(C,h+v*M)))}l1e-6)if(Math.abs(s*u-c*f)>1e-6&&i){var h=e-o,d=r-a,p=u*u+c*c,v=h*h+d*d,g=Math.sqrt(p),y=Math.sqrt(l),_=i*Math.tan((Yi-Math.acos((p+l-v)/(2*g*y)))/2),b=_/y,m=_/g;Math.abs(b-1)>1e-6&&(this._+="L"+(t+b*f)+","+(n+b*s)),this._+="A"+i+","+i+",0,0,"+ +(s*h>f*d)+","+(this._x1=t+m*u)+","+(this._y1=n+m*c)}else this._+="L"+(this._x1=t)+","+(this._y1=n);else;},arc:function(t,n,e,r,i,o){t=+t,n=+n;var a=(e=+e)*Math.cos(r),u=e*Math.sin(r),c=t+a,f=n+u,s=1^o,l=o?r-i:i-r;if(e<0)throw new Error("negative radius: "+e);null===this._x1?this._+="M"+c+","+f:(Math.abs(this._x1-c)>1e-6||Math.abs(this._y1-f)>1e-6)&&(this._+="L"+c+","+f),e&&(l<0&&(l=l%Fi+Fi),l>Ii?this._+="A"+e+","+e+",0,1,"+s+","+(t-a)+","+(n-u)+"A"+e+","+e+",0,1,"+s+","+(this._x1=c)+","+(this._y1=f):l>1e-6&&(this._+="A"+e+","+e+",0,"+ +(l>=Yi)+","+s+","+(this._x1=t+e*Math.cos(i))+","+(this._y1=n+e*Math.sin(i))))},rect:function(t,n,e,r){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+n)+"h"+ +e+"v"+ +r+"h"+-e+"Z"},toString:function(){return this._}};function Zi(){}function Qi(t,n){var e=new Zi;if(t instanceof Zi)t.each(function(t,n){e.set(n,t)});else if(Array.isArray(t)){var r,i=-1,o=t.length;if(null==n)for(;++ir!=d>r&&e<(h-f)*(r-s)/(d-s)+f&&(i=-i)}return i}function so(t,n,e){var r,i,o,a;return function(t,n,e){return(n[0]-t[0])*(e[1]-t[1])==(e[0]-t[0])*(n[1]-t[1])}(t,n,e)&&(i=t[r=+(t[0]===n[0])],o=e[r],a=n[r],i<=o&&o<=a||a<=o&&o<=i)}function lo(){}var ho=[[],[[[1,1.5],[.5,1]]],[[[1.5,1],[1,1.5]]],[[[1.5,1],[.5,1]]],[[[1,.5],[1.5,1]]],[[[1,1.5],[.5,1]],[[1,.5],[1.5,1]]],[[[1,.5],[1,1.5]]],[[[1,.5],[.5,1]]],[[[.5,1],[1,.5]]],[[[1,1.5],[1,.5]]],[[[.5,1],[1,.5]],[[1.5,1],[1,1.5]]],[[[1.5,1],[1,.5]]],[[[.5,1],[1.5,1]]],[[[1,1.5],[1.5,1]]],[[[.5,1],[1,1.5]]],[]];function po(){var t=1,n=1,e=M,r=u;function i(t){var n=e(t);if(Array.isArray(n))n=n.slice().sort(ao);else{var r=s(t),i=r[0],a=r[1];n=w(i,a,n),n=g(Math.floor(i/n)*n,Math.floor(a/n)*n,n)}return n.map(function(n){return o(t,n)})}function o(e,i){var o=[],u=[];return function(e,r,i){var o,u,c,f,s,l,h=new Array,d=new Array;o=u=-1,f=e[0]>=r,ho[f<<1].forEach(p);for(;++o=r,ho[c|f<<1].forEach(p);ho[f<<0].forEach(p);for(;++u=r,s=e[u*t]>=r,ho[f<<1|s<<2].forEach(p);++o=r,l=s,s=e[u*t+o+1]>=r,ho[c|f<<1|s<<2|l<<3].forEach(p);ho[f|s<<3].forEach(p)}o=-1,s=e[u*t]>=r,ho[s<<2].forEach(p);for(;++o=r,ho[s<<2|l<<3].forEach(p);function p(t){var n,e,r=[t[0][0]+o,t[0][1]+u],c=[t[1][0]+o,t[1][1]+u],f=a(r),s=a(c);(n=d[f])?(e=h[s])?(delete d[n.end],delete h[e.start],n===e?(n.ring.push(c),i(n.ring)):h[n.start]=d[e.end]={start:n.start,end:e.end,ring:n.ring.concat(e.ring)}):(delete d[n.end],n.ring.push(c),d[n.end=s]=n):(n=h[s])?(e=d[f])?(delete h[n.start],delete d[e.end],n===e?(n.ring.push(c),i(n.ring)):h[e.start]=d[n.end]={start:e.start,end:n.end,ring:e.ring.concat(n.ring)}):(delete h[n.start],n.ring.unshift(r),h[n.start=f]=n):h[f]=d[s]={start:f,end:s,ring:[r,c]}}ho[s<<3].forEach(p)}(e,i,function(t){r(t,e,i),function(t){for(var n=0,e=t.length,r=t[e-1][1]*t[0][0]-t[e-1][0]*t[0][1];++n0?o.push([t]):u.push(t)}),u.forEach(function(t){for(var n,e=0,r=o.length;e0&&a0&&u0&&o>0))throw new Error("invalid size");return t=r,n=o,i},i.thresholds=function(t){return arguments.length?(e="function"==typeof t?t:Array.isArray(t)?uo(oo.call(t)):uo(t),i):e},i.smooth=function(t){return arguments.length?(r=t?u:lo,i):r===u},i}function vo(t,n,e){for(var r=t.width,i=t.height,o=1+(e<<1),a=0;a=e&&(u>=o&&(c-=t.data[u-o+a*r]),n.data[u-e+a*r]=c/Math.min(u+1,r-1+o-u,o))}function go(t,n,e){for(var r=t.width,i=t.height,o=1+(e<<1),a=0;a=e&&(u>=o&&(c-=t.data[a+(u-o)*r]),n.data[a+(u-e)*r]=c/Math.min(u+1,i-1+o-u,o))}function yo(t){return t[0]}function _o(t){return t[1]}function bo(){return 1}var mo={},xo={},wo=34,Mo=10,No=13;function Ao(t){return new Function("d","return {"+t.map(function(t,n){return JSON.stringify(t)+": d["+n+"]"}).join(",")+"}")}function To(t){var n=Object.create(null),e=[];return t.forEach(function(t){for(var r in t)r in n||e.push(n[r]=r)}),e}function So(t,n){var e=t+"",r=e.length;return r9999?"+"+So(n,6):So(n,4))+"-"+So(t.getUTCMonth()+1,2)+"-"+So(t.getUTCDate(),2)+(o?"T"+So(e,2)+":"+So(r,2)+":"+So(i,2)+"."+So(o,3)+"Z":i?"T"+So(e,2)+":"+So(r,2)+":"+So(i,2)+"Z":r||e?"T"+So(e,2)+":"+So(r,2)+"Z":"")}function Eo(t){var n=new RegExp('["'+t+"\n\r]"),e=t.charCodeAt(0);function r(t,n){var r,i=[],o=t.length,a=0,u=0,c=o<=0,f=!1;function s(){if(c)return xo;if(f)return f=!1,mo;var n,r,i=a;if(t.charCodeAt(i)===wo){for(;a++=o?c=!0:(r=t.charCodeAt(a++))===Mo?f=!0:r===No&&(f=!0,t.charCodeAt(a)===Mo&&++a),t.slice(i+1,n-1).replace(/""/g,'"')}for(;a=(o=(v+y)/2))?v=o:y=o,(s=e>=(a=(g+_)/2))?g=a:_=a,i=d,!(d=d[l=s<<1|f]))return i[l]=p,t;if(u=+t._x.call(null,d.data),c=+t._y.call(null,d.data),n===u&&e===c)return p.next=d,i?i[l]=p:t._root=p,t;do{i=i?i[l]=new Array(4):t._root=new Array(4),(f=n>=(o=(v+y)/2))?v=o:y=o,(s=e>=(a=(g+_)/2))?g=a:_=a}while((l=s<<1|f)==(h=(c>=a)<<1|u>=o));return i[h]=d,i[l]=p,t}function ra(t,n,e,r,i){this.node=t,this.x0=n,this.y0=e,this.x1=r,this.y1=i}function ia(t){return t[0]}function oa(t){return t[1]}function aa(t,n,e){var r=new ua(null==n?ia:n,null==e?oa:e,NaN,NaN,NaN,NaN);return null==t?r:r.addAll(t)}function ua(t,n,e,r,i,o){this._x=t,this._y=n,this._x0=e,this._y0=r,this._x1=i,this._y1=o,this._root=void 0}function ca(t){for(var n={data:t.data},e=n;t=t.next;)e=e.next={data:t.data};return n}var fa=aa.prototype=ua.prototype;function sa(t){return t.x+t.vx}function la(t){return t.y+t.vy}function ha(t){return t.index}function da(t,n){var e=t.get(n);if(!e)throw new Error("missing: "+n);return e}function pa(t){return t.x}function va(t){return t.y}fa.copy=function(){var t,n,e=new ua(this._x,this._y,this._x0,this._y0,this._x1,this._y1),r=this._root;if(!r)return e;if(!r.length)return e._root=ca(r),e;for(t=[{source:r,target:e._root=new Array(4)}];r=t.pop();)for(var i=0;i<4;++i)(n=r.source[i])&&(n.length?t.push({source:n,target:r.target[i]=new Array(4)}):r.target[i]=ca(n));return e},fa.add=function(t){var n=+this._x.call(null,t),e=+this._y.call(null,t);return ea(this.cover(n,e),n,e,t)},fa.addAll=function(t){var n,e,r,i,o=t.length,a=new Array(o),u=new Array(o),c=1/0,f=1/0,s=-1/0,l=-1/0;for(e=0;es&&(s=r),il&&(l=i));if(c>s||f>l)return this;for(this.cover(c,f).cover(s,l),e=0;et||t>=i||r>n||n>=o;)switch(u=(nh||(o=c.y0)>d||(a=c.x1)=y)<<1|t>=g)&&(c=p[p.length-1],p[p.length-1]=p[p.length-1-f],p[p.length-1-f]=c)}else{var _=t-+this._x.call(null,v.data),b=n-+this._y.call(null,v.data),m=_*_+b*b;if(m=(u=(p+g)/2))?p=u:g=u,(s=a>=(c=(v+y)/2))?v=c:y=c,n=d,!(d=d[l=s<<1|f]))return this;if(!d.length)break;(n[l+1&3]||n[l+2&3]||n[l+3&3])&&(e=n,h=l)}for(;d.data!==t;)if(r=d,!(d=d.next))return this;return(i=d.next)&&delete d.next,r?(i?r.next=i:delete r.next,this):n?(i?n[l]=i:delete n[l],(d=n[0]||n[1]||n[2]||n[3])&&d===(n[3]||n[2]||n[1]||n[0])&&!d.length&&(e?e[h]=d:this._root=d),this):(this._root=i,this)},fa.removeAll=function(t){for(var n=0,e=t.length;n1?r[0]+r.slice(2):r,+t.slice(e+1)]}function ba(t){return(t=_a(Math.abs(t)))?t[1]:NaN}var ma,xa=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function wa(t){return new Ma(t)}function Ma(t){if(!(n=xa.exec(t)))throw new Error("invalid format: "+t);var n;this.fill=n[1]||" ",this.align=n[2]||">",this.sign=n[3]||"-",this.symbol=n[4]||"",this.zero=!!n[5],this.width=n[6]&&+n[6],this.comma=!!n[7],this.precision=n[8]&&+n[8].slice(1),this.trim=!!n[9],this.type=n[10]||""}function Na(t,n){var e=_a(t,n);if(!e)return t+"";var r=e[0],i=e[1];return i<0?"0."+new Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")}wa.prototype=Ma.prototype,Ma.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(null==this.width?"":Math.max(1,0|this.width))+(this.comma?",":"")+(null==this.precision?"":"."+Math.max(0,0|this.precision))+(this.trim?"~":"")+this.type};var Aa={"%":function(t,n){return(100*t).toFixed(n)},b:function(t){return Math.round(t).toString(2)},c:function(t){return t+""},d:function(t){return Math.round(t).toString(10)},e:function(t,n){return t.toExponential(n)},f:function(t,n){return t.toFixed(n)},g:function(t,n){return t.toPrecision(n)},o:function(t){return Math.round(t).toString(8)},p:function(t,n){return Na(100*t,n)},r:Na,s:function(t,n){var e=_a(t,n);if(!e)return t+"";var r=e[0],i=e[1],o=i-(ma=3*Math.max(-8,Math.min(8,Math.floor(i/3))))+1,a=r.length;return o===a?r:o>a?r+new Array(o-a+1).join("0"):o>0?r.slice(0,o)+"."+r.slice(o):"0."+new Array(1-o).join("0")+_a(t,Math.max(0,n+o-1))[0]},X:function(t){return Math.round(t).toString(16).toUpperCase()},x:function(t){return Math.round(t).toString(16)}};function Ta(t){return t}var Sa,ka=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];function Ea(t){var n,e,r=t.grouping&&t.thousands?(n=t.grouping,e=t.thousands,function(t,r){for(var i=t.length,o=[],a=0,u=n[0],c=0;i>0&&u>0&&(c+u+1>r&&(u=Math.max(1,r-c)),o.push(t.substring(i-=u,i+u)),!((c+=u+1)>r));)u=n[a=(a+1)%n.length];return o.reverse().join(e)}):Ta,i=t.currency,o=t.decimal,a=t.numerals?function(t){return function(n){return n.replace(/[0-9]/g,function(n){return t[+n]})}}(t.numerals):Ta,u=t.percent||"%";function c(t){var n=(t=wa(t)).fill,e=t.align,c=t.sign,f=t.symbol,s=t.zero,l=t.width,h=t.comma,d=t.precision,p=t.trim,v=t.type;"n"===v?(h=!0,v="g"):Aa[v]||(null==d&&(d=12),p=!0,v="g"),(s||"0"===n&&"="===e)&&(s=!0,n="0",e="=");var g="$"===f?i[0]:"#"===f&&/[boxX]/.test(v)?"0"+v.toLowerCase():"",y="$"===f?i[1]:/[%p]/.test(v)?u:"",_=Aa[v],b=/[defgprs%]/.test(v);function m(t){var i,u,f,m=g,x=y;if("c"===v)x=_(t)+x,t="";else{var w=(t=+t)<0;if(t=_(Math.abs(t),d),p&&(t=function(t){t:for(var n,e=t.length,r=1,i=-1;r0){if(!+t[r])break t;i=0}}return i>0?t.slice(0,i)+t.slice(n+1):t}(t)),w&&0==+t&&(w=!1),m=(w?"("===c?c:"-":"-"===c||"("===c?"":c)+m,x=("s"===v?ka[8+ma/3]:"")+x+(w&&"("===c?")":""),b)for(i=-1,u=t.length;++i(f=t.charCodeAt(i))||f>57){x=(46===f?o+t.slice(i+1):t.slice(i))+x,t=t.slice(0,i);break}}h&&!s&&(t=r(t,1/0));var M=m.length+t.length+x.length,N=M>1)+m+t+x+N.slice(M);break;default:t=N+m+t+x}return a(t)}return d=null==d?6:/[gprs]/.test(v)?Math.max(1,Math.min(21,d)):Math.max(0,Math.min(20,d)),m.toString=function(){return t+""},m}return{format:c,formatPrefix:function(t,n){var e=c(((t=wa(t)).type="f",t)),r=3*Math.max(-8,Math.min(8,Math.floor(ba(n)/3))),i=Math.pow(10,-r),o=ka[8+r/3];return function(t){return e(i*t)+o}}}}function Ca(n){return Sa=Ea(n),t.format=Sa.format,t.formatPrefix=Sa.formatPrefix,Sa}function Pa(t){return Math.max(0,-ba(Math.abs(t)))}function za(t,n){return Math.max(0,3*Math.max(-8,Math.min(8,Math.floor(ba(n)/3)))-ba(Math.abs(t)))}function Ra(t,n){return t=Math.abs(t),n=Math.abs(n)-t,Math.max(0,ba(n)-ba(t))+1}function Da(){return new qa}function qa(){this.reset()}Ca({decimal:".",thousands:",",grouping:[3],currency:["$",""]}),qa.prototype={constructor:qa,reset:function(){this.s=this.t=0},add:function(t){Ua(La,t,this.t),Ua(this,La.s,this.s),this.s?this.t+=La.t:this.s=La.t},valueOf:function(){return this.s}};var La=new qa;function Ua(t,n,e){var r=t.s=n+e,i=r-n,o=r-i;t.t=n-o+(e-i)}var Oa=1e-6,Ba=1e-12,Ya=Math.PI,Fa=Ya/2,Ia=Ya/4,ja=2*Ya,Ha=180/Ya,Xa=Ya/180,Ga=Math.abs,Va=Math.atan,$a=Math.atan2,Wa=Math.cos,Za=Math.ceil,Qa=Math.exp,Ja=Math.log,Ka=Math.pow,tu=Math.sin,nu=Math.sign||function(t){return t>0?1:t<0?-1:0},eu=Math.sqrt,ru=Math.tan;function iu(t){return t>1?0:t<-1?Ya:Math.acos(t)}function ou(t){return t>1?Fa:t<-1?-Fa:Math.asin(t)}function au(t){return(t=tu(t/2))*t}function uu(){}function cu(t,n){t&&su.hasOwnProperty(t.type)&&su[t.type](t,n)}var fu={Feature:function(t,n){cu(t.geometry,n)},FeatureCollection:function(t,n){for(var e=t.features,r=-1,i=e.length;++r=0?1:-1,i=r*e,o=Wa(n=(n*=Xa)/2+Ia),a=tu(n),u=_u*a,c=yu*o+u*Wa(i),f=u*r*tu(i);bu.add($a(f,c)),gu=t,yu=o,_u=a}function Tu(t){return[$a(t[1],t[0]),ou(t[2])]}function Su(t){var n=t[0],e=t[1],r=Wa(e);return[r*Wa(n),r*tu(n),tu(e)]}function ku(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]}function Eu(t,n){return[t[1]*n[2]-t[2]*n[1],t[2]*n[0]-t[0]*n[2],t[0]*n[1]-t[1]*n[0]]}function Cu(t,n){t[0]+=n[0],t[1]+=n[1],t[2]+=n[2]}function Pu(t,n){return[t[0]*n,t[1]*n,t[2]*n]}function zu(t){var n=eu(t[0]*t[0]+t[1]*t[1]+t[2]*t[2]);t[0]/=n,t[1]/=n,t[2]/=n}var Ru,Du,qu,Lu,Uu,Ou,Bu,Yu,Fu,Iu,ju,Hu,Xu,Gu,Vu,$u,Wu,Zu,Qu,Ju,Ku,tc,nc,ec,rc,ic,oc=Da(),ac={point:uc,lineStart:fc,lineEnd:sc,polygonStart:function(){ac.point=lc,ac.lineStart=hc,ac.lineEnd=dc,oc.reset(),xu.polygonStart()},polygonEnd:function(){xu.polygonEnd(),ac.point=uc,ac.lineStart=fc,ac.lineEnd=sc,bu<0?(Ru=-(qu=180),Du=-(Lu=90)):oc>Oa?Lu=90:oc<-Oa&&(Du=-90),Iu[0]=Ru,Iu[1]=qu}};function uc(t,n){Fu.push(Iu=[Ru=t,qu=t]),nLu&&(Lu=n)}function cc(t,n){var e=Su([t*Xa,n*Xa]);if(Yu){var r=Eu(Yu,e),i=Eu([r[1],-r[0],0],r);zu(i),i=Tu(i);var o,a=t-Uu,u=a>0?1:-1,c=i[0]*Ha*u,f=Ga(a)>180;f^(u*UuLu&&(Lu=o):f^(u*Uu<(c=(c+360)%360-180)&&cLu&&(Lu=n)),f?tpc(Ru,qu)&&(qu=t):pc(t,qu)>pc(Ru,qu)&&(Ru=t):qu>=Ru?(tqu&&(qu=t)):t>Uu?pc(Ru,t)>pc(Ru,qu)&&(qu=t):pc(t,qu)>pc(Ru,qu)&&(Ru=t)}else Fu.push(Iu=[Ru=t,qu=t]);nLu&&(Lu=n),Yu=e,Uu=t}function fc(){ac.point=cc}function sc(){Iu[0]=Ru,Iu[1]=qu,ac.point=uc,Yu=null}function lc(t,n){if(Yu){var e=t-Uu;oc.add(Ga(e)>180?e+(e>0?360:-360):e)}else Ou=t,Bu=n;xu.point(t,n),cc(t,n)}function hc(){xu.lineStart()}function dc(){lc(Ou,Bu),xu.lineEnd(),Ga(oc)>Oa&&(Ru=-(qu=180)),Iu[0]=Ru,Iu[1]=qu,Yu=null}function pc(t,n){return(n-=t)<0?n+360:n}function vc(t,n){return t[0]-n[0]}function gc(t,n){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nYa?t+Math.round(-t/ja)*ja:t,n]}function Pc(t,n,e){return(t%=ja)?n||e?Ec(Rc(t),Dc(n,e)):Rc(t):n||e?Dc(n,e):Cc}function zc(t){return function(n,e){return[(n+=t)>Ya?n-ja:n<-Ya?n+ja:n,e]}}function Rc(t){var n=zc(t);return n.invert=zc(-t),n}function Dc(t,n){var e=Wa(t),r=tu(t),i=Wa(n),o=tu(n);function a(t,n){var a=Wa(n),u=Wa(t)*a,c=tu(t)*a,f=tu(n),s=f*e+u*r;return[$a(c*i-s*o,u*e-f*r),ou(s*i+c*o)]}return a.invert=function(t,n){var a=Wa(n),u=Wa(t)*a,c=tu(t)*a,f=tu(n),s=f*i-c*o;return[$a(c*i+f*o,u*e+s*r),ou(s*e-u*r)]},a}function qc(t){function n(n){return(n=t(n[0]*Xa,n[1]*Xa))[0]*=Ha,n[1]*=Ha,n}return t=Pc(t[0]*Xa,t[1]*Xa,t.length>2?t[2]*Xa:0),n.invert=function(n){return(n=t.invert(n[0]*Xa,n[1]*Xa))[0]*=Ha,n[1]*=Ha,n},n}function Lc(t,n,e,r,i,o){if(e){var a=Wa(n),u=tu(n),c=r*e;null==i?(i=n+r*ja,o=n-c/2):(i=Uc(a,i),o=Uc(a,o),(r>0?io)&&(i+=r*ja));for(var f,s=i;r>0?s>o:s1&&n.push(n.pop().concat(n.shift()))},result:function(){var e=n;return n=[],t=null,e}}}function Bc(t,n){return Ga(t[0]-n[0])=0;--o)i.point((s=f[o])[0],s[1]);else r(h.x,h.p.x,-1,i);h=h.p}f=(h=h.o).z,d=!d}while(!h.v);i.lineEnd()}}}function Ic(t){if(n=t.length){for(var n,e,r=0,i=t[0];++r=0?1:-1,A=N*M,T=A>Ya,S=v*x;if(jc.add($a(S*N*tu(A),g*w+S*Wa(A))),a+=T?M+N*ja:M,T^d>=e^b>=e){var k=Eu(Su(h),Su(_));zu(k);var E=Eu(o,k);zu(E);var C=(T^M>=0?-1:1)*ou(E[2]);(r>C||r===C&&(k[0]||k[1]))&&(u+=T^M>=0?1:-1)}}return(a<-Oa||a0){for(l||(i.polygonStart(),l=!0),i.lineStart(),t=0;t1&&2&c&&h.push(h.pop().concat(h.shift())),a.push(h.filter(Gc))}return h}}function Gc(t){return t.length>1}function Vc(t,n){return((t=t.x)[0]<0?t[1]-Fa-Oa:Fa-t[1])-((n=n.x)[0]<0?n[1]-Fa-Oa:Fa-n[1])}var $c=Xc(function(){return!0},function(t){var n,e=NaN,r=NaN,i=NaN;return{lineStart:function(){t.lineStart(),n=1},point:function(o,a){var u=o>0?Ya:-Ya,c=Ga(o-e);Ga(c-Ya)0?Fa:-Fa),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(u,r),t.point(o,r),n=0):i!==u&&c>=Ya&&(Ga(e-i)Oa?Va((tu(n)*(o=Wa(r))*tu(e)-tu(r)*(i=Wa(n))*tu(t))/(i*o*a)):(n+r)/2}(e,r,o,a),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(u,r),n=0),t.point(e=o,r=a),i=u},lineEnd:function(){t.lineEnd(),e=r=NaN},clean:function(){return 2-n}}},function(t,n,e,r){var i;if(null==t)i=e*Fa,r.point(-Ya,i),r.point(0,i),r.point(Ya,i),r.point(Ya,0),r.point(Ya,-i),r.point(0,-i),r.point(-Ya,-i),r.point(-Ya,0),r.point(-Ya,i);else if(Ga(t[0]-n[0])>Oa){var o=t[0]0,i=Ga(n)>Oa;function o(t,e){return Wa(t)*Wa(e)>n}function a(t,e,r){var i=[1,0,0],o=Eu(Su(t),Su(e)),a=ku(o,o),u=o[0],c=a-u*u;if(!c)return!r&&t;var f=n*a/c,s=-n*u/c,l=Eu(i,o),h=Pu(i,f);Cu(h,Pu(o,s));var d=l,p=ku(h,d),v=ku(d,d),g=p*p-v*(ku(h,h)-1);if(!(g<0)){var y=eu(g),_=Pu(d,(-p-y)/v);if(Cu(_,h),_=Tu(_),!r)return _;var b,m=t[0],x=e[0],w=t[1],M=e[1];x0^_[1]<(Ga(_[0]-m)Ya^(m<=_[0]&&_[0]<=x)){var T=Pu(d,(-p+y)/v);return Cu(T,h),[_,Tu(T)]}}}function u(n,e){var i=r?t:Ya-t,o=0;return n<-i?o|=1:n>i&&(o|=2),e<-i?o|=4:e>i&&(o|=8),o}return Xc(o,function(t){var n,e,c,f,s;return{lineStart:function(){f=c=!1,s=1},point:function(l,h){var d,p=[l,h],v=o(l,h),g=r?v?0:u(l,h):v?u(l+(l<0?Ya:-Ya),h):0;if(!n&&(f=c=v)&&t.lineStart(),v!==c&&(!(d=a(n,p))||Bc(n,d)||Bc(p,d))&&(p[0]+=Oa,p[1]+=Oa,v=o(p[0],p[1])),v!==c)s=0,v?(t.lineStart(),d=a(p,n),t.point(d[0],d[1])):(d=a(n,p),t.point(d[0],d[1]),t.lineEnd()),n=d;else if(i&&n&&r^v){var y;g&e||!(y=a(p,n,!0))||(s=0,r?(t.lineStart(),t.point(y[0][0],y[0][1]),t.point(y[1][0],y[1][1]),t.lineEnd()):(t.point(y[1][0],y[1][1]),t.lineEnd(),t.lineStart(),t.point(y[0][0],y[0][1])))}!v||n&&Bc(n,p)||t.point(p[0],p[1]),n=p,c=v,e=g},lineEnd:function(){c&&t.lineEnd(),n=null},clean:function(){return s|(f&&c)<<1}}},function(n,r,i,o){Lc(o,t,e,i,n,r)},r?[0,-t]:[-Ya,t-Ya])}var Zc=1e9,Qc=-Zc;function Jc(t,n,e,r){function i(i,o){return t<=i&&i<=e&&n<=o&&o<=r}function o(i,o,u,f){var s=0,l=0;if(null==i||(s=a(i,u))!==(l=a(o,u))||c(i,o)<0^u>0)do{f.point(0===s||3===s?t:e,s>1?r:n)}while((s=(s+u+4)%4)!==l);else f.point(o[0],o[1])}function a(r,i){return Ga(r[0]-t)0?0:3:Ga(r[0]-e)0?2:1:Ga(r[1]-n)0?1:0:i>0?3:2}function u(t,n){return c(t.x,n.x)}function c(t,n){var e=a(t,1),r=a(n,1);return e!==r?e-r:0===e?n[1]-t[1]:1===e?t[0]-n[0]:2===e?t[1]-n[1]:n[0]-t[0]}return function(a){var c,f,s,l,h,d,p,v,g,y,_,b=a,m=Oc(),x={point:w,lineStart:function(){x.point=M,f&&f.push(s=[]);y=!0,g=!1,p=v=NaN},lineEnd:function(){c&&(M(l,h),d&&g&&m.rejoin(),c.push(m.result()));x.point=w,g&&b.lineEnd()},polygonStart:function(){b=m,c=[],f=[],_=!0},polygonEnd:function(){var n=function(){for(var n=0,e=0,i=f.length;er&&(h-o)*(r-a)>(d-a)*(t-o)&&++n:d<=r&&(h-o)*(r-a)<(d-a)*(t-o)&&--n;return n}(),e=_&&n,i=(c=T(c)).length;(e||i)&&(a.polygonStart(),e&&(a.lineStart(),o(null,null,1,a),a.lineEnd()),i&&Fc(c,u,n,o,a),a.polygonEnd());b=a,c=f=s=null}};function w(t,n){i(t,n)&&b.point(t,n)}function M(o,a){var u=i(o,a);if(f&&s.push([o,a]),y)l=o,h=a,d=u,y=!1,u&&(b.lineStart(),b.point(o,a));else if(u&&g)b.point(o,a);else{var c=[p=Math.max(Qc,Math.min(Zc,p)),v=Math.max(Qc,Math.min(Zc,v))],m=[o=Math.max(Qc,Math.min(Zc,o)),a=Math.max(Qc,Math.min(Zc,a))];!function(t,n,e,r,i,o){var a,u=t[0],c=t[1],f=0,s=1,l=n[0]-u,h=n[1]-c;if(a=e-u,l||!(a>0)){if(a/=l,l<0){if(a0){if(a>s)return;a>f&&(f=a)}if(a=i-u,l||!(a<0)){if(a/=l,l<0){if(a>s)return;a>f&&(f=a)}else if(l>0){if(a0)){if(a/=h,h<0){if(a0){if(a>s)return;a>f&&(f=a)}if(a=o-c,h||!(a<0)){if(a/=h,h<0){if(a>s)return;a>f&&(f=a)}else if(h>0){if(a0&&(t[0]=u+f*l,t[1]=c+f*h),s<1&&(n[0]=u+s*l,n[1]=c+s*h),!0}}}}}(c,m,t,n,e,r)?u&&(b.lineStart(),b.point(o,a),_=!1):(g||(b.lineStart(),b.point(c[0],c[1])),b.point(m[0],m[1]),u||b.lineEnd(),_=!1)}p=o,v=a,g=u}return x}}var Kc,tf,nf,ef=Da(),rf={sphere:uu,point:uu,lineStart:function(){rf.point=af,rf.lineEnd=of},lineEnd:uu,polygonStart:uu,polygonEnd:uu};function of(){rf.point=rf.lineEnd=uu}function af(t,n){Kc=t*=Xa,tf=tu(n*=Xa),nf=Wa(n),rf.point=uf}function uf(t,n){t*=Xa;var e=tu(n*=Xa),r=Wa(n),i=Ga(t-Kc),o=Wa(i),a=r*tu(i),u=nf*e-tf*r*o,c=tf*e+nf*r*o;ef.add($a(eu(a*a+u*u),c)),Kc=t,tf=e,nf=r}function cf(t){return ef.reset(),du(t,rf),+ef}var ff=[null,null],sf={type:"LineString",coordinates:ff};function lf(t,n){return ff[0]=t,ff[1]=n,cf(sf)}var hf={Feature:function(t,n){return pf(t.geometry,n)},FeatureCollection:function(t,n){for(var e=t.features,r=-1,i=e.length;++rOa}).map(c)).concat(g(Za(o/d)*d,i,d).filter(function(t){return Ga(t%v)>Oa}).map(f))}return _.lines=function(){return b().map(function(t){return{type:"LineString",coordinates:t}})},_.outline=function(){return{type:"Polygon",coordinates:[s(r).concat(l(a).slice(1),s(e).reverse().slice(1),l(u).reverse().slice(1))]}},_.extent=function(t){return arguments.length?_.extentMajor(t).extentMinor(t):_.extentMinor()},_.extentMajor=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],u=+t[0][1],a=+t[1][1],r>e&&(t=r,r=e,e=t),u>a&&(t=u,u=a,a=t),_.precision(y)):[[r,u],[e,a]]},_.extentMinor=function(e){return arguments.length?(n=+e[0][0],t=+e[1][0],o=+e[0][1],i=+e[1][1],n>t&&(e=n,n=t,t=e),o>i&&(e=o,o=i,i=e),_.precision(y)):[[n,o],[t,i]]},_.step=function(t){return arguments.length?_.stepMajor(t).stepMinor(t):_.stepMinor()},_.stepMajor=function(t){return arguments.length?(p=+t[0],v=+t[1],_):[p,v]},_.stepMinor=function(t){return arguments.length?(h=+t[0],d=+t[1],_):[h,d]},_.precision=function(h){return arguments.length?(y=+h,c=mf(o,i,90),f=xf(n,t,y),s=mf(u,a,90),l=xf(r,e,y),_):y},_.extentMajor([[-180,-90+Oa],[180,90-Oa]]).extentMinor([[-180,-80-Oa],[180,80+Oa]])}function Mf(t){return t}var Nf,Af,Tf,Sf,kf=Da(),Ef=Da(),Cf={point:uu,lineStart:uu,lineEnd:uu,polygonStart:function(){Cf.lineStart=Pf,Cf.lineEnd=Df},polygonEnd:function(){Cf.lineStart=Cf.lineEnd=Cf.point=uu,kf.add(Ga(Ef)),Ef.reset()},result:function(){var t=kf/2;return kf.reset(),t}};function Pf(){Cf.point=zf}function zf(t,n){Cf.point=Rf,Nf=Tf=t,Af=Sf=n}function Rf(t,n){Ef.add(Sf*t-Tf*n),Tf=t,Sf=n}function Df(){Rf(Nf,Af)}var qf=1/0,Lf=qf,Uf=-qf,Of=Uf,Bf={point:function(t,n){tUf&&(Uf=t);nOf&&(Of=n)},lineStart:uu,lineEnd:uu,polygonStart:uu,polygonEnd:uu,result:function(){var t=[[qf,Lf],[Uf,Of]];return Uf=Of=-(Lf=qf=1/0),t}};var Yf,Ff,If,jf,Hf=0,Xf=0,Gf=0,Vf=0,$f=0,Wf=0,Zf=0,Qf=0,Jf=0,Kf={point:ts,lineStart:ns,lineEnd:is,polygonStart:function(){Kf.lineStart=os,Kf.lineEnd=as},polygonEnd:function(){Kf.point=ts,Kf.lineStart=ns,Kf.lineEnd=is},result:function(){var t=Jf?[Zf/Jf,Qf/Jf]:Wf?[Vf/Wf,$f/Wf]:Gf?[Hf/Gf,Xf/Gf]:[NaN,NaN];return Hf=Xf=Gf=Vf=$f=Wf=Zf=Qf=Jf=0,t}};function ts(t,n){Hf+=t,Xf+=n,++Gf}function ns(){Kf.point=es}function es(t,n){Kf.point=rs,ts(If=t,jf=n)}function rs(t,n){var e=t-If,r=n-jf,i=eu(e*e+r*r);Vf+=i*(If+t)/2,$f+=i*(jf+n)/2,Wf+=i,ts(If=t,jf=n)}function is(){Kf.point=ts}function os(){Kf.point=us}function as(){cs(Yf,Ff)}function us(t,n){Kf.point=cs,ts(Yf=If=t,Ff=jf=n)}function cs(t,n){var e=t-If,r=n-jf,i=eu(e*e+r*r);Vf+=i*(If+t)/2,$f+=i*(jf+n)/2,Wf+=i,Zf+=(i=jf*t-If*n)*(If+t),Qf+=i*(jf+n),Jf+=3*i,ts(If=t,jf=n)}function fs(t){this._context=t}fs.prototype={_radius:4.5,pointRadius:function(t){return this._radius=t,this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._context.closePath(),this._point=NaN},point:function(t,n){switch(this._point){case 0:this._context.moveTo(t,n),this._point=1;break;case 1:this._context.lineTo(t,n);break;default:this._context.moveTo(t+this._radius,n),this._context.arc(t,n,this._radius,0,ja)}},result:uu};var ss,ls,hs,ds,ps,vs=Da(),gs={point:uu,lineStart:function(){gs.point=ys},lineEnd:function(){ss&&_s(ls,hs),gs.point=uu},polygonStart:function(){ss=!0},polygonEnd:function(){ss=null},result:function(){var t=+vs;return vs.reset(),t}};function ys(t,n){gs.point=_s,ls=ds=t,hs=ps=n}function _s(t,n){ds-=t,ps-=n,vs.add(eu(ds*ds+ps*ps)),ds=t,ps=n}function bs(){this._string=[]}function ms(t){return"m0,"+t+"a"+t+","+t+" 0 1,1 0,"+-2*t+"a"+t+","+t+" 0 1,1 0,"+2*t+"z"}function xs(t){return function(n){var e=new ws;for(var r in t)e[r]=t[r];return e.stream=n,e}}function ws(){}function Ms(t,n,e){var r=t.clipExtent&&t.clipExtent();return t.scale(150).translate([0,0]),null!=r&&t.clipExtent(null),du(e,t.stream(Bf)),n(Bf.result()),null!=r&&t.clipExtent(r),t}function Ns(t,n,e){return Ms(t,function(e){var r=n[1][0]-n[0][0],i=n[1][1]-n[0][1],o=Math.min(r/(e[1][0]-e[0][0]),i/(e[1][1]-e[0][1])),a=+n[0][0]+(r-o*(e[1][0]+e[0][0]))/2,u=+n[0][1]+(i-o*(e[1][1]+e[0][1]))/2;t.scale(150*o).translate([a,u])},e)}function As(t,n,e){return Ns(t,[[0,0],n],e)}function Ts(t,n,e){return Ms(t,function(e){var r=+n,i=r/(e[1][0]-e[0][0]),o=(r-i*(e[1][0]+e[0][0]))/2,a=-i*e[0][1];t.scale(150*i).translate([o,a])},e)}function Ss(t,n,e){return Ms(t,function(e){var r=+n,i=r/(e[1][1]-e[0][1]),o=-i*e[0][0],a=(r-i*(e[1][1]+e[0][1]))/2;t.scale(150*i).translate([o,a])},e)}bs.prototype={_radius:4.5,_circle:ms(4.5),pointRadius:function(t){return(t=+t)!==this._radius&&(this._radius=t,this._circle=null),this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._string.push("Z"),this._point=NaN},point:function(t,n){switch(this._point){case 0:this._string.push("M",t,",",n),this._point=1;break;case 1:this._string.push("L",t,",",n);break;default:null==this._circle&&(this._circle=ms(this._radius)),this._string.push("M",t,",",n,this._circle)}},result:function(){if(this._string.length){var t=this._string.join("");return this._string=[],t}return null}},ws.prototype={constructor:ws,point:function(t,n){this.stream.point(t,n)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}};var ks=16,Es=Wa(30*Xa);function Cs(t,n){return+n?function(t,n){function e(r,i,o,a,u,c,f,s,l,h,d,p,v,g){var y=f-r,_=s-i,b=y*y+_*_;if(b>4*n&&v--){var m=a+h,x=u+d,w=c+p,M=eu(m*m+x*x+w*w),N=ou(w/=M),A=Ga(Ga(w)-1)n||Ga((y*E+_*C)/b-.5)>.3||a*h+u*d+c*p2?t[2]%360*Xa:0,S()):[g*Ha,y*Ha,_*Ha]},A.angle=function(t){return arguments.length?(b=t%360*Xa,S()):b*Ha},A.precision=function(t){return arguments.length?(a=Cs(u,N=t*t),k()):eu(N)},A.fitExtent=function(t,n){return Ns(A,t,n)},A.fitSize=function(t,n){return As(A,t,n)},A.fitWidth=function(t,n){return Ts(A,t,n)},A.fitHeight=function(t,n){return Ss(A,t,n)},function(){return n=t.apply(this,arguments),A.invert=n.invert&&T,S()}}function qs(t){var n=0,e=Ya/3,r=Ds(t),i=r(n,e);return i.parallels=function(t){return arguments.length?r(n=t[0]*Xa,e=t[1]*Xa):[n*Ha,e*Ha]},i}function Ls(t,n){var e=tu(t),r=(e+tu(n))/2;if(Ga(r)0?n<-Fa+Oa&&(n=-Fa+Oa):n>Fa-Oa&&(n=Fa-Oa);var e=i/Ka(Xs(n),r);return[e*tu(r*t),i-e*Wa(r*t)]}return o.invert=function(t,n){var e=i-n,o=nu(r)*eu(t*t+e*e);return[$a(t,Ga(e))/r*nu(e),2*Va(Ka(i/o,1/r))-Fa]},o}function Vs(t,n){return[t,n]}function $s(t,n){var e=Wa(t),r=t===n?tu(t):(e-Wa(n))/(n-t),i=e/r+t;if(Ga(r)=0;)n+=e[r].value;else n=1;t.value=n}function ll(t,n){var e,r,i,o,a,u=new vl(t),c=+t.value&&(u.value=t.value),f=[u];for(null==n&&(n=hl);e=f.pop();)if(c&&(e.value=+e.data.value),(i=n(e.data))&&(a=i.length))for(e.children=new Array(a),o=a-1;o>=0;--o)f.push(r=e.children[o]=new vl(i[o])),r.parent=e,r.depth=e.depth+1;return u.eachBefore(pl)}function hl(t){return t.children}function dl(t){t.data=t.data.data}function pl(t){var n=0;do{t.height=n}while((t=t.parent)&&t.height<++n)}function vl(t){this.data=t,this.depth=this.height=0,this.parent=null}tl.invert=function(t,n){for(var e,r=n,i=r*r,o=i*i*i,a=0;a<12&&(o=(i=(r-=e=(r*(Ws+Zs*i+o*(Qs+Js*i))-n)/(Ws+3*Zs*i+o*(7*Qs+9*Js*i)))*r)*i*i,!(Ga(e)Oa&&--i>0);return[t/(.8707+(o=r*r)*(o*(o*o*o*(.003971-.001529*o)-.013791)-.131979)),r]},il.invert=Ys(ou),ol.invert=Ys(function(t){return 2*Va(t)}),al.invert=function(t,n){return[-n,2*Va(Qa(t))-Fa]},vl.prototype=ll.prototype={constructor:vl,count:function(){return this.eachAfter(sl)},each:function(t){var n,e,r,i,o=this,a=[o];do{for(n=a.reverse(),a=[];o=n.pop();)if(t(o),e=o.children)for(r=0,i=e.length;r=0;--e)i.push(n[e]);return this},sum:function(t){return this.eachAfter(function(n){for(var e=+t(n.data)||0,r=n.children,i=r&&r.length;--i>=0;)e+=r[i].value;n.value=e})},sort:function(t){return this.eachBefore(function(n){n.children&&n.children.sort(t)})},path:function(t){for(var n=this,e=function(t,n){if(t===n)return t;var e=t.ancestors(),r=n.ancestors(),i=null;for(t=e.pop(),n=r.pop();t===n;)i=t,t=e.pop(),n=r.pop();return i}(n,t),r=[n];n!==e;)n=n.parent,r.push(n);for(var i=r.length;t!==e;)r.splice(i,0,t),t=t.parent;return r},ancestors:function(){for(var t=this,n=[t];t=t.parent;)n.push(t);return n},descendants:function(){var t=[];return this.each(function(n){t.push(n)}),t},leaves:function(){var t=[];return this.eachBefore(function(n){n.children||t.push(n)}),t},links:function(){var t=this,n=[];return t.each(function(e){e!==t&&n.push({source:e.parent,target:e})}),n},copy:function(){return ll(this).eachBefore(dl)}};var gl=Array.prototype.slice;function yl(t){for(var n,e,r=0,i=(t=function(t){for(var n,e,r=t.length;r;)e=Math.random()*r--|0,n=t[r],t[r]=t[e],t[e]=n;return t}(gl.call(t))).length,o=[];r0&&e*e>r*r+i*i}function xl(t,n){for(var e=0;e(a*=a)?(r=(f+a-i)/(2*f),o=Math.sqrt(Math.max(0,a/f-r*r)),e.x=t.x-r*u-o*c,e.y=t.y-r*c+o*u):(r=(f+i-a)/(2*f),o=Math.sqrt(Math.max(0,i/f-r*r)),e.x=n.x+r*u-o*c,e.y=n.y+r*c+o*u)):(e.x=n.x+e.r,e.y=n.y)}function Tl(t,n){var e=t.r+n.r-1e-6,r=n.x-t.x,i=n.y-t.y;return e>0&&e*e>r*r+i*i}function Sl(t){var n=t._,e=t.next._,r=n.r+e.r,i=(n.x*e.r+e.x*n.r)/r,o=(n.y*e.r+e.y*n.r)/r;return i*i+o*o}function kl(t){this._=t,this.next=null,this.previous=null}function El(t){if(!(i=t.length))return 0;var n,e,r,i,o,a,u,c,f,s,l;if((n=t[0]).x=0,n.y=0,!(i>1))return n.r;if(e=t[1],n.x=-e.r,e.x=n.r,e.y=0,!(i>2))return n.r+e.r;Al(e,n,r=t[2]),n=new kl(n),e=new kl(e),r=new kl(r),n.next=r.previous=e,e.next=n.previous=r,r.next=e.previous=n;t:for(u=3;uh&&(h=u),g=s*s*v,(d=Math.max(h/g,g/l))>p){s-=u;break}p=d}y.push(a={value:s,dice:c1?n:1)},e}(Ql);var th=function t(n){function e(t,e,r,i,o){if((a=t._squarify)&&a.ratio===n)for(var a,u,c,f,s,l=-1,h=a.length,d=t.value;++l1?n:1)},e}(Ql);function nh(t,n){return t[0]-n[0]||t[1]-n[1]}function eh(t){for(var n,e,r,i=t.length,o=[0,1],a=2,u=2;u1&&(n=t[o[a-2]],e=t[o[a-1]],r=t[u],(e[0]-n[0])*(r[1]-n[1])-(e[1]-n[1])*(r[0]-n[0])<=0);)--a;o[a++]=u}return o.slice(0,a)}function rh(){return Math.random()}var ih=function t(n){function e(t,e){return t=null==t?0:+t,e=null==e?1:+e,1===arguments.length?(e=t,t=0):e-=t,function(){return n()*e+t}}return e.source=t,e}(rh),oh=function t(n){function e(t,e){var r,i;return t=null==t?0:+t,e=null==e?1:+e,function(){var o;if(null!=r)o=r,r=null;else do{r=2*n()-1,o=2*n()-1,i=r*r+o*o}while(!i||i>1);return t+e*o*Math.sqrt(-2*Math.log(i)/i)}}return e.source=t,e}(rh),ah=function t(n){function e(){var t=oh.source(n).apply(this,arguments);return function(){return Math.exp(t())}}return e.source=t,e}(rh),uh=function t(n){function e(t){return function(){for(var e=0,r=0;rr&&(n=e,e=r,r=n),function(t){return Math.max(e,Math.min(r,t))}}function Mh(t,n,e){var r=t[0],i=t[1],o=n[0],a=n[1];return i2?Nh:Mh,i=o=null,l}function l(n){return isNaN(n=+n)?e:(i||(i=r(a.map(t),u,c)))(t(f(n)))}return l.invert=function(e){return f(n((o||(o=r(u,a.map(t),he)))(e)))},l.domain=function(t){return arguments.length?(a=dh.call(t,_h),f===mh||(f=wh(a)),s()):a.slice()},l.range=function(t){return arguments.length?(u=ph.call(t),s()):u.slice()},l.rangeRound=function(t){return u=ph.call(t),c=_e,s()},l.clamp=function(t){return arguments.length?(f=t?wh(a):mh,l):f!==mh},l.interpolate=function(t){return arguments.length?(c=t,s()):c},l.unknown=function(t){return arguments.length?(e=t,l):e},function(e,r){return t=e,n=r,s()}}function Sh(t,n){return Th()(t,n)}function kh(n,e,r,i){var o,a=w(n,e,r);switch((i=wa(null==i?",f":i)).type){case"s":var u=Math.max(Math.abs(n),Math.abs(e));return null!=i.precision||isNaN(o=za(a,u))||(i.precision=o),t.formatPrefix(i,u);case"":case"e":case"g":case"p":case"r":null!=i.precision||isNaN(o=Ra(a,Math.max(Math.abs(n),Math.abs(e))))||(i.precision=o-("e"===i.type));break;case"f":case"%":null!=i.precision||isNaN(o=Pa(a))||(i.precision=o-2*("%"===i.type))}return t.format(i)}function Eh(t){var n=t.domain;return t.ticks=function(t){var e=n();return m(e[0],e[e.length-1],null==t?10:t)},t.tickFormat=function(t,e){var r=n();return kh(r[0],r[r.length-1],null==t?10:t,e)},t.nice=function(e){null==e&&(e=10);var r,i=n(),o=0,a=i.length-1,u=i[o],c=i[a];return c0?r=x(u=Math.floor(u/r)*r,c=Math.ceil(c/r)*r,e):r<0&&(r=x(u=Math.ceil(u*r)/r,c=Math.floor(c*r)/r,e)),r>0?(i[o]=Math.floor(u/r)*r,i[a]=Math.ceil(c/r)*r,n(i)):r<0&&(i[o]=Math.ceil(u*r)/r,i[a]=Math.floor(c*r)/r,n(i)),t},t}function Ch(t,n){var e,r=0,i=(t=t.slice()).length-1,o=t[r],a=t[i];return a0){for(;hc)break;v.push(l)}}else for(;h=1;--s)if(!((l=f*s)c)break;v.push(l)}}else v=m(h,d,Math.min(d-h,p)).map(r);return n?v.reverse():v},i.tickFormat=function(n,o){if(null==o&&(o=10===a?".0e":","),"function"!=typeof o&&(o=t.format(o)),n===1/0)return o;null==n&&(n=10);var u=Math.max(1,a*n/i.ticks().length);return function(t){var n=t/r(Math.round(e(t)));return n*a0))return u;do{u.push(a=new Date(+e)),n(e,o),t(e)}while(a=n)for(;t(n),!e(n);)n.setTime(n-1)},function(t,r){if(t>=t)if(r<0)for(;++r<=0;)for(;n(t,-1),!e(t););else for(;--r>=0;)for(;n(t,1),!e(t););})},e&&(i.count=function(n,r){return Gh.setTime(+n),Vh.setTime(+r),t(Gh),t(Vh),Math.floor(e(Gh,Vh))},i.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?i.filter(r?function(n){return r(n)%t==0}:function(n){return i.count(0,n)%t==0}):i:null}),i}var Wh=$h(function(){},function(t,n){t.setTime(+t+n)},function(t,n){return n-t});Wh.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?$h(function(n){n.setTime(Math.floor(n/t)*t)},function(n,e){n.setTime(+n+e*t)},function(n,e){return(e-n)/t}):Wh:null};var Zh=Wh.range,Qh=6e4,Jh=6048e5,Kh=$h(function(t){t.setTime(t-t.getMilliseconds())},function(t,n){t.setTime(+t+1e3*n)},function(t,n){return(n-t)/1e3},function(t){return t.getUTCSeconds()}),td=Kh.range,nd=$h(function(t){t.setTime(t-t.getMilliseconds()-1e3*t.getSeconds())},function(t,n){t.setTime(+t+n*Qh)},function(t,n){return(n-t)/Qh},function(t){return t.getMinutes()}),ed=nd.range,rd=$h(function(t){t.setTime(t-t.getMilliseconds()-1e3*t.getSeconds()-t.getMinutes()*Qh)},function(t,n){t.setTime(+t+36e5*n)},function(t,n){return(n-t)/36e5},function(t){return t.getHours()}),id=rd.range,od=$h(function(t){t.setHours(0,0,0,0)},function(t,n){t.setDate(t.getDate()+n)},function(t,n){return(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*Qh)/864e5},function(t){return t.getDate()-1}),ad=od.range;function ud(t){return $h(function(n){n.setDate(n.getDate()-(n.getDay()+7-t)%7),n.setHours(0,0,0,0)},function(t,n){t.setDate(t.getDate()+7*n)},function(t,n){return(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*Qh)/Jh})}var cd=ud(0),fd=ud(1),sd=ud(2),ld=ud(3),hd=ud(4),dd=ud(5),pd=ud(6),vd=cd.range,gd=fd.range,yd=sd.range,_d=ld.range,bd=hd.range,md=dd.range,xd=pd.range,wd=$h(function(t){t.setDate(1),t.setHours(0,0,0,0)},function(t,n){t.setMonth(t.getMonth()+n)},function(t,n){return n.getMonth()-t.getMonth()+12*(n.getFullYear()-t.getFullYear())},function(t){return t.getMonth()}),Md=wd.range,Nd=$h(function(t){t.setMonth(0,1),t.setHours(0,0,0,0)},function(t,n){t.setFullYear(t.getFullYear()+n)},function(t,n){return n.getFullYear()-t.getFullYear()},function(t){return t.getFullYear()});Nd.every=function(t){return isFinite(t=Math.floor(t))&&t>0?$h(function(n){n.setFullYear(Math.floor(n.getFullYear()/t)*t),n.setMonth(0,1),n.setHours(0,0,0,0)},function(n,e){n.setFullYear(n.getFullYear()+e*t)}):null};var Ad=Nd.range,Td=$h(function(t){t.setUTCSeconds(0,0)},function(t,n){t.setTime(+t+n*Qh)},function(t,n){return(n-t)/Qh},function(t){return t.getUTCMinutes()}),Sd=Td.range,kd=$h(function(t){t.setUTCMinutes(0,0,0)},function(t,n){t.setTime(+t+36e5*n)},function(t,n){return(n-t)/36e5},function(t){return t.getUTCHours()}),Ed=kd.range,Cd=$h(function(t){t.setUTCHours(0,0,0,0)},function(t,n){t.setUTCDate(t.getUTCDate()+n)},function(t,n){return(n-t)/864e5},function(t){return t.getUTCDate()-1}),Pd=Cd.range;function zd(t){return $h(function(n){n.setUTCDate(n.getUTCDate()-(n.getUTCDay()+7-t)%7),n.setUTCHours(0,0,0,0)},function(t,n){t.setUTCDate(t.getUTCDate()+7*n)},function(t,n){return(n-t)/Jh})}var Rd=zd(0),Dd=zd(1),qd=zd(2),Ld=zd(3),Ud=zd(4),Od=zd(5),Bd=zd(6),Yd=Rd.range,Fd=Dd.range,Id=qd.range,jd=Ld.range,Hd=Ud.range,Xd=Od.range,Gd=Bd.range,Vd=$h(function(t){t.setUTCDate(1),t.setUTCHours(0,0,0,0)},function(t,n){t.setUTCMonth(t.getUTCMonth()+n)},function(t,n){return n.getUTCMonth()-t.getUTCMonth()+12*(n.getUTCFullYear()-t.getUTCFullYear())},function(t){return t.getUTCMonth()}),$d=Vd.range,Wd=$h(function(t){t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)},function(t,n){t.setUTCFullYear(t.getUTCFullYear()+n)},function(t,n){return n.getUTCFullYear()-t.getUTCFullYear()},function(t){return t.getUTCFullYear()});Wd.every=function(t){return isFinite(t=Math.floor(t))&&t>0?$h(function(n){n.setUTCFullYear(Math.floor(n.getUTCFullYear()/t)*t),n.setUTCMonth(0,1),n.setUTCHours(0,0,0,0)},function(n,e){n.setUTCFullYear(n.getUTCFullYear()+e*t)}):null};var Zd=Wd.range;function Qd(t){if(0<=t.y&&t.y<100){var n=new Date(-1,t.m,t.d,t.H,t.M,t.S,t.L);return n.setFullYear(t.y),n}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function Jd(t){if(0<=t.y&&t.y<100){var n=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return n.setUTCFullYear(t.y),n}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function Kd(t){return{y:t,m:0,d:1,H:0,M:0,S:0,L:0}}function tp(t){var n=t.dateTime,e=t.date,r=t.time,i=t.periods,o=t.days,a=t.shortDays,u=t.months,c=t.shortMonths,f=cp(i),s=fp(i),l=cp(o),h=fp(o),d=cp(a),p=fp(a),v=cp(u),g=fp(u),y=cp(c),_=fp(c),b={a:function(t){return a[t.getDay()]},A:function(t){return o[t.getDay()]},b:function(t){return c[t.getMonth()]},B:function(t){return u[t.getMonth()]},c:null,d:Ep,e:Ep,f:Dp,H:Cp,I:Pp,j:zp,L:Rp,m:qp,M:Lp,p:function(t){return i[+(t.getHours()>=12)]},Q:sv,s:lv,S:Up,u:Op,U:Bp,V:Yp,w:Fp,W:Ip,x:null,X:null,y:jp,Y:Hp,Z:Xp,"%":fv},m={a:function(t){return a[t.getUTCDay()]},A:function(t){return o[t.getUTCDay()]},b:function(t){return c[t.getUTCMonth()]},B:function(t){return u[t.getUTCMonth()]},c:null,d:Gp,e:Gp,f:Qp,H:Vp,I:$p,j:Wp,L:Zp,m:Jp,M:Kp,p:function(t){return i[+(t.getUTCHours()>=12)]},Q:sv,s:lv,S:tv,u:nv,U:ev,V:rv,w:iv,W:ov,x:null,X:null,y:av,Y:uv,Z:cv,"%":fv},x={a:function(t,n,e){var r=d.exec(n.slice(e));return r?(t.w=p[r[0].toLowerCase()],e+r[0].length):-1},A:function(t,n,e){var r=l.exec(n.slice(e));return r?(t.w=h[r[0].toLowerCase()],e+r[0].length):-1},b:function(t,n,e){var r=y.exec(n.slice(e));return r?(t.m=_[r[0].toLowerCase()],e+r[0].length):-1},B:function(t,n,e){var r=v.exec(n.slice(e));return r?(t.m=g[r[0].toLowerCase()],e+r[0].length):-1},c:function(t,e,r){return N(t,n,e,r)},d:bp,e:bp,f:Ap,H:xp,I:xp,j:mp,L:Np,m:_p,M:wp,p:function(t,n,e){var r=f.exec(n.slice(e));return r?(t.p=s[r[0].toLowerCase()],e+r[0].length):-1},Q:Sp,s:kp,S:Mp,u:lp,U:hp,V:dp,w:sp,W:pp,x:function(t,n,r){return N(t,e,n,r)},X:function(t,n,e){return N(t,r,n,e)},y:gp,Y:vp,Z:yp,"%":Tp};function w(t,n){return function(e){var r,i,o,a=[],u=-1,c=0,f=t.length;for(e instanceof Date||(e=new Date(+e));++u53)return null;"w"in o||(o.w=1),"Z"in o?(i=(r=Jd(Kd(o.y))).getUTCDay(),r=i>4||0===i?Dd.ceil(r):Dd(r),r=Cd.offset(r,7*(o.V-1)),o.y=r.getUTCFullYear(),o.m=r.getUTCMonth(),o.d=r.getUTCDate()+(o.w+6)%7):(i=(r=n(Kd(o.y))).getDay(),r=i>4||0===i?fd.ceil(r):fd(r),r=od.offset(r,7*(o.V-1)),o.y=r.getFullYear(),o.m=r.getMonth(),o.d=r.getDate()+(o.w+6)%7)}else("W"in o||"U"in o)&&("w"in o||(o.w="u"in o?o.u%7:"W"in o?1:0),i="Z"in o?Jd(Kd(o.y)).getUTCDay():n(Kd(o.y)).getDay(),o.m=0,o.d="W"in o?(o.w+6)%7+7*o.W-(i+5)%7:o.w+7*o.U-(i+6)%7);return"Z"in o?(o.H+=o.Z/100|0,o.M+=o.Z%100,Jd(o)):n(o)}}function N(t,n,e,r){for(var i,o,a=0,u=n.length,c=e.length;a=c)return-1;if(37===(i=n.charCodeAt(a++))){if(i=n.charAt(a++),!(o=x[i in ep?n.charAt(a++):i])||(r=o(t,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}return b.x=w(e,b),b.X=w(r,b),b.c=w(n,b),m.x=w(e,m),m.X=w(r,m),m.c=w(n,m),{format:function(t){var n=w(t+="",b);return n.toString=function(){return t},n},parse:function(t){var n=M(t+="",Qd);return n.toString=function(){return t},n},utcFormat:function(t){var n=w(t+="",m);return n.toString=function(){return t},n},utcParse:function(t){var n=M(t,Jd);return n.toString=function(){return t},n}}}var np,ep={"-":"",_:" ",0:"0"},rp=/^\s*\d+/,ip=/^%/,op=/[\\^$*+?|[\]().{}]/g;function ap(t,n,e){var r=t<0?"-":"",i=(r?-t:t)+"",o=i.length;return r+(o68?1900:2e3),e+r[0].length):-1}function yp(t,n,e){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(n.slice(e,e+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),e+r[0].length):-1}function _p(t,n,e){var r=rp.exec(n.slice(e,e+2));return r?(t.m=r[0]-1,e+r[0].length):-1}function bp(t,n,e){var r=rp.exec(n.slice(e,e+2));return r?(t.d=+r[0],e+r[0].length):-1}function mp(t,n,e){var r=rp.exec(n.slice(e,e+3));return r?(t.m=0,t.d=+r[0],e+r[0].length):-1}function xp(t,n,e){var r=rp.exec(n.slice(e,e+2));return r?(t.H=+r[0],e+r[0].length):-1}function wp(t,n,e){var r=rp.exec(n.slice(e,e+2));return r?(t.M=+r[0],e+r[0].length):-1}function Mp(t,n,e){var r=rp.exec(n.slice(e,e+2));return r?(t.S=+r[0],e+r[0].length):-1}function Np(t,n,e){var r=rp.exec(n.slice(e,e+3));return r?(t.L=+r[0],e+r[0].length):-1}function Ap(t,n,e){var r=rp.exec(n.slice(e,e+6));return r?(t.L=Math.floor(r[0]/1e3),e+r[0].length):-1}function Tp(t,n,e){var r=ip.exec(n.slice(e,e+1));return r?e+r[0].length:-1}function Sp(t,n,e){var r=rp.exec(n.slice(e));return r?(t.Q=+r[0],e+r[0].length):-1}function kp(t,n,e){var r=rp.exec(n.slice(e));return r?(t.Q=1e3*+r[0],e+r[0].length):-1}function Ep(t,n){return ap(t.getDate(),n,2)}function Cp(t,n){return ap(t.getHours(),n,2)}function Pp(t,n){return ap(t.getHours()%12||12,n,2)}function zp(t,n){return ap(1+od.count(Nd(t),t),n,3)}function Rp(t,n){return ap(t.getMilliseconds(),n,3)}function Dp(t,n){return Rp(t,n)+"000"}function qp(t,n){return ap(t.getMonth()+1,n,2)}function Lp(t,n){return ap(t.getMinutes(),n,2)}function Up(t,n){return ap(t.getSeconds(),n,2)}function Op(t){var n=t.getDay();return 0===n?7:n}function Bp(t,n){return ap(cd.count(Nd(t),t),n,2)}function Yp(t,n){var e=t.getDay();return t=e>=4||0===e?hd(t):hd.ceil(t),ap(hd.count(Nd(t),t)+(4===Nd(t).getDay()),n,2)}function Fp(t){return t.getDay()}function Ip(t,n){return ap(fd.count(Nd(t),t),n,2)}function jp(t,n){return ap(t.getFullYear()%100,n,2)}function Hp(t,n){return ap(t.getFullYear()%1e4,n,4)}function Xp(t){var n=t.getTimezoneOffset();return(n>0?"-":(n*=-1,"+"))+ap(n/60|0,"0",2)+ap(n%60,"0",2)}function Gp(t,n){return ap(t.getUTCDate(),n,2)}function Vp(t,n){return ap(t.getUTCHours(),n,2)}function $p(t,n){return ap(t.getUTCHours()%12||12,n,2)}function Wp(t,n){return ap(1+Cd.count(Wd(t),t),n,3)}function Zp(t,n){return ap(t.getUTCMilliseconds(),n,3)}function Qp(t,n){return Zp(t,n)+"000"}function Jp(t,n){return ap(t.getUTCMonth()+1,n,2)}function Kp(t,n){return ap(t.getUTCMinutes(),n,2)}function tv(t,n){return ap(t.getUTCSeconds(),n,2)}function nv(t){var n=t.getUTCDay();return 0===n?7:n}function ev(t,n){return ap(Rd.count(Wd(t),t),n,2)}function rv(t,n){var e=t.getUTCDay();return t=e>=4||0===e?Ud(t):Ud.ceil(t),ap(Ud.count(Wd(t),t)+(4===Wd(t).getUTCDay()),n,2)}function iv(t){return t.getUTCDay()}function ov(t,n){return ap(Dd.count(Wd(t),t),n,2)}function av(t,n){return ap(t.getUTCFullYear()%100,n,2)}function uv(t,n){return ap(t.getUTCFullYear()%1e4,n,4)}function cv(){return"+0000"}function fv(){return"%"}function sv(t){return+t}function lv(t){return Math.floor(+t/1e3)}function hv(n){return np=tp(n),t.timeFormat=np.format,t.timeParse=np.parse,t.utcFormat=np.utcFormat,t.utcParse=np.utcParse,np}hv({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});var dv=Date.prototype.toISOString?function(t){return t.toISOString()}:t.utcFormat("%Y-%m-%dT%H:%M:%S.%LZ");var pv=+new Date("2000-01-01T00:00:00.000Z")?function(t){var n=new Date(t);return isNaN(n)?null:n}:t.utcParse("%Y-%m-%dT%H:%M:%S.%LZ"),vv=1e3,gv=60*vv,yv=60*gv,_v=24*yv,bv=7*_v,mv=30*_v,xv=365*_v;function wv(t){return new Date(t)}function Mv(t){return t instanceof Date?+t:+new Date(+t)}function Nv(t,n,r,i,o,a,u,c,f){var s=Sh(mh,mh),l=s.invert,h=s.domain,d=f(".%L"),p=f(":%S"),v=f("%I:%M"),g=f("%I %p"),y=f("%a %d"),_=f("%b %d"),b=f("%B"),m=f("%Y"),x=[[u,1,vv],[u,5,5*vv],[u,15,15*vv],[u,30,30*vv],[a,1,gv],[a,5,5*gv],[a,15,15*gv],[a,30,30*gv],[o,1,yv],[o,3,3*yv],[o,6,6*yv],[o,12,12*yv],[i,1,_v],[i,2,2*_v],[r,1,bv],[n,1,mv],[n,3,3*mv],[t,1,xv]];function M(e){return(u(e)=1?fy:t<=-1?-fy:Math.asin(t)}function hy(t){return t.innerRadius}function dy(t){return t.outerRadius}function py(t){return t.startAngle}function vy(t){return t.endAngle}function gy(t){return t&&t.padAngle}function yy(t,n,e,r,i,o,a){var u=t-e,c=n-r,f=(a?o:-o)/ay(u*u+c*c),s=f*c,l=-f*u,h=t+s,d=n+l,p=e+s,v=r+l,g=(h+p)/2,y=(d+v)/2,_=p-h,b=v-d,m=_*_+b*b,x=i-o,w=h*v-p*d,M=(b<0?-1:1)*ay(ry(0,x*x*m-w*w)),N=(w*b-_*M)/m,A=(-w*_-b*M)/m,T=(w*b+_*M)/m,S=(-w*_+b*M)/m,k=N-g,E=A-y,C=T-g,P=S-y;return k*k+E*E>C*C+P*P&&(N=T,A=S),{cx:N,cy:A,x01:-s,y01:-l,x11:N*(i/x-1),y11:A*(i/x-1)}}function _y(t){this._context=t}function by(t){return new _y(t)}function my(t){return t[0]}function xy(t){return t[1]}function wy(){var t=my,n=xy,e=Kg(!0),r=null,i=by,o=null;function a(a){var u,c,f,s=a.length,l=!1;for(null==r&&(o=i(f=Hi())),u=0;u<=s;++u)!(u=s;--l)u.point(g[l],y[l]);u.lineEnd(),u.areaEnd()}v&&(g[f]=+t(h,f,c),y[f]=+e(h,f,c),u.point(n?+n(h,f,c):g[f],r?+r(h,f,c):y[f]))}if(d)return u=null,d+""||null}function f(){return wy().defined(i).curve(a).context(o)}return c.x=function(e){return arguments.length?(t="function"==typeof e?e:Kg(+e),n=null,c):t},c.x0=function(n){return arguments.length?(t="function"==typeof n?n:Kg(+n),c):t},c.x1=function(t){return arguments.length?(n=null==t?null:"function"==typeof t?t:Kg(+t),c):n},c.y=function(t){return arguments.length?(e="function"==typeof t?t:Kg(+t),r=null,c):e},c.y0=function(t){return arguments.length?(e="function"==typeof t?t:Kg(+t),c):e},c.y1=function(t){return arguments.length?(r=null==t?null:"function"==typeof t?t:Kg(+t),c):r},c.lineX0=c.lineY0=function(){return f().x(t).y(e)},c.lineY1=function(){return f().x(t).y(r)},c.lineX1=function(){return f().x(n).y(e)},c.defined=function(t){return arguments.length?(i="function"==typeof t?t:Kg(!!t),c):i},c.curve=function(t){return arguments.length?(a=t,null!=o&&(u=a(o)),c):a},c.context=function(t){return arguments.length?(null==t?o=u=null:u=a(o=t),c):o},c}function Ny(t,n){return nt?1:n>=t?0:NaN}function Ay(t){return t}_y.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:this._context.lineTo(t,n)}}};var Ty=ky(by);function Sy(t){this._curve=t}function ky(t){function n(n){return new Sy(t(n))}return n._curve=t,n}function Ey(t){var n=t.curve;return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t.curve=function(t){return arguments.length?n(ky(t)):n()._curve},t}function Cy(){return Ey(wy().curve(Ty))}function Py(){var t=My().curve(Ty),n=t.curve,e=t.lineX0,r=t.lineX1,i=t.lineY0,o=t.lineY1;return t.angle=t.x,delete t.x,t.startAngle=t.x0,delete t.x0,t.endAngle=t.x1,delete t.x1,t.radius=t.y,delete t.y,t.innerRadius=t.y0,delete t.y0,t.outerRadius=t.y1,delete t.y1,t.lineStartAngle=function(){return Ey(e())},delete t.lineX0,t.lineEndAngle=function(){return Ey(r())},delete t.lineX1,t.lineInnerRadius=function(){return Ey(i())},delete t.lineY0,t.lineOuterRadius=function(){return Ey(o())},delete t.lineY1,t.curve=function(t){return arguments.length?n(ky(t)):n()._curve},t}function zy(t,n){return[(n=+n)*Math.cos(t-=Math.PI/2),n*Math.sin(t)]}Sy.prototype={areaStart:function(){this._curve.areaStart()},areaEnd:function(){this._curve.areaEnd()},lineStart:function(){this._curve.lineStart()},lineEnd:function(){this._curve.lineEnd()},point:function(t,n){this._curve.point(n*Math.sin(t),n*-Math.cos(t))}};var Ry=Array.prototype.slice;function Dy(t){return t.source}function qy(t){return t.target}function Ly(t){var n=Dy,e=qy,r=my,i=xy,o=null;function a(){var a,u=Ry.call(arguments),c=n.apply(this,u),f=e.apply(this,u);if(o||(o=a=Hi()),t(o,+r.apply(this,(u[0]=c,u)),+i.apply(this,u),+r.apply(this,(u[0]=f,u)),+i.apply(this,u)),a)return o=null,a+""||null}return a.source=function(t){return arguments.length?(n=t,a):n},a.target=function(t){return arguments.length?(e=t,a):e},a.x=function(t){return arguments.length?(r="function"==typeof t?t:Kg(+t),a):r},a.y=function(t){return arguments.length?(i="function"==typeof t?t:Kg(+t),a):i},a.context=function(t){return arguments.length?(o=null==t?null:t,a):o},a}function Uy(t,n,e,r,i){t.moveTo(n,e),t.bezierCurveTo(n=(n+r)/2,e,n,i,r,i)}function Oy(t,n,e,r,i){t.moveTo(n,e),t.bezierCurveTo(n,e=(e+i)/2,r,e,r,i)}function By(t,n,e,r,i){var o=zy(n,e),a=zy(n,e=(e+i)/2),u=zy(r,e),c=zy(r,i);t.moveTo(o[0],o[1]),t.bezierCurveTo(a[0],a[1],u[0],u[1],c[0],c[1])}var Yy={draw:function(t,n){var e=Math.sqrt(n/cy);t.moveTo(e,0),t.arc(0,0,e,0,sy)}},Fy={draw:function(t,n){var e=Math.sqrt(n/5)/2;t.moveTo(-3*e,-e),t.lineTo(-e,-e),t.lineTo(-e,-3*e),t.lineTo(e,-3*e),t.lineTo(e,-e),t.lineTo(3*e,-e),t.lineTo(3*e,e),t.lineTo(e,e),t.lineTo(e,3*e),t.lineTo(-e,3*e),t.lineTo(-e,e),t.lineTo(-3*e,e),t.closePath()}},Iy=Math.sqrt(1/3),jy=2*Iy,Hy={draw:function(t,n){var e=Math.sqrt(n/jy),r=e*Iy;t.moveTo(0,-e),t.lineTo(r,0),t.lineTo(0,e),t.lineTo(-r,0),t.closePath()}},Xy=Math.sin(cy/10)/Math.sin(7*cy/10),Gy=Math.sin(sy/10)*Xy,Vy=-Math.cos(sy/10)*Xy,$y={draw:function(t,n){var e=Math.sqrt(.8908130915292852*n),r=Gy*e,i=Vy*e;t.moveTo(0,-e),t.lineTo(r,i);for(var o=1;o<5;++o){var a=sy*o/5,u=Math.cos(a),c=Math.sin(a);t.lineTo(c*e,-u*e),t.lineTo(u*r-c*i,c*r+u*i)}t.closePath()}},Wy={draw:function(t,n){var e=Math.sqrt(n),r=-e/2;t.rect(r,r,e,e)}},Zy=Math.sqrt(3),Qy={draw:function(t,n){var e=-Math.sqrt(n/(3*Zy));t.moveTo(0,2*e),t.lineTo(-Zy*e,-e),t.lineTo(Zy*e,-e),t.closePath()}},Jy=Math.sqrt(3)/2,Ky=1/Math.sqrt(12),t_=3*(Ky/2+1),n_={draw:function(t,n){var e=Math.sqrt(n/t_),r=e/2,i=e*Ky,o=r,a=e*Ky+e,u=-o,c=a;t.moveTo(r,i),t.lineTo(o,a),t.lineTo(u,c),t.lineTo(-.5*r-Jy*i,Jy*r+-.5*i),t.lineTo(-.5*o-Jy*a,Jy*o+-.5*a),t.lineTo(-.5*u-Jy*c,Jy*u+-.5*c),t.lineTo(-.5*r+Jy*i,-.5*i-Jy*r),t.lineTo(-.5*o+Jy*a,-.5*a-Jy*o),t.lineTo(-.5*u+Jy*c,-.5*c-Jy*u),t.closePath()}},e_=[Yy,Fy,Hy,Wy,$y,Qy,n_];function r_(){}function i_(t,n,e){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+n)/6,(t._y0+4*t._y1+e)/6)}function o_(t){this._context=t}function a_(t){this._context=t}function u_(t){this._context=t}function c_(t,n){this._basis=new o_(t),this._beta=n}o_.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:i_(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:i_(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},a_.prototype={areaStart:r_,areaEnd:r_,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x2,this._y2),this._context.closePath();break;case 2:this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break;case 3:this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x2=t,this._y2=n;break;case 1:this._point=2,this._x3=t,this._y3=n;break;case 2:this._point=3,this._x4=t,this._y4=n,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+n)/6);break;default:i_(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},u_.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var e=(this._x0+4*this._x1+t)/6,r=(this._y0+4*this._y1+n)/6;this._line?this._context.lineTo(e,r):this._context.moveTo(e,r);break;case 3:this._point=4;default:i_(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},c_.prototype={lineStart:function(){this._x=[],this._y=[],this._basis.lineStart()},lineEnd:function(){var t=this._x,n=this._y,e=t.length-1;if(e>0)for(var r,i=t[0],o=n[0],a=t[e]-i,u=n[e]-o,c=-1;++c<=e;)r=c/e,this._basis.point(this._beta*t[c]+(1-this._beta)*(i+r*a),this._beta*n[c]+(1-this._beta)*(o+r*u));this._x=this._y=null,this._basis.lineEnd()},point:function(t,n){this._x.push(+t),this._y.push(+n)}};var f_=function t(n){function e(t){return 1===n?new o_(t):new c_(t,n)}return e.beta=function(n){return t(+n)},e}(.85);function s_(t,n,e){t._context.bezierCurveTo(t._x1+t._k*(t._x2-t._x0),t._y1+t._k*(t._y2-t._y0),t._x2+t._k*(t._x1-n),t._y2+t._k*(t._y1-e),t._x2,t._y2)}function l_(t,n){this._context=t,this._k=(1-n)/6}l_.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:s_(this,this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2,this._x1=t,this._y1=n;break;case 2:this._point=3;default:s_(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var h_=function t(n){function e(t){return new l_(t,n)}return e.tension=function(n){return t(+n)},e}(0);function d_(t,n){this._context=t,this._k=(1-n)/6}d_.prototype={areaStart:r_,areaEnd:r_,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:s_(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var p_=function t(n){function e(t){return new d_(t,n)}return e.tension=function(n){return t(+n)},e}(0);function v_(t,n){this._context=t,this._k=(1-n)/6}v_.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:s_(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var g_=function t(n){function e(t){return new v_(t,n)}return e.tension=function(n){return t(+n)},e}(0);function y_(t,n,e){var r=t._x1,i=t._y1,o=t._x2,a=t._y2;if(t._l01_a>uy){var u=2*t._l01_2a+3*t._l01_a*t._l12_a+t._l12_2a,c=3*t._l01_a*(t._l01_a+t._l12_a);r=(r*u-t._x0*t._l12_2a+t._x2*t._l01_2a)/c,i=(i*u-t._y0*t._l12_2a+t._y2*t._l01_2a)/c}if(t._l23_a>uy){var f=2*t._l23_2a+3*t._l23_a*t._l12_a+t._l12_2a,s=3*t._l23_a*(t._l23_a+t._l12_a);o=(o*f+t._x1*t._l23_2a-n*t._l12_2a)/s,a=(a*f+t._y1*t._l23_2a-e*t._l12_2a)/s}t._context.bezierCurveTo(r,i,o,a,t._x2,t._y2)}function __(t,n){this._context=t,this._alpha=n}__.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3;default:y_(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var b_=function t(n){function e(t){return n?new __(t,n):new l_(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);function m_(t,n){this._context=t,this._alpha=n}m_.prototype={areaStart:r_,areaEnd:r_,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:y_(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var x_=function t(n){function e(t){return n?new m_(t,n):new d_(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);function w_(t,n){this._context=t,this._alpha=n}w_.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:y_(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var M_=function t(n){function e(t){return n?new w_(t,n):new v_(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);function N_(t){this._context=t}function A_(t){return t<0?-1:1}function T_(t,n,e){var r=t._x1-t._x0,i=n-t._x1,o=(t._y1-t._y0)/(r||i<0&&-0),a=(e-t._y1)/(i||r<0&&-0),u=(o*i+a*r)/(r+i);return(A_(o)+A_(a))*Math.min(Math.abs(o),Math.abs(a),.5*Math.abs(u))||0}function S_(t,n){var e=t._x1-t._x0;return e?(3*(t._y1-t._y0)/e-n)/2:n}function k_(t,n,e){var r=t._x0,i=t._y0,o=t._x1,a=t._y1,u=(o-r)/3;t._context.bezierCurveTo(r+u,i+u*n,o-u,a-u*e,o,a)}function E_(t){this._context=t}function C_(t){this._context=new P_(t)}function P_(t){this._context=t}function z_(t){this._context=t}function R_(t){var n,e,r=t.length-1,i=new Array(r),o=new Array(r),a=new Array(r);for(i[0]=0,o[0]=2,a[0]=t[0]+2*t[1],n=1;n=0;--n)i[n]=(a[n]-i[n+1])/o[n];for(o[r-1]=(t[r]+i[r-1])/2,n=0;n1)for(var e,r,i,o=1,a=t[n[0]],u=a.length;o=0;)e[n]=n;return e}function U_(t,n){return t[n]}function O_(t){var n=t.map(B_);return L_(t).sort(function(t,e){return n[t]-n[e]})}function B_(t){for(var n,e=-1,r=0,i=t.length,o=-1/0;++eo&&(o=n,r=e);return r}function Y_(t){var n=t.map(F_);return L_(t).sort(function(t,e){return n[t]-n[e]})}function F_(t){for(var n,e=0,r=-1,i=t.length;++r0)){if(o/=h,h<0){if(o0){if(o>l)return;o>s&&(s=o)}if(o=r-c,h||!(o<0)){if(o/=h,h<0){if(o>l)return;o>s&&(s=o)}else if(h>0){if(o0)){if(o/=d,d<0){if(o0){if(o>l)return;o>s&&(s=o)}if(o=i-f,d||!(o<0)){if(o/=d,d<0){if(o>l)return;o>s&&(s=o)}else if(d>0){if(o0||l<1)||(s>0&&(t[0]=[c+s*h,f+s*d]),l<1&&(t[1]=[c+l*h,f+l*d]),!0)}}}}}function tb(t,n,e,r,i){var o=t[1];if(o)return!0;var a,u,c=t[0],f=t.left,s=t.right,l=f[0],h=f[1],d=s[0],p=s[1],v=(l+d)/2,g=(h+p)/2;if(p===h){if(v=r)return;if(l>d){if(c){if(c[1]>=i)return}else c=[v,e];o=[v,i]}else{if(c){if(c[1]1)if(l>d){if(c){if(c[1]>=i)return}else c=[(e-u)/a,e];o=[(i-u)/a,i]}else{if(c){if(c[1]=r)return}else c=[n,a*n+u];o=[r,a*r+u]}else{if(c){if(c[0]=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:if(this._t<=0)this._context.lineTo(this._x,n),this._context.lineTo(t,n);else{var e=this._x*(1-this._t)+t*this._t;this._context.lineTo(e,this._y),this._context.lineTo(e,n)}}this._x=t,this._y=n}},X_.prototype={constructor:X_,insert:function(t,n){var e,r,i;if(t){if(n.P=t,n.N=t.N,t.N&&(t.N.P=n),t.N=n,t.R){for(t=t.R;t.L;)t=t.L;t.L=n}else t.R=n;e=t}else this._?(t=W_(this._),n.P=null,n.N=t,t.P=t.L=n,e=t):(n.P=n.N=null,this._=n,e=null);for(n.L=n.R=null,n.U=e,n.C=!0,t=n;e&&e.C;)e===(r=e.U).L?(i=r.R)&&i.C?(e.C=i.C=!1,r.C=!0,t=r):(t===e.R&&(V_(this,e),e=(t=e).U),e.C=!1,r.C=!0,$_(this,r)):(i=r.L)&&i.C?(e.C=i.C=!1,r.C=!0,t=r):(t===e.L&&($_(this,e),e=(t=e).U),e.C=!1,r.C=!0,V_(this,r)),e=t.U;this._.C=!1},remove:function(t){t.N&&(t.N.P=t.P),t.P&&(t.P.N=t.N),t.N=t.P=null;var n,e,r,i=t.U,o=t.L,a=t.R;if(e=o?a?W_(a):o:a,i?i.L===t?i.L=e:i.R=e:this._=e,o&&a?(r=e.C,e.C=t.C,e.L=o,o.U=e,e!==a?(i=e.U,e.U=t.U,t=e.R,i.L=t,e.R=a,a.U=e):(e.U=i,i=e,t=e.R)):(r=t.C,t=e),t&&(t.U=i),!r)if(t&&t.C)t.C=!1;else{do{if(t===this._)break;if(t===i.L){if((n=i.R).C&&(n.C=!1,i.C=!0,V_(this,i),n=i.R),n.L&&n.L.C||n.R&&n.R.C){n.R&&n.R.C||(n.L.C=!1,n.C=!0,$_(this,n),n=i.R),n.C=i.C,i.C=n.R.C=!1,V_(this,i),t=this._;break}}else if((n=i.L).C&&(n.C=!1,i.C=!0,$_(this,i),n=i.L),n.L&&n.L.C||n.R&&n.R.C){n.L&&n.L.C||(n.R.C=!1,n.C=!0,V_(this,n),n=i.L),n.C=i.C,i.C=n.L.C=!1,$_(this,i),t=this._;break}n.C=!0,t=i,i=i.U}while(!t.C);t&&(t.C=!1)}}};var ib,ob=[];function ab(){G_(this),this.x=this.y=this.arc=this.site=this.cy=null}function ub(t){var n=t.P,e=t.N;if(n&&e){var r=n.site,i=t.site,o=e.site;if(r!==o){var a=i[0],u=i[1],c=r[0]-a,f=r[1]-u,s=o[0]-a,l=o[1]-u,h=2*(c*l-f*s);if(!(h>=-wb)){var d=c*c+f*f,p=s*s+l*l,v=(l*d-f*p)/h,g=(c*p-s*d)/h,y=ob.pop()||new ab;y.arc=t,y.site=i,y.x=v+a,y.y=(y.cy=g+u)+Math.sqrt(v*v+g*g),t.circle=y;for(var _=null,b=bb._;b;)if(y.yxb)u=u.L;else{if(!((i=o-gb(u,a))>xb)){r>-xb?(n=u.P,e=u):i>-xb?(n=u,e=u.N):n=e=u;break}if(!u.R){n=u;break}u=u.R}!function(t){_b[t.index]={site:t,halfedges:[]}}(t);var c=lb(t);if(yb.insert(n,c),n||e){if(n===e)return cb(n),e=lb(n.site),yb.insert(c,e),c.edge=e.edge=Z_(n.site,c.site),ub(n),void ub(e);if(e){cb(n),cb(e);var f=n.site,s=f[0],l=f[1],h=t[0]-s,d=t[1]-l,p=e.site,v=p[0]-s,g=p[1]-l,y=2*(h*g-d*v),_=h*h+d*d,b=v*v+g*g,m=[(g*_-d*b)/y+s,(h*b-v*_)/y+l];J_(e.edge,f,p,m),c.edge=Z_(f,t,null,m),e.edge=Z_(t,p,null,m),ub(n),ub(e)}else c.edge=Z_(n.site,c.site)}}function vb(t,n){var e=t.site,r=e[0],i=e[1],o=i-n;if(!o)return r;var a=t.P;if(!a)return-1/0;var u=(e=a.site)[0],c=e[1],f=c-n;if(!f)return u;var s=u-r,l=1/o-1/f,h=s/f;return l?(-h+Math.sqrt(h*h-2*l*(s*s/(-2*f)-c+f/2+i-o/2)))/l+r:(r+u)/2}function gb(t,n){var e=t.N;if(e)return vb(e,n);var r=t.site;return r[1]===n?r[0]:1/0}var yb,_b,bb,mb,xb=1e-6,wb=1e-12;function Mb(t,n){return n[1]-t[1]||n[0]-t[0]}function Nb(t,n){var e,r,i,o=t.sort(Mb).pop();for(mb=[],_b=new Array(t.length),yb=new X_,bb=new X_;;)if(i=ib,o&&(!i||o[1]xb||Math.abs(i[0][1]-i[1][1])>xb)||delete mb[o]}(a,u,c,f),function(t,n,e,r){var i,o,a,u,c,f,s,l,h,d,p,v,g=_b.length,y=!0;for(i=0;ixb||Math.abs(v-h)>xb)&&(c.splice(u,0,mb.push(Q_(a,d,Math.abs(p-t)xb?[t,Math.abs(l-t)xb?[Math.abs(h-r)xb?[e,Math.abs(l-e)xb?[Math.abs(h-n)=u)return null;var c=t-i.site[0],f=n-i.site[1],s=c*c+f*f;do{i=o.cells[r=a],a=null,i.halfedges.forEach(function(e){var r=o.edges[e],u=r.left;if(u!==i.site&&u||(u=r.right)){var c=t-u[0],f=n-u[1],l=c*c+f*f;lr?(r+i)/2:Math.min(0,r)||Math.max(0,i),a>o?(o+a)/2:Math.min(0,o)||Math.max(0,a))}Eb.prototype=Sb.prototype,t.version="5.9.2",t.bisect=i,t.bisectRight=i,t.bisectLeft=o,t.ascending=n,t.bisector=e,t.cross=function(t,n,e){var r,i,o,u,c=t.length,f=n.length,s=new Array(c*f);for(null==e&&(e=a),r=o=0;rt?1:n>=t?0:NaN},t.deviation=f,t.extent=s,t.histogram=function(){var t=v,n=s,e=M;function r(r){var o,a,u=r.length,c=new Array(u);for(o=0;ol;)h.pop(),--d;var p,v=new Array(d+1);for(o=0;o<=d;++o)(p=v[o]=[]).x0=o>0?h[o-1]:s,p.x1=o=r.length)return null!=t&&e.sort(t),null!=n?n(e):e;for(var c,f,s,l=-1,h=e.length,d=r[i++],p=Qi(),v=a();++lr.length)return e;var a,u=i[o-1];return null!=n&&o>=r.length?a=e.entries():(a=[],e.each(function(n,e){a.push({key:e,values:t(n,o)})})),null!=u?a.sort(function(t,n){return u(t.key,n.key)}):a}(o(t,0,to,no),0)},key:function(t){return r.push(t),e},sortKeys:function(t){return i[r.length-1]=t,e},sortValues:function(n){return t=n,e},rollup:function(t){return n=t,e}}},t.set=io,t.map=Qi,t.keys=function(t){var n=[];for(var e in t)n.push(e);return n},t.values=function(t){var n=[];for(var e in t)n.push(t[e]);return n},t.entries=function(t){var n=[];for(var e in t)n.push({key:e,value:t[e]});return n},t.color=hn,t.rgb=gn,t.hsl=mn,t.lab=Rn,t.hcl=Yn,t.lch=function(t,n,e,r){return 1===arguments.length?Bn(t):new Fn(e,n,t,null==r?1:r)},t.gray=function(t,n){return new Dn(t,0,0,null==n?1:n)},t.cubehelix=Zn,t.contours=po,t.contourDensity=function(){var t=yo,n=_o,e=bo,r=960,i=500,o=20,a=2,u=3*o,c=r+2*u>>a,f=i+2*u>>a,s=uo(20);function l(r){var i=new Float32Array(c*f),l=new Float32Array(c*f);r.forEach(function(r,o,s){var l=+t(r,o,s)+u>>a,h=+n(r,o,s)+u>>a,d=+e(r,o,s);l>=0&&l=0&&h>a),go({width:c,height:f,data:l},{width:c,height:f,data:i},o>>a),vo({width:c,height:f,data:i},{width:c,height:f,data:l},o>>a),go({width:c,height:f,data:l},{width:c,height:f,data:i},o>>a),vo({width:c,height:f,data:i},{width:c,height:f,data:l},o>>a),go({width:c,height:f,data:l},{width:c,height:f,data:i},o>>a);var d=s(i);if(!Array.isArray(d)){var p=A(i);d=w(0,p,d),(d=g(0,Math.floor(p/d)*d,d)).shift()}return po().thresholds(d).size([c,f])(i).map(h)}function h(t){return t.value*=Math.pow(2,-2*a),t.coordinates.forEach(d),t}function d(t){t.forEach(p)}function p(t){t.forEach(v)}function v(t){t[0]=t[0]*Math.pow(2,a)-u,t[1]=t[1]*Math.pow(2,a)-u}function y(){return c=r+2*(u=3*o)>>a,f=i+2*u>>a,l}return l.x=function(n){return arguments.length?(t="function"==typeof n?n:uo(+n),l):t},l.y=function(t){return arguments.length?(n="function"==typeof t?t:uo(+t),l):n},l.weight=function(t){return arguments.length?(e="function"==typeof t?t:uo(+t),l):e},l.size=function(t){if(!arguments.length)return[r,i];var n=Math.ceil(t[0]),e=Math.ceil(t[1]);if(!(n>=0||n>=0))throw new Error("invalid size");return r=n,i=e,y()},l.cellSize=function(t){if(!arguments.length)return 1<=1))throw new Error("invalid cell size");return a=Math.floor(Math.log(t)/Math.LN2),y()},l.thresholds=function(t){return arguments.length?(s="function"==typeof t?t:Array.isArray(t)?uo(oo.call(t)):uo(t),l):s},l.bandwidth=function(t){if(!arguments.length)return Math.sqrt(o*(o+1));if(!((t=+t)>=0))throw new Error("invalid bandwidth");return o=Math.round((Math.sqrt(4*t*t+1)-1)/2),y()},l},t.dispatch=I,t.drag=function(){var n,e,r,i,o=Gt,a=Vt,u=$t,c=Wt,f={},s=I("start","drag","end"),l=0,h=0;function d(t){t.on("mousedown.drag",p).filter(c).on("touchstart.drag",y).on("touchmove.drag",_).on("touchend.drag touchcancel.drag",b).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function p(){if(!i&&o.apply(this,arguments)){var u=m("mouse",a.apply(this,arguments),Ot,this,arguments);u&&(zt(t.event.view).on("mousemove.drag",v,!0).on("mouseup.drag",g,!0),It(t.event.view),Yt(),r=!1,n=t.event.clientX,e=t.event.clientY,u("start"))}}function v(){if(Ft(),!r){var i=t.event.clientX-n,o=t.event.clientY-e;r=i*i+o*o>h}f.mouse("drag")}function g(){zt(t.event.view).on("mousemove.drag mouseup.drag",null),jt(t.event.view,r),Ft(),f.mouse("end")}function y(){if(o.apply(this,arguments)){var n,e,r=t.event.changedTouches,i=a.apply(this,arguments),u=r.length;for(n=0;nc+d||if+d||ou.index){var p=c-a.x-a.vx,v=f-a.y-a.vy,g=p*p+v*v;gt.r&&(t.r=t[n].r)}function u(){if(n){var r,i,o=n.length;for(e=new Array(o),r=0;r=a)){(t.data!==n||t.next)&&(0===s&&(d+=(s=na())*s),0===l&&(d+=(l=na())*l),d1?(null==e?u.remove(t):u.set(t,d(e)),n):u.get(t)},find:function(n,e,r){var i,o,a,u,c,f=0,s=t.length;for(null==r?r=1/0:r*=r,f=0;f1?(f.on(t,e),n):f.on(t)}}},t.forceX=function(t){var n,e,r,i=ta(.1);function o(t){for(var i,o=0,a=n.length;opc(r[0],r[1])&&(r[1]=i[1]),pc(i[0],r[1])>pc(r[0],r[1])&&(r[0]=i[0])):o.push(r=i);for(a=-1/0,n=0,r=o[e=o.length-1];n<=e;r=i,++n)i=o[n],(u=pc(r[1],i[0]))>a&&(a=u,Ru=i[0],qu=r[1])}return Fu=Iu=null,Ru===1/0||Du===1/0?[[NaN,NaN],[NaN,NaN]]:[[Ru,Du],[qu,Lu]]},t.geoCentroid=function(t){ju=Hu=Xu=Gu=Vu=$u=Wu=Zu=Qu=Ju=Ku=0,du(t,yc);var n=Qu,e=Ju,r=Ku,i=n*n+e*e+r*r;return i=.12&&i<.234&&r>=-.425&&r<-.214?u:i>=.166&&i<.234&&r>=-.214&&r<-.115?c:a).invert(t)},s.stream=function(e){return t&&n===e?t:(r=[a.stream(n=e),u.stream(e),c.stream(e)],i=r.length,t={point:function(t,n){for(var e=-1;++e2?t[2]+90:90]):[(t=e())[0],t[1],t[2]-90]},e([0,0,90]).scale(159.155)},t.geoTransverseMercatorRaw=al,t.geoRotation=qc,t.geoStream=du,t.geoTransform=function(t){return{stream:xs(t)}},t.cluster=function(){var t=ul,n=1,e=1,r=!1;function i(i){var o,a=0;i.eachAfter(function(n){var e=n.children;e?(n.x=function(t){return t.reduce(cl,0)/t.length}(e),n.y=function(t){return 1+t.reduce(fl,0)}(e)):(n.x=o?a+=t(n,o):0,n.y=0,o=n)});var u=function(t){for(var n;n=t.children;)t=n[0];return t}(i),c=function(t){for(var n;n=t.children;)t=n[n.length-1];return t}(i),f=u.x-t(u,c)/2,s=c.x+t(c,u)/2;return i.eachAfter(r?function(t){t.x=(t.x-i.x)*n,t.y=(i.y-t.y)*e}:function(t){t.x=(t.x-f)/(s-f)*n,t.y=(1-(i.y?t.y/i.y:1))*e})}return i.separation=function(n){return arguments.length?(t=n,i):t},i.size=function(t){return arguments.length?(r=!1,n=+t[0],e=+t[1],i):r?null:[n,e]},i.nodeSize=function(t){return arguments.length?(r=!0,n=+t[0],e=+t[1],i):r?[n,e]:null},i},t.hierarchy=ll,t.pack=function(){var t=null,n=1,e=1,r=Pl;function i(i){return i.x=n/2,i.y=e/2,t?i.eachBefore(Dl(t)).eachAfter(ql(r,.5)).eachBefore(Ll(1)):i.eachBefore(Dl(Rl)).eachAfter(ql(Pl,1)).eachAfter(ql(r,i.r/Math.min(n,e))).eachBefore(Ll(Math.min(n,e)/(2*i.r))),i}return i.radius=function(n){return arguments.length?(t=null==(e=n)?null:Cl(e),i):t;var e},i.size=function(t){return arguments.length?(n=+t[0],e=+t[1],i):[n,e]},i.padding=function(t){return arguments.length?(r="function"==typeof t?t:zl(+t),i):r},i},t.packSiblings=function(t){return El(t),t},t.packEnclose=yl,t.partition=function(){var t=1,n=1,e=0,r=!1;function i(i){var o=i.height+1;return i.x0=i.y0=e,i.x1=t,i.y1=n/o,i.eachBefore(function(t,n){return function(r){r.children&&Ol(r,r.x0,t*(r.depth+1)/n,r.x1,t*(r.depth+2)/n);var i=r.x0,o=r.y0,a=r.x1-e,u=r.y1-e;a0)throw new Error("cycle");return o}return e.id=function(n){return arguments.length?(t=Cl(n),e):t},e.parentId=function(t){return arguments.length?(n=Cl(t),e):n},e},t.tree=function(){var t=Hl,n=1,e=1,r=null;function i(i){var c=function(t){for(var n,e,r,i,o,a=new Wl(t,0),u=[a];n=u.pop();)if(r=n._.children)for(n.children=new Array(o=r.length),i=o-1;i>=0;--i)u.push(e=n.children[i]=new Wl(r[i],i)),e.parent=n;return(a.parent=new Wl(null,0)).children=[a],a}(i);if(c.eachAfter(o),c.parent.m=-c.z,c.eachBefore(a),r)i.eachBefore(u);else{var f=i,s=i,l=i;i.eachBefore(function(t){t.xs.x&&(s=t),t.depth>l.depth&&(l=t)});var h=f===s?1:t(f,s)/2,d=h-f.x,p=n/(s.x+h+d),v=e/(l.depth||1);i.eachBefore(function(t){t.x=(t.x+d)*p,t.y=t.depth*v})}return i}function o(n){var e=n.children,r=n.parent.children,i=n.i?r[n.i-1]:null;if(e){!function(t){for(var n,e=0,r=0,i=t.children,o=i.length;--o>=0;)(n=i[o]).z+=e,n.m+=e,e+=n.s+(r+=n.c)}(n);var o=(e[0].z+e[e.length-1].z)/2;i?(n.z=i.z+t(n._,i._),n.m=n.z-o):n.z=o}else i&&(n.z=i.z+t(n._,i._));n.parent.A=function(n,e,r){if(e){for(var i,o=n,a=n,u=e,c=o.parent.children[0],f=o.m,s=a.m,l=u.m,h=c.m;u=Gl(u),o=Xl(o),u&&o;)c=Xl(c),(a=Gl(a)).a=n,(i=u.z+l-o.z-f+t(u._,o._))>0&&(Vl($l(u,n,r),n,i),f+=i,s+=i),l+=u.m,f+=o.m,h+=c.m,s+=a.m;u&&!Gl(a)&&(a.t=u,a.m+=l-s),o&&!Xl(c)&&(c.t=o,c.m+=f-h,r=n)}return r}(n,i,n.parent.A||r[0])}function a(t){t._.x=t.z+t.parent.m,t.m+=t.parent.m}function u(t){t.x*=n,t.y=t.depth*e}return i.separation=function(n){return arguments.length?(t=n,i):t},i.size=function(t){return arguments.length?(r=!1,n=+t[0],e=+t[1],i):r?null:[n,e]},i.nodeSize=function(t){return arguments.length?(r=!0,n=+t[0],e=+t[1],i):r?[n,e]:null},i},t.treemap=function(){var t=Kl,n=!1,e=1,r=1,i=[0],o=Pl,a=Pl,u=Pl,c=Pl,f=Pl;function s(t){return t.x0=t.y0=0,t.x1=e,t.y1=r,t.eachBefore(l),i=[0],n&&t.eachBefore(Ul),t}function l(n){var e=i[n.depth],r=n.x0+e,s=n.y0+e,l=n.x1-e,h=n.y1-e;l=e-1){var s=u[n];return s.x0=i,s.y0=o,s.x1=a,void(s.y1=c)}for(var l=f[n],h=r/2+l,d=n+1,p=e-1;d>>1;f[v]c-o){var _=(i*y+a*g)/r;t(n,d,g,i,o,_,c),t(d,e,y,_,o,a,c)}else{var b=(o*y+c*g)/r;t(n,d,g,i,o,a,b),t(d,e,y,i,b,a,c)}}(0,c,t.value,n,e,r,i)},t.treemapDice=Ol,t.treemapSlice=Zl,t.treemapSliceDice=function(t,n,e,r,i){(1&t.depth?Zl:Ol)(t,n,e,r,i)},t.treemapSquarify=Kl,t.treemapResquarify=th,t.interpolate=ye,t.interpolateArray=se,t.interpolateBasis=Kn,t.interpolateBasisClosed=te,t.interpolateDate=le,t.interpolateDiscrete=function(t){var n=t.length;return function(e){return t[Math.max(0,Math.min(n-1,Math.floor(e*n)))]}},t.interpolateHue=function(t,n){var e=re(+t,+n);return function(t){var n=e(t);return n-360*Math.floor(n/360)}},t.interpolateNumber=he,t.interpolateObject=de,t.interpolateRound=_e,t.interpolateString=ge,t.interpolateTransformCss=Se,t.interpolateTransformSvg=ke,t.interpolateZoom=De,t.interpolateRgb=ae,t.interpolateRgbBasis=ce,t.interpolateRgbBasisClosed=fe,t.interpolateHsl=Le,t.interpolateHslLong=Ue,t.interpolateLab=function(t,n){var e=oe((t=Rn(t)).l,(n=Rn(n)).l),r=oe(t.a,n.a),i=oe(t.b,n.b),o=oe(t.opacity,n.opacity);return function(n){return t.l=e(n),t.a=r(n),t.b=i(n),t.opacity=o(n),t+""}},t.interpolateHcl=Be,t.interpolateHclLong=Ye,t.interpolateCubehelix=Ie,t.interpolateCubehelixLong=je,t.piecewise=function(t,n){for(var e=0,r=n.length-1,i=n[0],o=new Array(r<0?0:r);e=0;--n)f.push(t[r[o[n]][2]]);for(n=+u;nu!=f>u&&a<(c-e)*(u-r)/(f-r)+e&&(s=!s),c=e,f=r;return s},t.polygonLength=function(t){for(var n,e,r=-1,i=t.length,o=t[i-1],a=o[0],u=o[1],c=0;++r0?a[n-1]:r[0],n=o?[a[o-1],r]:[a[n-1],a[n]]},c.unknown=function(t){return arguments.length?(n=t,c):c},c.thresholds=function(){return a.slice()},c.copy=function(){return t().domain([e,r]).range(u).unknown(n)},sh.apply(Eh(c),arguments)},t.scaleThreshold=function t(){var n,e=[.5],r=[0,1],o=1;function a(t){return t<=t?r[i(e,t,0,o)]:n}return a.domain=function(t){return arguments.length?(e=ph.call(t),o=Math.min(e.length,r.length-1),a):e.slice()},a.range=function(t){return arguments.length?(r=ph.call(t),o=Math.min(e.length,r.length-1),a):r.slice()},a.invertExtent=function(t){var n=r.indexOf(t);return[e[n-1],e[n]]},a.unknown=function(t){return arguments.length?(n=t,a):n},a.copy=function(){return t().domain(e).range(r).unknown(n)},sh.apply(a,arguments)},t.scaleTime=function(){return sh.apply(Nv(Nd,wd,cd,od,rd,nd,Kh,Wh,t.timeFormat).domain([new Date(2e3,0,1),new Date(2e3,0,2)]),arguments)},t.scaleUtc=function(){return sh.apply(Nv(Wd,Vd,Rd,Cd,kd,Td,Kh,Wh,t.utcFormat).domain([Date.UTC(2e3,0,1),Date.UTC(2e3,0,2)]),arguments)},t.scaleSequential=function t(){var n=Eh(Av()(mh));return n.copy=function(){return Tv(n,t())},lh.apply(n,arguments)},t.scaleSequentialLog=function t(){var n=Uh(Av()).domain([1,10]);return n.copy=function(){return Tv(n,t()).base(n.base())},lh.apply(n,arguments)},t.scaleSequentialPow=Sv,t.scaleSequentialSqrt=function(){return Sv.apply(null,arguments).exponent(.5)},t.scaleSequentialSymlog=function t(){var n=Yh(Av());return n.copy=function(){return Tv(n,t()).constant(n.constant())},lh.apply(n,arguments)},t.scaleSequentialQuantile=function t(){var e=[],r=mh;function o(t){if(!isNaN(t=+t))return r((i(e,t)-1)/(e.length-1))}return o.domain=function(t){if(!arguments.length)return e.slice();e=[];for(var r,i=0,a=t.length;i1)&&(t-=Math.floor(t));var n=Math.abs(t-.5);return Hg.h=360*t-100,Hg.s=1.5-1.5*n,Hg.l=.8-.9*n,Hg+""},t.interpolateWarm=Ig,t.interpolateCool=jg,t.interpolateSinebow=function(t){var n;return t=(.5-t)*Math.PI,Xg.r=255*(n=Math.sin(t))*n,Xg.g=255*(n=Math.sin(t+Gg))*n,Xg.b=255*(n=Math.sin(t+Vg))*n,Xg+""},t.interpolateViridis=Wg,t.interpolateMagma=Zg,t.interpolateInferno=Qg,t.interpolatePlasma=Jg,t.create=function(t){return zt(W(t).call(document.documentElement))},t.creator=W,t.local=Dt,t.matcher=tt,t.mouse=Ot,t.namespace=$,t.namespaces=V,t.clientPoint=Ut,t.select=zt,t.selectAll=function(t){return"string"==typeof t?new Ct([document.querySelectorAll(t)],[document.documentElement]):new Ct([null==t?[]:t],Et)},t.selection=Pt,t.selector=Q,t.selectorAll=K,t.style=ct,t.touch=Bt,t.touches=function(t,n){null==n&&(n=Lt().touches);for(var e=0,r=n?n.length:0,i=new Array(r);ed;if(u||(u=c=Hi()),huy)if(v>sy-uy)u.moveTo(h*ey(d),h*oy(d)),u.arc(0,0,h,d,p,!g),l>uy&&(u.moveTo(l*ey(p),l*oy(p)),u.arc(0,0,l,p,d,g));else{var y,_,b=d,m=p,x=d,w=p,M=v,N=v,A=a.apply(this,arguments)/2,T=A>uy&&(r?+r.apply(this,arguments):ay(l*l+h*h)),S=iy(ty(h-l)/2,+e.apply(this,arguments)),k=S,E=S;if(T>uy){var C=ly(T/l*oy(A)),P=ly(T/h*oy(A));(M-=2*C)>uy?(x+=C*=g?1:-1,w-=C):(M=0,x=w=(d+p)/2),(N-=2*P)>uy?(b+=P*=g?1:-1,m-=P):(N=0,b=m=(d+p)/2)}var z=h*ey(b),R=h*oy(b),D=l*ey(w),q=l*oy(w);if(S>uy){var L,U=h*ey(m),O=h*oy(m),B=l*ey(x),Y=l*oy(x);if(v1?0:s<-1?cy:Math.acos(s))/2),G=ay(L[0]*L[0]+L[1]*L[1]);k=iy(S,(l-G)/(X-1)),E=iy(S,(h-G)/(X+1))}}N>uy?E>uy?(y=yy(B,Y,z,R,h,E,g),_=yy(U,O,D,q,h,E,g),u.moveTo(y.cx+y.x01,y.cy+y.y01),Euy&&M>uy?k>uy?(y=yy(D,q,U,O,l,-k,g),_=yy(z,R,B,Y,l,-k,g),u.lineTo(y.cx+y.x01,y.cy+y.y01),k0&&(d+=l);for(null!=n?p.sort(function(t,e){return n(v[t],v[e])}):null!=e&&p.sort(function(t,n){return e(a[t],a[n])}),u=0,f=d?(y-h*b)/d:0;u0?l*f:0)+b,v[c]={data:a[c],index:u,value:l,startAngle:g,endAngle:s,padAngle:_};return v}return a.value=function(n){return arguments.length?(t="function"==typeof n?n:Kg(+n),a):t},a.sortValues=function(t){return arguments.length?(n=t,e=null,a):n},a.sort=function(t){return arguments.length?(e=t,n=null,a):e},a.startAngle=function(t){return arguments.length?(r="function"==typeof t?t:Kg(+t),a):r},a.endAngle=function(t){return arguments.length?(i="function"==typeof t?t:Kg(+t),a):i},a.padAngle=function(t){return arguments.length?(o="function"==typeof t?t:Kg(+t),a):o},a},t.areaRadial=Py,t.radialArea=Py,t.lineRadial=Cy,t.radialLine=Cy,t.pointRadial=zy,t.linkHorizontal=function(){return Ly(Uy)},t.linkVertical=function(){return Ly(Oy)},t.linkRadial=function(){var t=Ly(By);return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t},t.symbol=function(){var t=Kg(Yy),n=Kg(64),e=null;function r(){var r;if(e||(e=r=Hi()),t.apply(this,arguments).draw(e,+n.apply(this,arguments)),r)return e=null,r+""||null}return r.type=function(n){return arguments.length?(t="function"==typeof n?n:Kg(n),r):t},r.size=function(t){return arguments.length?(n="function"==typeof t?t:Kg(+t),r):n},r.context=function(t){return arguments.length?(e=null==t?null:t,r):e},r},t.symbols=e_,t.symbolCircle=Yy,t.symbolCross=Fy,t.symbolDiamond=Hy,t.symbolSquare=Wy,t.symbolStar=$y,t.symbolTriangle=Qy,t.symbolWye=n_,t.curveBasisClosed=function(t){return new a_(t)},t.curveBasisOpen=function(t){return new u_(t)},t.curveBasis=function(t){return new o_(t)},t.curveBundle=f_,t.curveCardinalClosed=p_,t.curveCardinalOpen=g_,t.curveCardinal=h_,t.curveCatmullRomClosed=x_,t.curveCatmullRomOpen=M_,t.curveCatmullRom=b_,t.curveLinearClosed=function(t){return new N_(t)},t.curveLinear=by,t.curveMonotoneX=function(t){return new E_(t)},t.curveMonotoneY=function(t){return new C_(t)},t.curveNatural=function(t){return new z_(t)},t.curveStep=function(t){return new D_(t,.5)},t.curveStepAfter=function(t){return new D_(t,1)},t.curveStepBefore=function(t){return new D_(t,0)},t.stack=function(){var t=Kg([]),n=L_,e=q_,r=U_;function i(i){var o,a,u=t.apply(this,arguments),c=i.length,f=u.length,s=new Array(f);for(o=0;o0){for(var e,r,i,o=0,a=t[0].length;o1)for(var e,r,i,o,a,u,c=0,f=t[n[0]].length;c=0?(r[0]=o,r[1]=o+=i):i<0?(r[1]=a,r[0]=a+=i):r[0]=o},t.stackOffsetNone=q_,t.stackOffsetSilhouette=function(t,n){if((e=t.length)>0){for(var e,r=0,i=t[n[0]],o=i.length;r0&&(r=(e=t[n[0]]).length)>0){for(var e,r,i,o=0,a=1;adr&&e.name===n)return new Er([[t]],fi,n,+r);return null},t.interrupt=Mr,t.voronoi=function(){var t=j_,n=H_,e=null;function r(r){return new Nb(r.map(function(e,i){var o=[Math.round(t(e,i,r)/xb)*xb,Math.round(n(e,i,r)/xb)*xb];return o.index=i,o.data=e,o}),e)}return r.polygons=function(t){return r(t).polygons()},r.links=function(t){return r(t).links()},r.triangles=function(t){return r(t).triangles()},r.x=function(n){return arguments.length?(t="function"==typeof n?n:I_(+n),r):t},r.y=function(t){return arguments.length?(n="function"==typeof t?t:I_(+t),r):n},r.extent=function(t){return arguments.length?(e=null==t?null:[[+t[0][0],+t[0][1]],[+t[1][0],+t[1][1]]],r):e&&[[e[0][0],e[0][1]],[e[1][0],e[1][1]]]},r.size=function(t){return arguments.length?(e=null==t?null:[[0,0],[+t[0],+t[1]]],r):e&&[e[1][0]-e[0][0],e[1][1]-e[0][1]]},r},t.zoom=function(){var n,e,r=zb,i=Rb,o=Ub,a=qb,u=Lb,c=[0,1/0],f=[[-1/0,-1/0],[1/0,1/0]],s=250,l=De,h=[],d=I("start","zoom","end"),p=500,v=150,g=0;function y(t){t.property("__zoom",Db).on("wheel.zoom",N).on("mousedown.zoom",A).on("dblclick.zoom",T).filter(u).on("touchstart.zoom",S).on("touchmove.zoom",k).on("touchend.zoom touchcancel.zoom",E).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function _(t,n){return(n=Math.max(c[0],Math.min(c[1],n)))===t.k?t:new Sb(n,t.x,t.y)}function b(t,n,e){var r=n[0]-e[0]*t.k,i=n[1]-e[1]*t.k;return r===t.x&&i===t.y?t:new Sb(t.k,r,i)}function m(t){return[(+t[0][0]+ +t[1][0])/2,(+t[0][1]+ +t[1][1])/2]}function x(t,n,e){t.on("start.zoom",function(){w(this,arguments).start()}).on("interrupt.zoom end.zoom",function(){w(this,arguments).end()}).tween("zoom",function(){var t=arguments,r=w(this,t),o=i.apply(this,t),a=e||m(o),u=Math.max(o[1][0]-o[0][0],o[1][1]-o[0][1]),c=this.__zoom,f="function"==typeof n?n.apply(this,t):n,s=l(c.invert(a).concat(u/c.k),f.invert(a).concat(u/f.k));return function(t){if(1===t)t=f;else{var n=s(t),e=u/n[2];t=new Sb(e,a[0]-n[0]*e,a[1]-n[1]*e)}r.zoom(null,t)}})}function w(t,n){for(var e,r=0,i=h.length;rg}n.zoom("mouse",o(b(n.that.__zoom,n.mouse[0]=Ot(n.that),n.mouse[1]),n.extent,f))},!0).on("mouseup.zoom",function(){i.on("mousemove.zoom mouseup.zoom",null),jt(t.event.view,n.moved),Pb(),n.end()},!0),a=Ot(this),u=t.event.clientX,c=t.event.clientY;It(t.event.view),Cb(),n.mouse=[a,this.__zoom.invert(a)],Mr(this),n.start()}}function T(){if(r.apply(this,arguments)){var n=this.__zoom,e=Ot(this),a=n.invert(e),u=n.k*(t.event.shiftKey?.5:2),c=o(b(_(n,u),e,a),i.apply(this,arguments),f);Pb(),s>0?zt(this).transition().duration(s).call(x,c,e):zt(this).call(y.transform,c)}}function S(){if(r.apply(this,arguments)){var e,i,o,a,u=w(this,arguments),c=t.event.changedTouches,f=c.length;for(Cb(),i=0;in?1:t>=n?0:NaN}function e(t){let e=t,r=t;function i(t,n,e,i){for(null==e&&(e=0),null==i&&(i=t.length);e>>1;r(t[o],n)<0?e=o+1:i=o}return e}return 1===t.length&&(e=(n,e)=>t(n)-e,r=function(t){return(e,r)=>n(t(e),r)}(t)),{left:i,center:function(t,n,r,o){null==r&&(r=0),null==o&&(o=t.length);const a=i(t,n,r,o-1);return a>r&&e(t[a-1],n)>-e(t[a],n)?a-1:a},right:function(t,n,e,i){for(null==e&&(e=0),null==i&&(i=t.length);e>>1;r(t[o],n)>0?i=o:e=o+1}return e}}}function r(t){return null===t?NaN:+t}const i=e(n),o=i.right,a=i.left,u=e(r).center;function c(t,n){let e=0;if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&++e;else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(i=+i)>=i&&++e}return e}function f(t){return 0|t.length}function s(t){return!(t>0)}function l(t){return"object"!=typeof t||"length"in t?t:Array.from(t)}function h(t,n){let e,r=0,i=0,o=0;if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&(e=n-i,i+=e/++r,o+=e*(n-i));else{let a=-1;for(let u of t)null!=(u=n(u,++a,t))&&(u=+u)>=u&&(e=u-i,i+=e/++r,o+=e*(u-i))}if(r>1)return o/(r-1)}function d(t,n){const e=h(t,n);return e?Math.sqrt(e):e}function p(t,n){let e,r;if(void 0===n)for(const n of t)null!=n&&(void 0===e?n>=n&&(e=r=n):(e>n&&(e=n),r=o&&(e=r=o):(e>o&&(e=o),r0){for(o=t[--i];i>0&&(n=o,e=t[--i],o=n+e,r=e-(o-n),!r););i>0&&(r<0&&t[i-1]<0||r>0&&t[i-1]>0)&&(e=2*r,n=o+e,e==n-o&&(o=n))}return o}}class y extends Map{constructor(t,n=x){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:n}}),null!=t)for(const[n,e]of t)this.set(n,e)}get(t){return super.get(_(this,t))}has(t){return super.has(_(this,t))}set(t,n){return super.set(b(this,t),n)}delete(t){return super.delete(m(this,t))}}class v extends Set{constructor(t,n=x){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:n}}),null!=t)for(const n of t)this.add(n)}has(t){return super.has(_(this,t))}add(t){return super.add(b(this,t))}delete(t){return super.delete(m(this,t))}}function _({_intern:t,_key:n},e){const r=n(e);return t.has(r)?t.get(r):e}function b({_intern:t,_key:n},e){const r=n(e);return t.has(r)?t.get(r):(t.set(r,e),e)}function m({_intern:t,_key:n},e){const r=n(e);return t.has(r)&&(e=t.get(e),t.delete(r)),e}function x(t){return null!==t&&"object"==typeof t?t.valueOf():t}function w(t){return t}function M(t,...n){return S(t,w,w,n)}function A(t,n,...e){return S(t,w,n,e)}function T(t){if(1!==t.length)throw new Error("duplicate key");return t[0]}function S(t,n,e,r){return function t(i,o){if(o>=r.length)return e(i);const a=new y,u=r[o++];let c=-1;for(const t of i){const n=u(t,++c,i),e=a.get(n);e?e.push(t):a.set(n,[t])}for(const[n,e]of a)a.set(n,t(e,o));return n(a)}(t,0)}function E(t,n){return Array.from(n,(n=>t[n]))}function k(t,...e){if("function"!=typeof t[Symbol.iterator])throw new TypeError("values is not iterable");t=Array.from(t);let[r=n]=e;if(1===r.length||e.length>1){const i=Uint32Array.from(t,((t,n)=>n));return e.length>1?(e=e.map((n=>t.map(n))),i.sort(((t,r)=>{for(const i of e){const e=n(i[t],i[r]);if(e)return e}}))):(r=t.map(r),i.sort(((t,e)=>n(r[t],r[e])))),E(t,i)}return t.sort(r)}var N=Array.prototype.slice;function C(t){return function(){return t}}var P=Math.sqrt(50),z=Math.sqrt(10),D=Math.sqrt(2);function q(t,n,e){var r,i,o,a,u=-1;if(e=+e,(t=+t)===(n=+n)&&e>0)return[t];if((r=n0){let e=Math.round(t/a),r=Math.round(n/a);for(e*an&&--r,o=new Array(i=r-e+1);++un&&--r,o=new Array(i=r-e+1);++u=0?(o>=P?10:o>=z?5:o>=D?2:1)*Math.pow(10,i):-Math.pow(10,-i)/(o>=P?10:o>=z?5:o>=D?2:1)}function F(t,n,e){var r=Math.abs(n-t)/Math.max(0,e),i=Math.pow(10,Math.floor(Math.log(r)/Math.LN10)),o=r/i;return o>=P?i*=10:o>=z?i*=5:o>=D&&(i*=2),n0?(t=Math.floor(t/i)*i,n=Math.ceil(n/i)*i):i<0&&(t=Math.ceil(t*i)/i,n=Math.floor(n*i)/i),r=i}}function I(t){return Math.ceil(Math.log(c(t))/Math.LN2)+1}function U(){var t=w,n=p,e=I;function r(r){Array.isArray(r)||(r=Array.from(r));var i,a,u=r.length,c=new Array(u);for(i=0;i=l)if(t>=l&&n===p){const t=R(s,l,e);isFinite(t)&&(t>0?l=(Math.floor(l/t)+1)*t:t<0&&(l=(Math.ceil(l*-t)+1)/-t))}else h.pop()}for(var d=h.length;h[0]<=s;)h.shift(),--d;for(;h[d-1]>l;)h.pop(),--d;var g,y=new Array(d+1);for(i=0;i<=d;++i)(g=y[i]=[]).x0=i>0?h[i-1]:s,g.x1=i=n)&&(e=n);else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(e=i)&&(e=i)}return e}function Y(t,n){let e;if(void 0===n)for(const n of t)null!=n&&(e>n||void 0===e&&n>=n)&&(e=n);else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(e>i||void 0===e&&i>=i)&&(e=i)}return e}function L(t,e,r=0,i=t.length-1,o=n){for(;i>r;){if(i-r>600){const n=i-r+1,a=e-r+1,u=Math.log(n),c=.5*Math.exp(2*u/3),f=.5*Math.sqrt(u*c*(n-c)/n)*(a-n/2<0?-1:1);L(t,e,Math.max(r,Math.floor(e-a*c/n+f)),Math.min(i,Math.floor(e+(n-a)*c/n+f)),o)}const n=t[e];let a=r,u=i;for(j(t,r,e),o(t[i],n)>0&&j(t,r,i);a0;)--u}0===o(t[r],n)?j(t,r,u):(++u,j(t,u,i)),u<=e&&(r=u+1),e<=u&&(i=u-1)}return t}function j(t,n,e){const r=t[n];t[n]=t[e],t[e]=r}function H(t,n,e){if(r=(t=Float64Array.from(function*(t,n){if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&(yield n);else{let e=-1;for(let r of t)null!=(r=n(r,++e,t))&&(r=+r)>=r&&(yield r)}}(t,e))).length){if((n=+n)<=0||r<2)return Y(t);if(n>=1)return B(t);var r,i=(r-1)*n,o=Math.floor(i),a=B(L(t,o).subarray(0,o+1));return a+(Y(t.subarray(o+1))-a)*(i-o)}}function X(t,n,e=r){if(i=t.length){if((n=+n)<=0||i<2)return+e(t[0],0,t);if(n>=1)return+e(t[i-1],i-1,t);var i,o=(i-1)*n,a=Math.floor(o),u=+e(t[a],a,t);return u+(+e(t[a+1],a+1,t)-u)*(o-a)}}function G(t,n){let e,r=-1,i=-1;if(void 0===n)for(const n of t)++i,null!=n&&(e=n)&&(e=n,r=i);else for(let o of t)null!=(o=n(o,++i,t))&&(e=o)&&(e=o,r=i);return r}function V(t){return Array.from(function*(t){for(const n of t)yield*n}(t))}function $(t,n){let e,r=-1,i=-1;if(void 0===n)for(const n of t)++i,null!=n&&(e>n||void 0===e&&n>=n)&&(e=n,r=i);else for(let o of t)null!=(o=n(o,++i,t))&&(e>o||void 0===e&&o>=o)&&(e=o,r=i);return r}function W(t,n){return[t,n]}function Z(t,n,e){t=+t,n=+n,e=(i=arguments.length)<2?(n=t,t=0,1):i<3?1:+e;for(var r=-1,i=0|Math.max(0,Math.ceil((n-t)/e)),o=new Array(i);++r+t(n)}function st(t,n){return n=Math.max(0,t.bandwidth()-2*n)/2,t.round()&&(n=Math.round(n)),e=>+t(e)+n}function lt(){return!this.__axis}function ht(t,n){var e=[],r=null,i=null,o=6,a=6,u=3,c="undefined"!=typeof window&&window.devicePixelRatio>1?0:.5,f=1===t||4===t?-1:1,s=4===t||2===t?"x":"y",l=1===t||3===t?ut:ct;function h(h){var d=null==r?n.ticks?n.ticks.apply(n,e):n.domain():r,p=null==i?n.tickFormat?n.tickFormat.apply(n,e):ot:i,g=Math.max(o,0)+u,y=n.range(),v=+y[0]+c,_=+y[y.length-1]+c,b=(n.bandwidth?st:ft)(n.copy(),c),m=h.selection?h.selection():h,x=m.selectAll(".domain").data([null]),w=m.selectAll(".tick").data(d,n).order(),M=w.exit(),A=w.enter().append("g").attr("class","tick"),T=w.select("line"),S=w.select("text");x=x.merge(x.enter().insert("path",".tick").attr("class","domain").attr("stroke","currentColor")),w=w.merge(A),T=T.merge(A.append("line").attr("stroke","currentColor").attr(s+"2",f*o)),S=S.merge(A.append("text").attr("fill","currentColor").attr(s,f*g).attr("dy",1===t?"0em":3===t?"0.71em":"0.32em")),h!==m&&(x=x.transition(h),w=w.transition(h),T=T.transition(h),S=S.transition(h),M=M.transition(h).attr("opacity",at).attr("transform",(function(t){return isFinite(t=b(t))?l(t+c):this.getAttribute("transform")})),A.attr("opacity",at).attr("transform",(function(t){var n=this.parentNode.__axis;return l((n&&isFinite(n=n(t))?n:b(t))+c)}))),M.remove(),x.attr("d",4===t||2===t?a?"M"+f*a+","+v+"H"+c+"V"+_+"H"+f*a:"M"+c+","+v+"V"+_:a?"M"+v+","+f*a+"V"+c+"H"+_+"V"+f*a:"M"+v+","+c+"H"+_),w.attr("opacity",1).attr("transform",(function(t){return l(b(t)+c)})),T.attr(s+"2",f*o),S.attr(s,f*g).text(p),m.filter(lt).attr("fill","none").attr("font-size",10).attr("font-family","sans-serif").attr("text-anchor",2===t?"start":4===t?"end":"middle"),m.each((function(){this.__axis=b}))}return h.scale=function(t){return arguments.length?(n=t,h):n},h.ticks=function(){return e=it.call(arguments),h},h.tickArguments=function(t){return arguments.length?(e=null==t?[]:it.call(t),h):e.slice()},h.tickValues=function(t){return arguments.length?(r=null==t?null:it.call(t),h):r&&r.slice()},h.tickFormat=function(t){return arguments.length?(i=t,h):i},h.tickSize=function(t){return arguments.length?(o=a=+t,h):o},h.tickSizeInner=function(t){return arguments.length?(o=+t,h):o},h.tickSizeOuter=function(t){return arguments.length?(a=+t,h):a},h.tickPadding=function(t){return arguments.length?(u=+t,h):u},h.offset=function(t){return arguments.length?(c=+t,h):c},h}var dt={value:()=>{}};function pt(){for(var t,n=0,e=arguments.length,r={};n=0&&(e=t.slice(r+1),t=t.slice(0,r)),t&&!n.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:e}}))}function vt(t,n){for(var e,r=0,i=t.length;r0)for(var e,r,i=new Array(e),o=0;o=0&&"xmlns"!==(n=t.slice(0,e))&&(t=t.slice(e+1)),mt.hasOwnProperty(n)?{space:mt[n],local:t}:t}function wt(t){return function(){var n=this.ownerDocument,e=this.namespaceURI;return e===bt&&n.documentElement.namespaceURI===bt?n.createElement(t):n.createElementNS(e,t)}}function Mt(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function At(t){var n=xt(t);return(n.local?Mt:wt)(n)}function Tt(){}function St(t){return null==t?Tt:function(){return this.querySelector(t)}}function Et(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}function kt(){return[]}function Nt(t){return null==t?kt:function(){return this.querySelectorAll(t)}}function Ct(t){return function(){return this.matches(t)}}function Pt(t){return function(n){return n.matches(t)}}var zt=Array.prototype.find;function Dt(){return this.firstElementChild}var qt=Array.prototype.filter;function Rt(){return this.children}function Ft(t){return new Array(t.length)}function Ot(t,n){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=n}function It(t){return function(){return t}}function Ut(t,n,e,r,i,o){for(var a,u=0,c=n.length,f=o.length;un?1:t>=n?0:NaN}function jt(t){return function(){this.removeAttribute(t)}}function Ht(t){return function(){this.removeAttributeNS(t.space,t.local)}}function Xt(t,n){return function(){this.setAttribute(t,n)}}function Gt(t,n){return function(){this.setAttributeNS(t.space,t.local,n)}}function Vt(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttribute(t):this.setAttribute(t,e)}}function $t(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,e)}}function Wt(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function Zt(t){return function(){this.style.removeProperty(t)}}function Kt(t,n,e){return function(){this.style.setProperty(t,n,e)}}function Qt(t,n,e){return function(){var r=n.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,e)}}function Jt(t,n){return t.style.getPropertyValue(n)||Wt(t).getComputedStyle(t,null).getPropertyValue(n)}function tn(t){return function(){delete this[t]}}function nn(t,n){return function(){this[t]=n}}function en(t,n){return function(){var e=n.apply(this,arguments);null==e?delete this[t]:this[t]=e}}function rn(t){return t.trim().split(/^|\s+/)}function on(t){return t.classList||new an(t)}function an(t){this._node=t,this._names=rn(t.getAttribute("class")||"")}function un(t,n){for(var e=on(t),r=-1,i=n.length;++r=0&&(n=t.slice(e+1),t=t.slice(0,e)),{type:t,name:n}}))}function Tn(t){return function(){var n=this.__on;if(n){for(var e,r=0,i=-1,o=n.length;r=0&&(this._names.splice(n,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var Cn=[null];function Pn(t,n){this._groups=t,this._parents=n}function zn(){return new Pn([[document.documentElement]],Cn)}function Dn(t){return"string"==typeof t?new Pn([[document.querySelector(t)]],[document.documentElement]):new Pn([[t]],Cn)}Pn.prototype=zn.prototype={constructor:Pn,select:function(t){"function"!=typeof t&&(t=St(t));for(var n=this._groups,e=n.length,r=new Array(e),i=0;i=x&&(x=m+1);!(b=y[x])&&++x=0;)(r=i[o])&&(a&&4^r.compareDocumentPosition(a)&&a.parentNode.insertBefore(r,a),a=r);return this},sort:function(t){function n(n,e){return n&&e?t(n.__data__,e.__data__):!n-!e}t||(t=Lt);for(var e=this._groups,r=e.length,i=new Array(r),o=0;o1?this.each((null==n?Zt:"function"==typeof n?Qt:Kt)(t,n,null==e?"":e)):Jt(this.node(),t)},property:function(t,n){return arguments.length>1?this.each((null==n?tn:"function"==typeof n?en:nn)(t,n)):this.node()[t]},classed:function(t,n){var e=rn(t+"");if(arguments.length<2){for(var r=on(this.node()),i=-1,o=e.length;++i()=>t;function Hn(t,{sourceEvent:n,subject:e,target:r,identifier:i,active:o,x:a,y:u,dx:c,dy:f,dispatch:s}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:n,enumerable:!0,configurable:!0},subject:{value:e,enumerable:!0,configurable:!0},target:{value:r,enumerable:!0,configurable:!0},identifier:{value:i,enumerable:!0,configurable:!0},active:{value:o,enumerable:!0,configurable:!0},x:{value:a,enumerable:!0,configurable:!0},y:{value:u,enumerable:!0,configurable:!0},dx:{value:c,enumerable:!0,configurable:!0},dy:{value:f,enumerable:!0,configurable:!0},_:{value:s}})}function Xn(t){return!t.ctrlKey&&!t.button}function Gn(){return this.parentNode}function Vn(t,n){return null==n?{x:t.x,y:t.y}:n}function $n(){return navigator.maxTouchPoints||"ontouchstart"in this}function Wn(t,n,e){t.prototype=n.prototype=e,e.constructor=t}function Zn(t,n){var e=Object.create(t.prototype);for(var r in n)e[r]=n[r];return e}function Kn(){}Hn.prototype.on=function(){var t=this._.on.apply(this._,arguments);return t===this._?this:t};var Qn=.7,Jn=1/Qn,te="\\s*([+-]?\\d+)\\s*",ne="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",ee="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",re=/^#([0-9a-f]{3,8})$/,ie=new RegExp("^rgb\\("+[te,te,te]+"\\)$"),oe=new RegExp("^rgb\\("+[ee,ee,ee]+"\\)$"),ae=new RegExp("^rgba\\("+[te,te,te,ne]+"\\)$"),ue=new RegExp("^rgba\\("+[ee,ee,ee,ne]+"\\)$"),ce=new RegExp("^hsl\\("+[ne,ee,ee]+"\\)$"),fe=new RegExp("^hsla\\("+[ne,ee,ee,ne]+"\\)$"),se={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};function le(){return this.rgb().formatHex()}function he(){return this.rgb().formatRgb()}function de(t){var n,e;return t=(t+"").trim().toLowerCase(),(n=re.exec(t))?(e=n[1].length,n=parseInt(n[1],16),6===e?pe(n):3===e?new _e(n>>8&15|n>>4&240,n>>4&15|240&n,(15&n)<<4|15&n,1):8===e?ge(n>>24&255,n>>16&255,n>>8&255,(255&n)/255):4===e?ge(n>>12&15|n>>8&240,n>>8&15|n>>4&240,n>>4&15|240&n,((15&n)<<4|15&n)/255):null):(n=ie.exec(t))?new _e(n[1],n[2],n[3],1):(n=oe.exec(t))?new _e(255*n[1]/100,255*n[2]/100,255*n[3]/100,1):(n=ae.exec(t))?ge(n[1],n[2],n[3],n[4]):(n=ue.exec(t))?ge(255*n[1]/100,255*n[2]/100,255*n[3]/100,n[4]):(n=ce.exec(t))?we(n[1],n[2]/100,n[3]/100,1):(n=fe.exec(t))?we(n[1],n[2]/100,n[3]/100,n[4]):se.hasOwnProperty(t)?pe(se[t]):"transparent"===t?new _e(NaN,NaN,NaN,0):null}function pe(t){return new _e(t>>16&255,t>>8&255,255&t,1)}function ge(t,n,e,r){return r<=0&&(t=n=e=NaN),new _e(t,n,e,r)}function ye(t){return t instanceof Kn||(t=de(t)),t?new _e((t=t.rgb()).r,t.g,t.b,t.opacity):new _e}function ve(t,n,e,r){return 1===arguments.length?ye(t):new _e(t,n,e,null==r?1:r)}function _e(t,n,e,r){this.r=+t,this.g=+n,this.b=+e,this.opacity=+r}function be(){return"#"+xe(this.r)+xe(this.g)+xe(this.b)}function me(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?")":", "+t+")")}function xe(t){return((t=Math.max(0,Math.min(255,Math.round(t)||0)))<16?"0":"")+t.toString(16)}function we(t,n,e,r){return r<=0?t=n=e=NaN:e<=0||e>=1?t=n=NaN:n<=0&&(t=NaN),new Te(t,n,e,r)}function Me(t){if(t instanceof Te)return new Te(t.h,t.s,t.l,t.opacity);if(t instanceof Kn||(t=de(t)),!t)return new Te;if(t instanceof Te)return t;var n=(t=t.rgb()).r/255,e=t.g/255,r=t.b/255,i=Math.min(n,e,r),o=Math.max(n,e,r),a=NaN,u=o-i,c=(o+i)/2;return u?(a=n===o?(e-r)/u+6*(e0&&c<1?0:a,new Te(a,u,c,t.opacity)}function Ae(t,n,e,r){return 1===arguments.length?Me(t):new Te(t,n,e,null==r?1:r)}function Te(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function Se(t,n,e){return 255*(t<60?n+(e-n)*t/60:t<180?e:t<240?n+(e-n)*(240-t)/60:n)}Wn(Kn,de,{copy:function(t){return Object.assign(new this.constructor,this,t)},displayable:function(){return this.rgb().displayable()},hex:le,formatHex:le,formatHsl:function(){return Me(this).formatHsl()},formatRgb:he,toString:he}),Wn(_e,ve,Zn(Kn,{brighter:function(t){return t=null==t?Jn:Math.pow(Jn,t),new _e(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?Qn:Math.pow(Qn,t),new _e(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:be,formatHex:be,formatRgb:me,toString:me})),Wn(Te,Ae,Zn(Kn,{brighter:function(t){return t=null==t?Jn:Math.pow(Jn,t),new Te(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?Qn:Math.pow(Qn,t),new Te(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),n=isNaN(t)||isNaN(this.s)?0:this.s,e=this.l,r=e+(e<.5?e:1-e)*n,i=2*e-r;return new _e(Se(t>=240?t-240:t+120,i,r),Se(t,i,r),Se(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl:function(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"hsl(":"hsla(")+(this.h||0)+", "+100*(this.s||0)+"%, "+100*(this.l||0)+"%"+(1===t?")":", "+t+")")}}));const Ee=Math.PI/180,ke=180/Math.PI,Ne=.96422,Ce=.82521,Pe=4/29,ze=6/29,De=3*ze*ze;function qe(t){if(t instanceof Fe)return new Fe(t.l,t.a,t.b,t.opacity);if(t instanceof je)return He(t);t instanceof _e||(t=ye(t));var n,e,r=Be(t.r),i=Be(t.g),o=Be(t.b),a=Oe((.2225045*r+.7168786*i+.0606169*o)/1);return r===i&&i===o?n=e=a:(n=Oe((.4360747*r+.3850649*i+.1430804*o)/Ne),e=Oe((.0139322*r+.0971045*i+.7141733*o)/Ce)),new Fe(116*a-16,500*(n-a),200*(a-e),t.opacity)}function Re(t,n,e,r){return 1===arguments.length?qe(t):new Fe(t,n,e,null==r?1:r)}function Fe(t,n,e,r){this.l=+t,this.a=+n,this.b=+e,this.opacity=+r}function Oe(t){return t>.008856451679035631?Math.pow(t,1/3):t/De+Pe}function Ie(t){return t>ze?t*t*t:De*(t-Pe)}function Ue(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function Be(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function Ye(t){if(t instanceof je)return new je(t.h,t.c,t.l,t.opacity);if(t instanceof Fe||(t=qe(t)),0===t.a&&0===t.b)return new je(NaN,0=1?(e=1,n-1):Math.floor(e*n),i=t[r],o=t[r+1],a=r>0?t[r-1]:2*i-o,u=r()=>t;function ar(t,n){return function(e){return t+e*n}}function ur(t,n){var e=n-t;return e?ar(t,e>180||e<-180?e-360*Math.round(e/360):e):or(isNaN(t)?n:t)}function cr(t){return 1==(t=+t)?fr:function(n,e){return e-n?function(t,n,e){return t=Math.pow(t,e),n=Math.pow(n,e)-t,e=1/e,function(r){return Math.pow(t+r*n,e)}}(n,e,t):or(isNaN(n)?e:n)}}function fr(t,n){var e=n-t;return e?ar(t,e):or(isNaN(t)?n:t)}var sr=function t(n){var e=cr(n);function r(t,n){var r=e((t=ve(t)).r,(n=ve(n)).r),i=e(t.g,n.g),o=e(t.b,n.b),a=fr(t.opacity,n.opacity);return function(n){return t.r=r(n),t.g=i(n),t.b=o(n),t.opacity=a(n),t+""}}return r.gamma=t,r}(1);function lr(t){return function(n){var e,r,i=n.length,o=new Array(i),a=new Array(i),u=new Array(i);for(e=0;eo&&(i=n.slice(o,i),u[a]?u[a]+=i:u[++a]=i),(e=e[0])===(r=r[0])?u[a]?u[a]+=r:u[++a]=r:(u[++a]=null,c.push({i:a,x:_r(e,r)})),o=xr.lastIndex;return o180?n+=360:n-t>180&&(t+=360),o.push({i:e.push(i(e)+"rotate(",null,r)-2,x:_r(t,n)})):n&&e.push(i(e)+"rotate("+n+r)}(o.rotate,a.rotate,u,c),function(t,n,e,o){t!==n?o.push({i:e.push(i(e)+"skewX(",null,r)-2,x:_r(t,n)}):n&&e.push(i(e)+"skewX("+n+r)}(o.skewX,a.skewX,u,c),function(t,n,e,r,o,a){if(t!==e||n!==r){var u=o.push(i(o)+"scale(",null,",",null,")");a.push({i:u-4,x:_r(t,e)},{i:u-2,x:_r(n,r)})}else 1===e&&1===r||o.push(i(o)+"scale("+e+","+r+")")}(o.scaleX,o.scaleY,a.scaleX,a.scaleY,u,c),o=a=null,function(t){for(var n,e=-1,r=c.length;++e=0&&n._call.call(null,t),n=n._next;--Gr}function oi(){Zr=(Wr=Qr.now())+Kr,Gr=Vr=0;try{ii()}finally{Gr=0,function(){var t,n,e=Hr,r=1/0;for(;e;)e._call?(r>e._time&&(r=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:Hr=n);Xr=t,ui(r)}(),Zr=0}}function ai(){var t=Qr.now(),n=t-Wr;n>1e3&&(Kr-=n,Wr=t)}function ui(t){Gr||(Vr&&(Vr=clearTimeout(Vr)),t-Zr>24?(t<1/0&&(Vr=setTimeout(oi,t-Qr.now()-Kr)),$r&&($r=clearInterval($r))):($r||(Wr=Qr.now(),$r=setInterval(ai,1e3)),Gr=1,Jr(oi)))}function ci(t,n,e){var r=new ei;return n=null==n?0:+n,r.restart((e=>{r.stop(),t(e+n)}),n,e),r}ei.prototype=ri.prototype={constructor:ei,restart:function(t,n,e){if("function"!=typeof t)throw new TypeError("callback is not a function");e=(null==e?ti():+e)+(null==n?0:+n),this._next||Xr===this||(Xr?Xr._next=this:Hr=this,Xr=this),this._call=t,this._time=e,ui()},stop:function(){this._call&&(this._call=null,this._time=1/0,ui())}};var fi=pt("start","end","cancel","interrupt"),si=[];function li(t,n,e,r,i,o){var a=t.__transition;if(a){if(e in a)return}else t.__transition={};!function(t,n,e){var r,i=t.__transition;function o(t){e.state=1,e.timer.restart(a,e.delay,e.time),e.delay<=t&&a(t-e.delay)}function a(o){var f,s,l,h;if(1!==e.state)return c();for(f in i)if((h=i[f]).name===e.name){if(3===h.state)return ci(a);4===h.state?(h.state=6,h.timer.stop(),h.on.call("interrupt",t,t.__data__,h.index,h.group),delete i[f]):+f0)throw new Error("too late; already scheduled");return e}function di(t,n){var e=pi(t,n);if(e.state>3)throw new Error("too late; already running");return e}function pi(t,n){var e=t.__transition;if(!e||!(e=e[n]))throw new Error("transition not found");return e}function gi(t,n){var e,r,i,o=t.__transition,a=!0;if(o){for(i in n=null==n?null:n+"",o)(e=o[i]).name===n?(r=e.state>2&&e.state<5,e.state=6,e.timer.stop(),e.on.call(r?"interrupt":"cancel",t,t.__data__,e.index,e.group),delete o[i]):a=!1;a&&delete t.__transition}}function yi(t,n){var e,r;return function(){var i=di(this,t),o=i.tween;if(o!==e)for(var a=0,u=(r=e=o).length;a=0&&(t=t.slice(0,n)),!t||"start"===t}))}(n)?hi:di;return function(){var a=o(this,t),u=a.on;u!==r&&(i=(r=u).copy()).on(n,e),a.on=i}}var Fi=zn.prototype.constructor;function Oi(t){return function(){this.style.removeProperty(t)}}function Ii(t,n,e){return function(r){this.style.setProperty(t,n.call(this,r),e)}}function Ui(t,n,e){var r,i;function o(){var o=n.apply(this,arguments);return o!==i&&(r=(i=o)&&Ii(t,o,e)),r}return o._value=n,o}function Bi(t){return function(n){this.textContent=t.call(this,n)}}function Yi(t){var n,e;function r(){var r=t.apply(this,arguments);return r!==e&&(n=(e=r)&&Bi(r)),n}return r._value=t,r}var Li=0;function ji(t,n,e,r){this._groups=t,this._parents=n,this._name=e,this._id=r}function Hi(t){return zn().transition(t)}function Xi(){return++Li}var Gi=zn.prototype;ji.prototype=Hi.prototype={constructor:ji,select:function(t){var n=this._name,e=this._id;"function"!=typeof t&&(t=St(t));for(var r=this._groups,i=r.length,o=new Array(i),a=0;a()=>t;function mo(t,{sourceEvent:n,target:e,selection:r,mode:i,dispatch:o}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:n,enumerable:!0,configurable:!0},target:{value:e,enumerable:!0,configurable:!0},selection:{value:r,enumerable:!0,configurable:!0},mode:{value:i,enumerable:!0,configurable:!0},_:{value:o}})}function xo(t){t.stopImmediatePropagation()}function wo(t){t.preventDefault(),t.stopImmediatePropagation()}var Mo={name:"drag"},Ao={name:"space"},To={name:"handle"},So={name:"center"};const{abs:Eo,max:ko,min:No}=Math;function Co(t){return[+t[0],+t[1]]}function Po(t){return[Co(t[0]),Co(t[1])]}var zo={name:"x",handles:["w","e"].map(Bo),input:function(t,n){return null==t?null:[[+t[0],n[0][1]],[+t[1],n[1][1]]]},output:function(t){return t&&[t[0][0],t[1][0]]}},Do={name:"y",handles:["n","s"].map(Bo),input:function(t,n){return null==t?null:[[n[0][0],+t[0]],[n[1][0],+t[1]]]},output:function(t){return t&&[t[0][1],t[1][1]]}},qo={name:"xy",handles:["n","w","e","s","nw","ne","sw","se"].map(Bo),input:function(t){return null==t?null:Po(t)},output:function(t){return t}},Ro={overlay:"crosshair",selection:"move",n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Fo={e:"w",w:"e",nw:"ne",ne:"nw",se:"sw",sw:"se"},Oo={n:"s",s:"n",nw:"sw",ne:"se",se:"ne",sw:"nw"},Io={overlay:1,selection:1,n:null,e:1,s:null,w:-1,nw:-1,ne:1,se:1,sw:-1},Uo={overlay:1,selection:1,n:-1,e:null,s:1,w:null,nw:-1,ne:-1,se:1,sw:1};function Bo(t){return{type:t}}function Yo(t){return!t.ctrlKey&&!t.button}function Lo(){var t=this.ownerSVGElement||this;return t.hasAttribute("viewBox")?[[(t=t.viewBox.baseVal).x,t.y],[t.x+t.width,t.y+t.height]]:[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]}function jo(){return navigator.maxTouchPoints||"ontouchstart"in this}function Ho(t){for(;!t.__brush;)if(!(t=t.parentNode))return;return t.__brush}function Xo(t){return t[0][0]===t[1][0]||t[0][1]===t[1][1]}function Go(t){var n,e=Lo,r=Yo,i=jo,o=!0,a=pt("start","brush","end"),u=6;function c(n){var e=n.property("__brush",g).selectAll(".overlay").data([Bo("overlay")]);e.enter().append("rect").attr("class","overlay").attr("pointer-events","all").attr("cursor",Ro.overlay).merge(e).each((function(){var t=Ho(this).extent;Dn(this).attr("x",t[0][0]).attr("y",t[0][1]).attr("width",t[1][0]-t[0][0]).attr("height",t[1][1]-t[0][1])})),n.selectAll(".selection").data([Bo("selection")]).enter().append("rect").attr("class","selection").attr("cursor",Ro.selection).attr("fill","#777").attr("fill-opacity",.3).attr("stroke","#fff").attr("shape-rendering","crispEdges");var r=n.selectAll(".handle").data(t.handles,(function(t){return t.type}));r.exit().remove(),r.enter().append("rect").attr("class",(function(t){return"handle handle--"+t.type})).attr("cursor",(function(t){return Ro[t.type]})),n.each(f).attr("fill","none").attr("pointer-events","all").on("mousedown.brush",h).filter(i).on("touchstart.brush",h).on("touchmove.brush",d).on("touchend.brush touchcancel.brush",p).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function f(){var t=Dn(this),n=Ho(this).selection;n?(t.selectAll(".selection").style("display",null).attr("x",n[0][0]).attr("y",n[0][1]).attr("width",n[1][0]-n[0][0]).attr("height",n[1][1]-n[0][1]),t.selectAll(".handle").style("display",null).attr("x",(function(t){return"e"===t.type[t.type.length-1]?n[1][0]-u/2:n[0][0]-u/2})).attr("y",(function(t){return"s"===t.type[0]?n[1][1]-u/2:n[0][1]-u/2})).attr("width",(function(t){return"n"===t.type||"s"===t.type?n[1][0]-n[0][0]+u:u})).attr("height",(function(t){return"e"===t.type||"w"===t.type?n[1][1]-n[0][1]+u:u}))):t.selectAll(".selection,.handle").style("display","none").attr("x",null).attr("y",null).attr("width",null).attr("height",null)}function s(t,n,e){var r=t.__brush.emitter;return!r||e&&r.clean?new l(t,n,e):r}function l(t,n,e){this.that=t,this.args=n,this.state=t.__brush,this.active=0,this.clean=e}function h(e){if((!n||e.touches)&&r.apply(this,arguments)){var i,a,u,c,l,h,d,p,g,y,v,_=this,b=e.target.__data__.type,m="selection"===(o&&e.metaKey?b="overlay":b)?Mo:o&&e.altKey?So:To,x=t===Do?null:Io[b],w=t===zo?null:Uo[b],M=Ho(_),A=M.extent,T=M.selection,S=A[0][0],E=A[0][1],k=A[1][0],N=A[1][1],C=0,P=0,z=x&&w&&o&&e.shiftKey,D=Array.from(e.touches||[e],(t=>{const n=t.identifier;return(t=In(t,_)).point0=t.slice(),t.identifier=n,t}));if("overlay"===b){T&&(g=!0);const n=[D[0],D[1]||D[0]];M.selection=T=[[i=t===Do?S:No(n[0][0],n[1][0]),u=t===zo?E:No(n[0][1],n[1][1])],[l=t===Do?k:ko(n[0][0],n[1][0]),d=t===zo?N:ko(n[0][1],n[1][1])]],D.length>1&&U()}else i=T[0][0],u=T[0][1],l=T[1][0],d=T[1][1];a=i,c=u,h=l,p=d;var q=Dn(_).attr("pointer-events","none"),R=q.selectAll(".overlay").attr("cursor",Ro[b]);gi(_);var F=s(_,arguments,!0).beforestart();if(e.touches)F.moved=I,F.ended=B;else{var O=Dn(e.view).on("mousemove.brush",I,!0).on("mouseup.brush",B,!0);o&&O.on("keydown.brush",Y,!0).on("keyup.brush",L,!0),Yn(e.view)}f.call(_),F.start(e,m.name)}function I(t){for(const n of t.changedTouches||[t])for(const t of D)t.identifier===n.identifier&&(t.cur=In(n,_));if(z&&!y&&!v&&1===D.length){const t=D[0];Eo(t.cur[0]-t[0])>Eo(t.cur[1]-t[1])?v=!0:y=!0}for(const t of D)t.cur&&(t[0]=t.cur[0],t[1]=t.cur[1]);g=!0,wo(t),U(t)}function U(t){const n=D[0],e=n.point0;var r;switch(C=n[0]-e[0],P=n[1]-e[1],m){case Ao:case Mo:x&&(C=ko(S-i,No(k-l,C)),a=i+C,h=l+C),w&&(P=ko(E-u,No(N-d,P)),c=u+P,p=d+P);break;case To:D[1]?(x&&(a=ko(S,No(k,D[0][0])),h=ko(S,No(k,D[1][0])),x=1),w&&(c=ko(E,No(N,D[0][1])),p=ko(E,No(N,D[1][1])),w=1)):(x<0?(C=ko(S-i,No(k-i,C)),a=i+C,h=l):x>0&&(C=ko(S-l,No(k-l,C)),a=i,h=l+C),w<0?(P=ko(E-u,No(N-u,P)),c=u+P,p=d):w>0&&(P=ko(E-d,No(N-d,P)),c=u,p=d+P));break;case So:x&&(a=ko(S,No(k,i-C*x)),h=ko(S,No(k,l+C*x))),w&&(c=ko(E,No(N,u-P*w)),p=ko(E,No(N,d+P*w)))}h0&&(i=a-C),w<0?d=p-P:w>0&&(u=c-P),m=Ao,R.attr("cursor",Ro.selection),U());break;default:return}wo(t)}function L(t){switch(t.keyCode){case 16:z&&(y=v=z=!1,U());break;case 18:m===So&&(x<0?l=h:x>0&&(i=a),w<0?d=p:w>0&&(u=c),m=To,U());break;case 32:m===Ao&&(t.altKey?(x&&(l=h-C*x,i=a+C*x),w&&(d=p-P*w,u=c+P*w),m=So):(x<0?l=h:x>0&&(i=a),w<0?d=p:w>0&&(u=c),m=To),R.attr("cursor",Ro[b]),U());break;default:return}wo(t)}}function d(t){s(this,arguments).moved(t)}function p(t){s(this,arguments).ended(t)}function g(){var n=this.__brush||{selection:null};return n.extent=Po(e.apply(this,arguments)),n.dim=t,n}return c.move=function(n,e){n.tween?n.on("start.brush",(function(t){s(this,arguments).beforestart().start(t)})).on("interrupt.brush end.brush",(function(t){s(this,arguments).end(t)})).tween("brush",(function(){var n=this,r=n.__brush,i=s(n,arguments),o=r.selection,a=t.input("function"==typeof e?e.apply(this,arguments):e,r.extent),u=Mr(o,a);function c(t){r.selection=1===t&&null===a?null:u(t),f.call(n),i.brush()}return null!==o&&null!==a?c:c(1)})):n.each((function(){var n=this,r=arguments,i=n.__brush,o=t.input("function"==typeof e?e.apply(n,r):e,i.extent),a=s(n,r).beforestart();gi(n),i.selection=null===o?null:o,f.call(n),a.start().brush().end()}))},c.clear=function(t){c.move(t,null)},l.prototype={beforestart:function(){return 1==++this.active&&(this.state.emitter=this,this.starting=!0),this},start:function(t,n){return this.starting?(this.starting=!1,this.emit("start",t,n)):this.emit("brush",t),this},brush:function(t,n){return this.emit("brush",t,n),this},end:function(t,n){return 0==--this.active&&(delete this.state.emitter,this.emit("end",t,n)),this},emit:function(n,e,r){var i=Dn(this.that).datum();a.call(n,this.that,new mo(n,{sourceEvent:e,target:c,selection:t.output(this.state.selection),mode:r,dispatch:a}),i)}},c.extent=function(t){return arguments.length?(e="function"==typeof t?t:bo(Po(t)),c):e},c.filter=function(t){return arguments.length?(r="function"==typeof t?t:bo(!!t),c):r},c.touchable=function(t){return arguments.length?(i="function"==typeof t?t:bo(!!t),c):i},c.handleSize=function(t){return arguments.length?(u=+t,c):u},c.keyModifiers=function(t){return arguments.length?(o=!!t,c):o},c.on=function(){var t=a.on.apply(a,arguments);return t===a?c:t},c}var Vo=Math.abs,$o=Math.cos,Wo=Math.sin,Zo=Math.PI,Ko=Zo/2,Qo=2*Zo,Jo=Math.max,ta=1e-12;function na(t,n){return Array.from({length:n-t},((n,e)=>t+e))}function ea(t){return function(n,e){return t(n.source.value+n.target.value,e.source.value+e.target.value)}}function ra(t,n){var e=0,r=null,i=null,o=null;function a(a){var u,c=a.length,f=new Array(c),s=na(0,c),l=new Array(c*c),h=new Array(c),d=0;a=Float64Array.from({length:c*c},n?(t,n)=>a[n%c][n/c|0]:(t,n)=>a[n/c|0][n%c]);for(let n=0;nr(f[t],f[n])));for(const e of s){const r=n;if(t){const t=na(1+~c,c).filter((t=>t<0?a[~t*c+e]:a[e*c+t]));i&&t.sort(((t,n)=>i(t<0?-a[~t*c+e]:a[e*c+t],n<0?-a[~n*c+e]:a[e*c+n])));for(const r of t)if(r<0){(l[~r*c+e]||(l[~r*c+e]={source:null,target:null})).target={index:e,startAngle:n,endAngle:n+=a[~r*c+e]*d,value:a[~r*c+e]}}else{(l[e*c+r]||(l[e*c+r]={source:null,target:null})).source={index:e,startAngle:n,endAngle:n+=a[e*c+r]*d,value:a[e*c+r]}}h[e]={index:e,startAngle:r,endAngle:n,value:f[e]}}else{const t=na(0,c).filter((t=>a[e*c+t]||a[t*c+e]));i&&t.sort(((t,n)=>i(a[e*c+t],a[e*c+n])));for(const r of t){let t;if(eaa)if(Math.abs(s*u-c*f)>aa&&i){var h=e-o,d=r-a,p=u*u+c*c,g=h*h+d*d,y=Math.sqrt(p),v=Math.sqrt(l),_=i*Math.tan((ia-Math.acos((p+l-g)/(2*y*v)))/2),b=_/v,m=_/y;Math.abs(b-1)>aa&&(this._+="L"+(t+b*f)+","+(n+b*s)),this._+="A"+i+","+i+",0,0,"+ +(s*h>f*d)+","+(this._x1=t+m*u)+","+(this._y1=n+m*c)}else this._+="L"+(this._x1=t)+","+(this._y1=n);else;},arc:function(t,n,e,r,i,o){t=+t,n=+n,o=!!o;var a=(e=+e)*Math.cos(r),u=e*Math.sin(r),c=t+a,f=n+u,s=1^o,l=o?r-i:i-r;if(e<0)throw new Error("negative radius: "+e);null===this._x1?this._+="M"+c+","+f:(Math.abs(this._x1-c)>aa||Math.abs(this._y1-f)>aa)&&(this._+="L"+c+","+f),e&&(l<0&&(l=l%oa+oa),l>ua?this._+="A"+e+","+e+",0,1,"+s+","+(t-a)+","+(n-u)+"A"+e+","+e+",0,1,"+s+","+(this._x1=c)+","+(this._y1=f):l>aa&&(this._+="A"+e+","+e+",0,"+ +(l>=ia)+","+s+","+(this._x1=t+e*Math.cos(i))+","+(this._y1=n+e*Math.sin(i))))},rect:function(t,n,e,r){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+n)+"h"+ +e+"v"+ +r+"h"+-e+"Z"},toString:function(){return this._}};var sa=Array.prototype.slice;function la(t){return function(){return t}}function ha(t){return t.source}function da(t){return t.target}function pa(t){return t.radius}function ga(t){return t.startAngle}function ya(t){return t.endAngle}function va(){return 0}function _a(){return 10}function ba(t){var n=ha,e=da,r=pa,i=pa,o=ga,a=ya,u=va,c=null;function f(){var f,s=n.apply(this,arguments),l=e.apply(this,arguments),h=u.apply(this,arguments)/2,d=sa.call(arguments),p=+r.apply(this,(d[0]=s,d)),g=o.apply(this,d)-Ko,y=a.apply(this,d)-Ko,v=+i.apply(this,(d[0]=l,d)),_=o.apply(this,d)-Ko,b=a.apply(this,d)-Ko;if(c||(c=f=fa()),h>ta&&(Vo(y-g)>2*h+ta?y>g?(g+=h,y-=h):(g-=h,y+=h):g=y=(g+y)/2,Vo(b-_)>2*h+ta?b>_?(_+=h,b-=h):(_-=h,b+=h):_=b=(_+b)/2),c.moveTo(p*$o(g),p*Wo(g)),c.arc(0,0,p,g,y),g!==_||y!==b)if(t){var m=+t.apply(this,arguments),x=v-m,w=(_+b)/2;c.quadraticCurveTo(0,0,x*$o(_),x*Wo(_)),c.lineTo(v*$o(w),v*Wo(w)),c.lineTo(x*$o(b),x*Wo(b))}else c.quadraticCurveTo(0,0,v*$o(_),v*Wo(_)),c.arc(0,0,v,_,b);if(c.quadraticCurveTo(0,0,p*$o(g),p*Wo(g)),c.closePath(),f)return c=null,f+""||null}return t&&(f.headRadius=function(n){return arguments.length?(t="function"==typeof n?n:la(+n),f):t}),f.radius=function(t){return arguments.length?(r=i="function"==typeof t?t:la(+t),f):r},f.sourceRadius=function(t){return arguments.length?(r="function"==typeof t?t:la(+t),f):r},f.targetRadius=function(t){return arguments.length?(i="function"==typeof t?t:la(+t),f):i},f.startAngle=function(t){return arguments.length?(o="function"==typeof t?t:la(+t),f):o},f.endAngle=function(t){return arguments.length?(a="function"==typeof t?t:la(+t),f):a},f.padAngle=function(t){return arguments.length?(u="function"==typeof t?t:la(+t),f):u},f.source=function(t){return arguments.length?(n=t,f):n},f.target=function(t){return arguments.length?(e=t,f):e},f.context=function(t){return arguments.length?(c=null==t?null:t,f):c},f}var ma=Array.prototype.slice;function xa(t,n){return t-n}var wa=t=>()=>t;function Ma(t,n){for(var e,r=-1,i=n.length;++rr!=d>r&&e<(h-f)*(r-s)/(d-s)+f&&(i=-i)}return i}function Ta(t,n,e){var r,i,o,a;return function(t,n,e){return(n[0]-t[0])*(e[1]-t[1])==(e[0]-t[0])*(n[1]-t[1])}(t,n,e)&&(i=t[r=+(t[0]===n[0])],o=e[r],a=n[r],i<=o&&o<=a||a<=o&&o<=i)}function Sa(){}var Ea=[[],[[[1,1.5],[.5,1]]],[[[1.5,1],[1,1.5]]],[[[1.5,1],[.5,1]]],[[[1,.5],[1.5,1]]],[[[1,1.5],[.5,1]],[[1,.5],[1.5,1]]],[[[1,.5],[1,1.5]]],[[[1,.5],[.5,1]]],[[[.5,1],[1,.5]]],[[[1,1.5],[1,.5]]],[[[.5,1],[1,.5]],[[1.5,1],[1,1.5]]],[[[1.5,1],[1,.5]]],[[[.5,1],[1.5,1]]],[[[1,1.5],[1.5,1]]],[[[.5,1],[1,1.5]]],[]];function ka(){var t=1,n=1,e=I,r=u;function i(t){var n=e(t);if(Array.isArray(n))n=n.slice().sort(xa);else{var r=p(t),i=r[0],a=r[1];n=F(i,a,n),n=Z(Math.floor(i/n)*n,Math.floor(a/n)*n,n)}return n.map((function(n){return o(t,n)}))}function o(e,i){var o=[],u=[];return function(e,r,i){var o,u,c,f,s,l,h=new Array,d=new Array;o=u=-1,f=e[0]>=r,Ea[f<<1].forEach(p);for(;++o=r,Ea[c|f<<1].forEach(p);Ea[f<<0].forEach(p);for(;++u=r,s=e[u*t]>=r,Ea[f<<1|s<<2].forEach(p);++o=r,l=s,s=e[u*t+o+1]>=r,Ea[c|f<<1|s<<2|l<<3].forEach(p);Ea[f|s<<3].forEach(p)}o=-1,s=e[u*t]>=r,Ea[s<<2].forEach(p);for(;++o=r,Ea[s<<2|l<<3].forEach(p);function p(t){var n,e,r=[t[0][0]+o,t[0][1]+u],c=[t[1][0]+o,t[1][1]+u],f=a(r),s=a(c);(n=d[f])?(e=h[s])?(delete d[n.end],delete h[e.start],n===e?(n.ring.push(c),i(n.ring)):h[n.start]=d[e.end]={start:n.start,end:e.end,ring:n.ring.concat(e.ring)}):(delete d[n.end],n.ring.push(c),d[n.end=s]=n):(n=h[s])?(e=d[f])?(delete h[n.start],delete d[e.end],n===e?(n.ring.push(c),i(n.ring)):h[e.start]=d[n.end]={start:e.start,end:n.end,ring:e.ring.concat(n.ring)}):(delete h[n.start],n.ring.unshift(r),h[n.start=f]=n):h[f]=d[s]={start:f,end:s,ring:[r,c]}}Ea[s<<3].forEach(p)}(e,i,(function(t){r(t,e,i),function(t){for(var n=0,e=t.length,r=t[e-1][1]*t[0][0]-t[e-1][0]*t[0][1];++n0?o.push([t]):u.push(t)})),u.forEach((function(t){for(var n,e=0,r=o.length;e0&&a0&&u=0&&o>=0))throw new Error("invalid size");return t=r,n=o,i},i.thresholds=function(t){return arguments.length?(e="function"==typeof t?t:Array.isArray(t)?wa(ma.call(t)):wa(t),i):e},i.smooth=function(t){return arguments.length?(r=t?u:Sa,i):r===u},i}function Na(t,n,e){for(var r=t.width,i=t.height,o=1+(e<<1),a=0;a=e&&(u>=o&&(c-=t.data[u-o+a*r]),n.data[u-e+a*r]=c/Math.min(u+1,r-1+o-u,o))}function Ca(t,n,e){for(var r=t.width,i=t.height,o=1+(e<<1),a=0;a=e&&(u>=o&&(c-=t.data[a+(u-o)*r]),n.data[a+(u-e)*r]=c/Math.min(u+1,i-1+o-u,o))}function Pa(t){return t[0]}function za(t){return t[1]}function Da(){return 1}const qa=Math.pow(2,-52),Ra=new Uint32Array(512);class Fa{static from(t,n=Ha,e=Xa){const r=t.length,i=new Float64Array(2*r);for(let o=0;o>1;if(n>0&&"number"!=typeof t[0])throw new Error("Expected coords to contain numbers.");this.coords=t;const e=Math.max(2*n-5,0);this._triangles=new Uint32Array(3*e),this._halfedges=new Int32Array(3*e),this._hashSize=Math.ceil(Math.sqrt(n)),this._hullPrev=new Uint32Array(n),this._hullNext=new Uint32Array(n),this._hullTri=new Uint32Array(n),this._hullHash=new Int32Array(this._hashSize).fill(-1),this._ids=new Uint32Array(n),this._dists=new Float64Array(n),this.update()}update(){const{coords:t,_hullPrev:n,_hullNext:e,_hullTri:r,_hullHash:i}=this,o=t.length>>1;let a=1/0,u=1/0,c=-1/0,f=-1/0;for(let n=0;nc&&(c=e),r>f&&(f=r),this._ids[n]=n}const s=(a+c)/2,l=(u+f)/2;let h,d,p,g=1/0;for(let n=0;n0&&(d=n,g=e)}let _=t[2*d],b=t[2*d+1],m=1/0;for(let n=0;nr&&(n[e++]=i,r=this._dists[i])}return this.hull=n.subarray(0,e),this.triangles=new Uint32Array(0),void(this.halfedges=new Uint32Array(0))}if(Ua(y,v,_,b,x,w)){const t=d,n=_,e=b;d=p,_=x,b=w,p=t,x=n,w=e}const M=function(t,n,e,r,i,o){const a=e-t,u=r-n,c=i-t,f=o-n,s=a*a+u*u,l=c*c+f*f,h=.5/(a*f-u*c);return{x:t+(f*s-u*l)*h,y:n+(a*l-c*s)*h}}(y,v,_,b,x,w);this._cx=M.x,this._cy=M.y;for(let n=0;n0&&Math.abs(f-o)<=qa&&Math.abs(s-a)<=qa)continue;if(o=f,a=s,c===h||c===d||c===p)continue;let l=0;for(let t=0,n=this._hashKey(f,s);t0?3-e:1+e)/4}(t-this._cx,n-this._cy)*this._hashSize)%this._hashSize}_legalize(t){const{_triangles:n,_halfedges:e,coords:r}=this;let i=0,o=0;for(;;){const a=e[t],u=t-t%3;if(o=u+(t+2)%3,-1===a){if(0===i)break;t=Ra[--i];continue}const c=a-a%3,f=u+(t+1)%3,s=c+(a+2)%3,l=n[o],h=n[t],d=n[f],p=n[s];if(Ba(r[2*l],r[2*l+1],r[2*h],r[2*h+1],r[2*d],r[2*d+1],r[2*p],r[2*p+1])){n[t]=p,n[a]=l;const r=e[s];if(-1===r){let n=this._hullStart;do{if(this._hullTri[n]===s){this._hullTri[n]=t;break}n=this._hullPrev[n]}while(n!==this._hullStart)}this._link(t,r),this._link(a,e[o]),this._link(o,s);const u=c+(a+1)%3;i=33306690738754716e-32*Math.abs(a+u)?a-u:0}function Ua(t,n,e,r,i,o){return(Ia(i,o,t,n,e,r)||Ia(t,n,e,r,i,o)||Ia(e,r,i,o,t,n))<0}function Ba(t,n,e,r,i,o,a,u){const c=t-a,f=n-u,s=e-a,l=r-u,h=i-a,d=o-u,p=s*s+l*l,g=h*h+d*d;return c*(l*g-p*d)-f*(s*g-p*h)+(c*c+f*f)*(s*d-l*h)<0}function Ya(t,n,e,r,i,o){const a=e-t,u=r-n,c=i-t,f=o-n,s=a*a+u*u,l=c*c+f*f,h=.5/(a*f-u*c),d=(f*s-u*l)*h,p=(a*l-c*s)*h;return d*d+p*p}function La(t,n,e,r){if(r-e<=20)for(let i=e+1;i<=r;i++){const r=t[i],o=n[r];let a=i-1;for(;a>=e&&n[t[a]]>o;)t[a+1]=t[a--];t[a+1]=r}else{let i=e+1,o=r;ja(t,e+r>>1,i),n[t[e]]>n[t[r]]&&ja(t,e,r),n[t[i]]>n[t[r]]&&ja(t,i,r),n[t[e]]>n[t[i]]&&ja(t,e,i);const a=t[i],u=n[a];for(;;){do{i++}while(n[t[i]]u);if(o=o-e?(La(t,n,i,r),La(t,n,e,o-1)):(La(t,n,e,o-1),La(t,n,i,r))}}function ja(t,n,e){const r=t[n];t[n]=t[e],t[e]=r}function Ha(t){return t[0]}function Xa(t){return t[1]}const Ga=1e-6;class Va{constructor(){this._x0=this._y0=this._x1=this._y1=null,this._=""}moveTo(t,n){this._+=`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}`}closePath(){null!==this._x1&&(this._x1=this._x0,this._y1=this._y0,this._+="Z")}lineTo(t,n){this._+=`L${this._x1=+t},${this._y1=+n}`}arc(t,n,e){const r=(t=+t)+(e=+e),i=n=+n;if(e<0)throw new Error("negative radius");null===this._x1?this._+=`M${r},${i}`:(Math.abs(this._x1-r)>Ga||Math.abs(this._y1-i)>Ga)&&(this._+="L"+r+","+i),e&&(this._+=`A${e},${e},0,1,1,${t-e},${n}A${e},${e},0,1,1,${this._x1=r},${this._y1=i}`)}rect(t,n,e,r){this._+=`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}h${+e}v${+r}h${-e}Z`}value(){return this._||null}}class $a{constructor(){this._=[]}moveTo(t,n){this._.push([t,n])}closePath(){this._.push(this._[0].slice())}lineTo(t,n){this._.push([t,n])}value(){return this._.length?this._:null}}class Wa{constructor(t,[n,e,r,i]=[0,0,960,500]){if(!((r=+r)>=(n=+n)&&(i=+i)>=(e=+e)))throw new Error("invalid bounds");this.delaunay=t,this._circumcenters=new Float64Array(2*t.points.length),this.vectors=new Float64Array(2*t.points.length),this.xmax=r,this.xmin=n,this.ymax=i,this.ymin=e,this._init()}update(){return this.delaunay.update(),this._init(),this}_init(){const{delaunay:{points:t,hull:n,triangles:e},vectors:r}=this,i=this.circumcenters=this._circumcenters.subarray(0,e.length/3*2);for(let n,r,o=0,a=0,u=e.length;o1;)i-=2;for(let t=2;t4)for(let t=0;t0){if(n>=this.ymax)return null;(i=(this.ymax-n)/r)0){if(t>=this.xmax)return null;(i=(this.xmax-t)/e)this.xmax?2:0)|(nthis.ymax?8:0)}}const Za=2*Math.PI,Ka=Math.pow;function Qa(t){return t[0]}function Ja(t){return t[1]}function tu(t,n,e){return[t+Math.sin(t+n)*e,n+Math.cos(t-n)*e]}class nu{static from(t,n=Qa,e=Ja,r){return new nu("length"in t?function(t,n,e,r){const i=t.length,o=new Float64Array(2*i);for(let a=0;a2&&function(t){const{triangles:n,coords:e}=t;for(let t=0;t1e-10)return!1}return!0}(t)){this.collinear=Int32Array.from({length:n.length/2},((t,n)=>n)).sort(((t,e)=>n[2*t]-n[2*e]||n[2*t+1]-n[2*e+1]));const t=this.collinear[0],e=this.collinear[this.collinear.length-1],r=[n[2*t],n[2*t+1],n[2*e],n[2*e+1]],i=1e-8*Math.hypot(r[3]-r[1],r[2]-r[0]);for(let t=0,e=n.length/2;t0&&(this.triangles=new Int32Array(3).fill(-1),this.halfedges=new Int32Array(3).fill(-1),this.triangles[0]=r[0],this.triangles[1]=r[1],this.triangles[2]=r[1],o[r[0]]=1,2===r.length&&(o[r[1]]=0))}voronoi(t){return new Wa(this,t)}*neighbors(t){const{inedges:n,hull:e,_hullIndex:r,halfedges:i,triangles:o,collinear:a}=this;if(a){const n=a.indexOf(t);return n>0&&(yield a[n-1]),void(n=0&&i!==e&&i!==r;)e=i;return i}_step(t,n,e){const{inedges:r,hull:i,_hullIndex:o,halfedges:a,triangles:u,points:c}=this;if(-1===r[t]||!c.length)return(t+1)%(c.length>>1);let f=t,s=Ka(n-c[2*t],2)+Ka(e-c[2*t+1],2);const l=r[t];let h=l;do{let r=u[h];const l=Ka(n-c[2*r],2)+Ka(e-c[2*r+1],2);if(l9999?"+"+au(t,6):au(t,4)}(t.getUTCFullYear())+"-"+au(t.getUTCMonth()+1,2)+"-"+au(t.getUTCDate(),2)+(i?"T"+au(n,2)+":"+au(e,2)+":"+au(r,2)+"."+au(i,3)+"Z":r?"T"+au(n,2)+":"+au(e,2)+":"+au(r,2)+"Z":e||n?"T"+au(n,2)+":"+au(e,2)+"Z":"")}function cu(t){var n=new RegExp('["'+t+"\n\r]"),e=t.charCodeAt(0);function r(t,n){var r,i=[],o=t.length,a=0,u=0,c=o<=0,f=!1;function s(){if(c)return ru;if(f)return f=!1,eu;var n,r,i=a;if(34===t.charCodeAt(i)){for(;a++=o?c=!0:10===(r=t.charCodeAt(a++))?f=!0:13===r&&(f=!0,10===t.charCodeAt(a)&&++a),t.slice(i+1,n-1).replace(/""/g,'"')}for(;aNu(n,e).then((n=>(new DOMParser).parseFromString(n,t)))}var Ru=qu("application/xml"),Fu=qu("text/html"),Ou=qu("image/svg+xml");function Iu(t,n,e,r){if(isNaN(n)||isNaN(e))return t;var i,o,a,u,c,f,s,l,h,d=t._root,p={data:r},g=t._x0,y=t._y0,v=t._x1,_=t._y1;if(!d)return t._root=p,t;for(;d.length;)if((f=n>=(o=(g+v)/2))?g=o:v=o,(s=e>=(a=(y+_)/2))?y=a:_=a,i=d,!(d=d[l=s<<1|f]))return i[l]=p,t;if(u=+t._x.call(null,d.data),c=+t._y.call(null,d.data),n===u&&e===c)return p.next=d,i?i[l]=p:t._root=p,t;do{i=i?i[l]=new Array(4):t._root=new Array(4),(f=n>=(o=(g+v)/2))?g=o:v=o,(s=e>=(a=(y+_)/2))?y=a:_=a}while((l=s<<1|f)==(h=(c>=a)<<1|u>=o));return i[h]=d,i[l]=p,t}function Uu(t,n,e,r,i){this.node=t,this.x0=n,this.y0=e,this.x1=r,this.y1=i}function Bu(t){return t[0]}function Yu(t){return t[1]}function Lu(t,n,e){var r=new ju(null==n?Bu:n,null==e?Yu:e,NaN,NaN,NaN,NaN);return null==t?r:r.addAll(t)}function ju(t,n,e,r,i,o){this._x=t,this._y=n,this._x0=e,this._y0=r,this._x1=i,this._y1=o,this._root=void 0}function Hu(t){for(var n={data:t.data},e=n;t=t.next;)e=e.next={data:t.data};return n}var Xu=Lu.prototype=ju.prototype;function Gu(t){return function(){return t}}function Vu(t){return 1e-6*(t()-.5)}function $u(t){return t.x+t.vx}function Wu(t){return t.y+t.vy}function Zu(t){return t.index}function Ku(t,n){var e=t.get(n);if(!e)throw new Error("node not found: "+n);return e}Xu.copy=function(){var t,n,e=new ju(this._x,this._y,this._x0,this._y0,this._x1,this._y1),r=this._root;if(!r)return e;if(!r.length)return e._root=Hu(r),e;for(t=[{source:r,target:e._root=new Array(4)}];r=t.pop();)for(var i=0;i<4;++i)(n=r.source[i])&&(n.length?t.push({source:n,target:r.target[i]=new Array(4)}):r.target[i]=Hu(n));return e},Xu.add=function(t){const n=+this._x.call(null,t),e=+this._y.call(null,t);return Iu(this.cover(n,e),n,e,t)},Xu.addAll=function(t){var n,e,r,i,o=t.length,a=new Array(o),u=new Array(o),c=1/0,f=1/0,s=-1/0,l=-1/0;for(e=0;es&&(s=r),il&&(l=i));if(c>s||f>l)return this;for(this.cover(c,f).cover(s,l),e=0;et||t>=i||r>n||n>=o;)switch(u=(nh||(o=c.y0)>d||(a=c.x1)=v)<<1|t>=y)&&(c=p[p.length-1],p[p.length-1]=p[p.length-1-f],p[p.length-1-f]=c)}else{var _=t-+this._x.call(null,g.data),b=n-+this._y.call(null,g.data),m=_*_+b*b;if(m=(u=(p+y)/2))?p=u:y=u,(s=a>=(c=(g+v)/2))?g=c:v=c,n=d,!(d=d[l=s<<1|f]))return this;if(!d.length)break;(n[l+1&3]||n[l+2&3]||n[l+3&3])&&(e=n,h=l)}for(;d.data!==t;)if(r=d,!(d=d.next))return this;return(i=d.next)&&delete d.next,r?(i?r.next=i:delete r.next,this):n?(i?n[l]=i:delete n[l],(d=n[0]||n[1]||n[2]||n[3])&&d===(n[3]||n[2]||n[1]||n[0])&&!d.length&&(e?e[h]=d:this._root=d),this):(this._root=i,this)},Xu.removeAll=function(t){for(var n=0,e=t.length;n1?r[0]+r.slice(2):r,+t.slice(e+1)]}function rc(t){return(t=ec(Math.abs(t)))?t[1]:NaN}var ic,oc=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function ac(t){if(!(n=oc.exec(t)))throw new Error("invalid format: "+t);var n;return new uc({fill:n[1],align:n[2],sign:n[3],symbol:n[4],zero:n[5],width:n[6],comma:n[7],precision:n[8]&&n[8].slice(1),trim:n[9],type:n[10]})}function uc(t){this.fill=void 0===t.fill?" ":t.fill+"",this.align=void 0===t.align?">":t.align+"",this.sign=void 0===t.sign?"-":t.sign+"",this.symbol=void 0===t.symbol?"":t.symbol+"",this.zero=!!t.zero,this.width=void 0===t.width?void 0:+t.width,this.comma=!!t.comma,this.precision=void 0===t.precision?void 0:+t.precision,this.trim=!!t.trim,this.type=void 0===t.type?"":t.type+""}function cc(t,n){var e=ec(t,n);if(!e)return t+"";var r=e[0],i=e[1];return i<0?"0."+new Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")}ac.prototype=uc.prototype,uc.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(void 0===this.width?"":Math.max(1,0|this.width))+(this.comma?",":"")+(void 0===this.precision?"":"."+Math.max(0,0|this.precision))+(this.trim?"~":"")+this.type};var fc={"%":(t,n)=>(100*t).toFixed(n),b:t=>Math.round(t).toString(2),c:t=>t+"",d:function(t){return Math.abs(t=Math.round(t))>=1e21?t.toLocaleString("en").replace(/,/g,""):t.toString(10)},e:(t,n)=>t.toExponential(n),f:(t,n)=>t.toFixed(n),g:(t,n)=>t.toPrecision(n),o:t=>Math.round(t).toString(8),p:(t,n)=>cc(100*t,n),r:cc,s:function(t,n){var e=ec(t,n);if(!e)return t+"";var r=e[0],i=e[1],o=i-(ic=3*Math.max(-8,Math.min(8,Math.floor(i/3))))+1,a=r.length;return o===a?r:o>a?r+new Array(o-a+1).join("0"):o>0?r.slice(0,o)+"."+r.slice(o):"0."+new Array(1-o).join("0")+ec(t,Math.max(0,n+o-1))[0]},X:t=>Math.round(t).toString(16).toUpperCase(),x:t=>Math.round(t).toString(16)};function sc(t){return t}var lc,hc=Array.prototype.map,dc=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];function pc(t){var n,e,r=void 0===t.grouping||void 0===t.thousands?sc:(n=hc.call(t.grouping,Number),e=t.thousands+"",function(t,r){for(var i=t.length,o=[],a=0,u=n[0],c=0;i>0&&u>0&&(c+u+1>r&&(u=Math.max(1,r-c)),o.push(t.substring(i-=u,i+u)),!((c+=u+1)>r));)u=n[a=(a+1)%n.length];return o.reverse().join(e)}),i=void 0===t.currency?"":t.currency[0]+"",o=void 0===t.currency?"":t.currency[1]+"",a=void 0===t.decimal?".":t.decimal+"",u=void 0===t.numerals?sc:function(t){return function(n){return n.replace(/[0-9]/g,(function(n){return t[+n]}))}}(hc.call(t.numerals,String)),c=void 0===t.percent?"%":t.percent+"",f=void 0===t.minus?"−":t.minus+"",s=void 0===t.nan?"NaN":t.nan+"";function l(t){var n=(t=ac(t)).fill,e=t.align,l=t.sign,h=t.symbol,d=t.zero,p=t.width,g=t.comma,y=t.precision,v=t.trim,_=t.type;"n"===_?(g=!0,_="g"):fc[_]||(void 0===y&&(y=12),v=!0,_="g"),(d||"0"===n&&"="===e)&&(d=!0,n="0",e="=");var b="$"===h?i:"#"===h&&/[boxX]/.test(_)?"0"+_.toLowerCase():"",m="$"===h?o:/[%p]/.test(_)?c:"",x=fc[_],w=/[defgprs%]/.test(_);function M(t){var i,o,c,h=b,M=m;if("c"===_)M=x(t)+M,t="";else{var A=(t=+t)<0||1/t<0;if(t=isNaN(t)?s:x(Math.abs(t),y),v&&(t=function(t){t:for(var n,e=t.length,r=1,i=-1;r0&&(i=0)}return i>0?t.slice(0,i)+t.slice(n+1):t}(t)),A&&0==+t&&"+"!==l&&(A=!1),h=(A?"("===l?l:f:"-"===l||"("===l?"":l)+h,M=("s"===_?dc[8+ic/3]:"")+M+(A&&"("===l?")":""),w)for(i=-1,o=t.length;++i(c=t.charCodeAt(i))||c>57){M=(46===c?a+t.slice(i+1):t.slice(i))+M,t=t.slice(0,i);break}}g&&!d&&(t=r(t,1/0));var T=h.length+t.length+M.length,S=T>1)+h+t+M+S.slice(T);break;default:t=S+h+t+M}return u(t)}return y=void 0===y?6:/[gprs]/.test(_)?Math.max(1,Math.min(21,y)):Math.max(0,Math.min(20,y)),M.toString=function(){return t+""},M}return{format:l,formatPrefix:function(t,n){var e=l(((t=ac(t)).type="f",t)),r=3*Math.max(-8,Math.min(8,Math.floor(rc(n)/3))),i=Math.pow(10,-r),o=dc[8+r/3];return function(t){return e(i*t)+o}}}}function gc(n){return lc=pc(n),t.format=lc.format,t.formatPrefix=lc.formatPrefix,lc}function yc(t){return Math.max(0,-rc(Math.abs(t)))}function vc(t,n){return Math.max(0,3*Math.max(-8,Math.min(8,Math.floor(rc(n)/3)))-rc(Math.abs(t)))}function _c(t,n){return t=Math.abs(t),n=Math.abs(n)-t,Math.max(0,rc(n)-rc(t))+1}t.format=void 0,t.formatPrefix=void 0,gc({thousands:",",grouping:[3],currency:["$",""]});var bc=1e-6,mc=1e-12,xc=Math.PI,wc=xc/2,Mc=xc/4,Ac=2*xc,Tc=180/xc,Sc=xc/180,Ec=Math.abs,kc=Math.atan,Nc=Math.atan2,Cc=Math.cos,Pc=Math.ceil,zc=Math.exp,Dc=Math.hypot,qc=Math.log,Rc=Math.pow,Fc=Math.sin,Oc=Math.sign||function(t){return t>0?1:t<0?-1:0},Ic=Math.sqrt,Uc=Math.tan;function Bc(t){return t>1?0:t<-1?xc:Math.acos(t)}function Yc(t){return t>1?wc:t<-1?-wc:Math.asin(t)}function Lc(t){return(t=Fc(t/2))*t}function jc(){}function Hc(t,n){t&&Gc.hasOwnProperty(t.type)&&Gc[t.type](t,n)}var Xc={Feature:function(t,n){Hc(t.geometry,n)},FeatureCollection:function(t,n){for(var e=t.features,r=-1,i=e.length;++r=0?1:-1,i=r*e,o=Cc(n=(n*=Sc)/2+Mc),a=Fc(n),u=tf*a,c=Jc*o+u*Cc(i),f=u*r*Fc(i);df.add(Nc(f,c)),Qc=t,Jc=o,tf=a}function mf(t){return[Nc(t[1],t[0]),Yc(t[2])]}function xf(t){var n=t[0],e=t[1],r=Cc(e);return[r*Cc(n),r*Fc(n),Fc(e)]}function wf(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]}function Mf(t,n){return[t[1]*n[2]-t[2]*n[1],t[2]*n[0]-t[0]*n[2],t[0]*n[1]-t[1]*n[0]]}function Af(t,n){t[0]+=n[0],t[1]+=n[1],t[2]+=n[2]}function Tf(t,n){return[t[0]*n,t[1]*n,t[2]*n]}function Sf(t){var n=Ic(t[0]*t[0]+t[1]*t[1]+t[2]*t[2]);t[0]/=n,t[1]/=n,t[2]/=n}var Ef,kf,Nf,Cf,Pf,zf,Df,qf,Rf,Ff,Of,If,Uf,Bf,Yf,Lf,jf={point:Hf,lineStart:Gf,lineEnd:Vf,polygonStart:function(){jf.point=$f,jf.lineStart=Wf,jf.lineEnd=Zf,sf=new g,gf.polygonStart()},polygonEnd:function(){gf.polygonEnd(),jf.point=Hf,jf.lineStart=Gf,jf.lineEnd=Vf,df<0?(nf=-(rf=180),ef=-(of=90)):sf>bc?of=90:sf<-1e-6&&(ef=-90),hf[0]=nf,hf[1]=rf},sphere:function(){nf=-(rf=180),ef=-(of=90)}};function Hf(t,n){lf.push(hf=[nf=t,rf=t]),nof&&(of=n)}function Xf(t,n){var e=xf([t*Sc,n*Sc]);if(ff){var r=Mf(ff,e),i=Mf([r[1],-r[0],0],r);Sf(i),i=mf(i);var o,a=t-af,u=a>0?1:-1,c=i[0]*Tc*u,f=Ec(a)>180;f^(u*afof&&(of=o):f^(u*af<(c=(c+360)%360-180)&&cof&&(of=n)),f?tKf(nf,rf)&&(rf=t):Kf(t,rf)>Kf(nf,rf)&&(nf=t):rf>=nf?(trf&&(rf=t)):t>af?Kf(nf,t)>Kf(nf,rf)&&(rf=t):Kf(t,rf)>Kf(nf,rf)&&(nf=t)}else lf.push(hf=[nf=t,rf=t]);nof&&(of=n),ff=e,af=t}function Gf(){jf.point=Xf}function Vf(){hf[0]=nf,hf[1]=rf,jf.point=Hf,ff=null}function $f(t,n){if(ff){var e=t-af;sf.add(Ec(e)>180?e+(e>0?360:-360):e)}else uf=t,cf=n;gf.point(t,n),Xf(t,n)}function Wf(){gf.lineStart()}function Zf(){$f(uf,cf),gf.lineEnd(),Ec(sf)>bc&&(nf=-(rf=180)),hf[0]=nf,hf[1]=rf,ff=null}function Kf(t,n){return(n-=t)<0?n+360:n}function Qf(t,n){return t[0]-n[0]}function Jf(t,n){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nxc?t+Math.round(-t/Ac)*Ac:t,n]}function ps(t,n,e){return(t%=Ac)?n||e?hs(ys(t),vs(n,e)):ys(t):n||e?vs(n,e):ds}function gs(t){return function(n,e){return[(n+=t)>xc?n-Ac:n<-xc?n+Ac:n,e]}}function ys(t){var n=gs(t);return n.invert=gs(-t),n}function vs(t,n){var e=Cc(t),r=Fc(t),i=Cc(n),o=Fc(n);function a(t,n){var a=Cc(n),u=Cc(t)*a,c=Fc(t)*a,f=Fc(n),s=f*e+u*r;return[Nc(c*i-s*o,u*e-f*r),Yc(s*i+c*o)]}return a.invert=function(t,n){var a=Cc(n),u=Cc(t)*a,c=Fc(t)*a,f=Fc(n),s=f*i-c*o;return[Nc(c*i+f*o,u*e+s*r),Yc(s*e-u*r)]},a}function _s(t){function n(n){return(n=t(n[0]*Sc,n[1]*Sc))[0]*=Tc,n[1]*=Tc,n}return t=ps(t[0]*Sc,t[1]*Sc,t.length>2?t[2]*Sc:0),n.invert=function(n){return(n=t.invert(n[0]*Sc,n[1]*Sc))[0]*=Tc,n[1]*=Tc,n},n}function bs(t,n,e,r,i,o){if(e){var a=Cc(n),u=Fc(n),c=r*e;null==i?(i=n+r*Ac,o=n-c/2):(i=ms(a,i),o=ms(a,o),(r>0?io)&&(i+=r*Ac));for(var f,s=i;r>0?s>o:s1&&n.push(n.pop().concat(n.shift()))},result:function(){var e=n;return n=[],t=null,e}}}function ws(t,n){return Ec(t[0]-n[0])=0;--o)i.point((s=f[o])[0],s[1]);else r(h.x,h.p.x,-1,i);h=h.p}f=(h=h.o).z,d=!d}while(!h.v);i.lineEnd()}}}function Ts(t){if(n=t.length){for(var n,e,r=0,i=t[0];++r=0?1:-1,E=S*T,k=E>xc,N=v*M;if(c.add(Nc(N*S*Fc(E),_*A+N*Cc(E))),a+=k?T+S*Ac:T,k^p>=e^x>=e){var C=Mf(xf(d),xf(m));Sf(C);var P=Mf(o,C);Sf(P);var z=(k^T>=0?-1:1)*Yc(P[2]);(r>z||r===z&&(C[0]||C[1]))&&(u+=k^T>=0?1:-1)}}return(a<-1e-6||a0){for(l||(i.polygonStart(),l=!0),i.lineStart(),t=0;t1&&2&c&&h.push(h.pop().concat(h.shift())),a.push(h.filter(Ns))}return h}}function Ns(t){return t.length>1}function Cs(t,n){return((t=t.x)[0]<0?t[1]-wc-bc:wc-t[1])-((n=n.x)[0]<0?n[1]-wc-bc:wc-n[1])}ds.invert=ds;var Ps=ks((function(){return!0}),(function(t){var n,e=NaN,r=NaN,i=NaN;return{lineStart:function(){t.lineStart(),n=1},point:function(o,a){var u=o>0?xc:-xc,c=Ec(o-e);Ec(c-xc)0?wc:-wc),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(u,r),t.point(o,r),n=0):i!==u&&c>=xc&&(Ec(e-i)bc?kc((Fc(n)*(o=Cc(r))*Fc(e)-Fc(r)*(i=Cc(n))*Fc(t))/(i*o*a)):(n+r)/2}(e,r,o,a),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(u,r),n=0),t.point(e=o,r=a),i=u},lineEnd:function(){t.lineEnd(),e=r=NaN},clean:function(){return 2-n}}}),(function(t,n,e,r){var i;if(null==t)i=e*wc,r.point(-xc,i),r.point(0,i),r.point(xc,i),r.point(xc,0),r.point(xc,-i),r.point(0,-i),r.point(-xc,-i),r.point(-xc,0),r.point(-xc,i);else if(Ec(t[0]-n[0])>bc){var o=t[0]0,i=Ec(n)>bc;function o(t,e){return Cc(t)*Cc(e)>n}function a(t,e,r){var i=[1,0,0],o=Mf(xf(t),xf(e)),a=wf(o,o),u=o[0],c=a-u*u;if(!c)return!r&&t;var f=n*a/c,s=-n*u/c,l=Mf(i,o),h=Tf(i,f);Af(h,Tf(o,s));var d=l,p=wf(h,d),g=wf(d,d),y=p*p-g*(wf(h,h)-1);if(!(y<0)){var v=Ic(y),_=Tf(d,(-p-v)/g);if(Af(_,h),_=mf(_),!r)return _;var b,m=t[0],x=e[0],w=t[1],M=e[1];x0^_[1]<(Ec(_[0]-m)xc^(m<=_[0]&&_[0]<=x)){var S=Tf(d,(-p+v)/g);return Af(S,h),[_,mf(S)]}}}function u(n,e){var i=r?t:xc-t,o=0;return n<-i?o|=1:n>i&&(o|=2),e<-i?o|=4:e>i&&(o|=8),o}return ks(o,(function(t){var n,e,c,f,s;return{lineStart:function(){f=c=!1,s=1},point:function(l,h){var d,p=[l,h],g=o(l,h),y=r?g?0:u(l,h):g?u(l+(l<0?xc:-xc),h):0;if(!n&&(f=c=g)&&t.lineStart(),g!==c&&(!(d=a(n,p))||ws(n,d)||ws(p,d))&&(p[2]=1),g!==c)s=0,g?(t.lineStart(),d=a(p,n),t.point(d[0],d[1])):(d=a(n,p),t.point(d[0],d[1],2),t.lineEnd()),n=d;else if(i&&n&&r^g){var v;y&e||!(v=a(p,n,!0))||(s=0,r?(t.lineStart(),t.point(v[0][0],v[0][1]),t.point(v[1][0],v[1][1]),t.lineEnd()):(t.point(v[1][0],v[1][1]),t.lineEnd(),t.lineStart(),t.point(v[0][0],v[0][1],3)))}!g||n&&ws(n,p)||t.point(p[0],p[1]),n=p,c=g,e=y},lineEnd:function(){c&&t.lineEnd(),n=null},clean:function(){return s|(f&&c)<<1}}}),(function(n,r,i,o){bs(o,t,e,i,n,r)}),r?[0,-t]:[-xc,t-xc])}var Ds,qs,Rs,Fs,Os=1e9,Is=-Os;function Us(t,n,e,r){function i(i,o){return t<=i&&i<=e&&n<=o&&o<=r}function o(i,o,u,f){var s=0,l=0;if(null==i||(s=a(i,u))!==(l=a(o,u))||c(i,o)<0^u>0)do{f.point(0===s||3===s?t:e,s>1?r:n)}while((s=(s+u+4)%4)!==l);else f.point(o[0],o[1])}function a(r,i){return Ec(r[0]-t)0?0:3:Ec(r[0]-e)0?2:1:Ec(r[1]-n)0?1:0:i>0?3:2}function u(t,n){return c(t.x,n.x)}function c(t,n){var e=a(t,1),r=a(n,1);return e!==r?e-r:0===e?n[1]-t[1]:1===e?t[0]-n[0]:2===e?t[1]-n[1]:n[0]-t[0]}return function(a){var c,f,s,l,h,d,p,g,y,v,_,b=a,m=xs(),x={point:w,lineStart:function(){x.point=M,f&&f.push(s=[]);v=!0,y=!1,p=g=NaN},lineEnd:function(){c&&(M(l,h),d&&y&&m.rejoin(),c.push(m.result()));x.point=w,y&&b.lineEnd()},polygonStart:function(){b=m,c=[],f=[],_=!0},polygonEnd:function(){var n=function(){for(var n=0,e=0,i=f.length;er&&(h-o)*(r-a)>(d-a)*(t-o)&&++n:d<=r&&(h-o)*(r-a)<(d-a)*(t-o)&&--n;return n}(),e=_&&n,i=(c=V(c)).length;(e||i)&&(a.polygonStart(),e&&(a.lineStart(),o(null,null,1,a),a.lineEnd()),i&&As(c,u,n,o,a),a.polygonEnd());b=a,c=f=s=null}};function w(t,n){i(t,n)&&b.point(t,n)}function M(o,a){var u=i(o,a);if(f&&s.push([o,a]),v)l=o,h=a,d=u,v=!1,u&&(b.lineStart(),b.point(o,a));else if(u&&y)b.point(o,a);else{var c=[p=Math.max(Is,Math.min(Os,p)),g=Math.max(Is,Math.min(Os,g))],m=[o=Math.max(Is,Math.min(Os,o)),a=Math.max(Is,Math.min(Os,a))];!function(t,n,e,r,i,o){var a,u=t[0],c=t[1],f=0,s=1,l=n[0]-u,h=n[1]-c;if(a=e-u,l||!(a>0)){if(a/=l,l<0){if(a0){if(a>s)return;a>f&&(f=a)}if(a=i-u,l||!(a<0)){if(a/=l,l<0){if(a>s)return;a>f&&(f=a)}else if(l>0){if(a0)){if(a/=h,h<0){if(a0){if(a>s)return;a>f&&(f=a)}if(a=o-c,h||!(a<0)){if(a/=h,h<0){if(a>s)return;a>f&&(f=a)}else if(h>0){if(a0&&(t[0]=u+f*l,t[1]=c+f*h),s<1&&(n[0]=u+s*l,n[1]=c+s*h),!0}}}}}(c,m,t,n,e,r)?u&&(b.lineStart(),b.point(o,a),_=!1):(y||(b.lineStart(),b.point(c[0],c[1])),b.point(m[0],m[1]),u||b.lineEnd(),_=!1)}p=o,g=a,y=u}return x}}var Bs={sphere:jc,point:jc,lineStart:function(){Bs.point=Ls,Bs.lineEnd=Ys},lineEnd:jc,polygonStart:jc,polygonEnd:jc};function Ys(){Bs.point=Bs.lineEnd=jc}function Ls(t,n){qs=t*=Sc,Rs=Fc(n*=Sc),Fs=Cc(n),Bs.point=js}function js(t,n){t*=Sc;var e=Fc(n*=Sc),r=Cc(n),i=Ec(t-qs),o=Cc(i),a=r*Fc(i),u=Fs*e-Rs*r*o,c=Rs*e+Fs*r*o;Ds.add(Nc(Ic(a*a+u*u),c)),qs=t,Rs=e,Fs=r}function Hs(t){return Ds=new g,Wc(t,Bs),+Ds}var Xs=[null,null],Gs={type:"LineString",coordinates:Xs};function Vs(t,n){return Xs[0]=t,Xs[1]=n,Hs(Gs)}var $s={Feature:function(t,n){return Zs(t.geometry,n)},FeatureCollection:function(t,n){for(var e=t.features,r=-1,i=e.length;++r0&&(i=Vs(t[o],t[o-1]))>0&&e<=i&&r<=i&&(e+r-i)*(1-Math.pow((e-r)/i,2))bc})).map(c)).concat(Z(Pc(o/d)*d,i,d).filter((function(t){return Ec(t%g)>bc})).map(f))}return v.lines=function(){return _().map((function(t){return{type:"LineString",coordinates:t}}))},v.outline=function(){return{type:"Polygon",coordinates:[s(r).concat(l(a).slice(1),s(e).reverse().slice(1),l(u).reverse().slice(1))]}},v.extent=function(t){return arguments.length?v.extentMajor(t).extentMinor(t):v.extentMinor()},v.extentMajor=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],u=+t[0][1],a=+t[1][1],r>e&&(t=r,r=e,e=t),u>a&&(t=u,u=a,a=t),v.precision(y)):[[r,u],[e,a]]},v.extentMinor=function(e){return arguments.length?(n=+e[0][0],t=+e[1][0],o=+e[0][1],i=+e[1][1],n>t&&(e=n,n=t,t=e),o>i&&(e=o,o=i,i=e),v.precision(y)):[[n,o],[t,i]]},v.step=function(t){return arguments.length?v.stepMajor(t).stepMinor(t):v.stepMinor()},v.stepMajor=function(t){return arguments.length?(p=+t[0],g=+t[1],v):[p,g]},v.stepMinor=function(t){return arguments.length?(h=+t[0],d=+t[1],v):[h,d]},v.precision=function(h){return arguments.length?(y=+h,c=el(o,i,90),f=rl(n,t,y),s=el(u,a,90),l=rl(r,e,y),v):y},v.extentMajor([[-180,-89.999999],[180,89.999999]]).extentMinor([[-180,-80.000001],[180,80.000001]])}var ol,al,ul,cl,fl=t=>t,sl=new g,ll=new g,hl={point:jc,lineStart:jc,lineEnd:jc,polygonStart:function(){hl.lineStart=dl,hl.lineEnd=yl},polygonEnd:function(){hl.lineStart=hl.lineEnd=hl.point=jc,sl.add(Ec(ll)),ll=new g},result:function(){var t=sl/2;return sl=new g,t}};function dl(){hl.point=pl}function pl(t,n){hl.point=gl,ol=ul=t,al=cl=n}function gl(t,n){ll.add(cl*t-ul*n),ul=t,cl=n}function yl(){gl(ol,al)}var vl=1/0,_l=vl,bl=-vl,ml=bl,xl={point:function(t,n){tbl&&(bl=t);n<_l&&(_l=n);n>ml&&(ml=n)},lineStart:jc,lineEnd:jc,polygonStart:jc,polygonEnd:jc,result:function(){var t=[[vl,_l],[bl,ml]];return bl=ml=-(_l=vl=1/0),t}};var wl,Ml,Al,Tl,Sl=0,El=0,kl=0,Nl=0,Cl=0,Pl=0,zl=0,Dl=0,ql=0,Rl={point:Fl,lineStart:Ol,lineEnd:Bl,polygonStart:function(){Rl.lineStart=Yl,Rl.lineEnd=Ll},polygonEnd:function(){Rl.point=Fl,Rl.lineStart=Ol,Rl.lineEnd=Bl},result:function(){var t=ql?[zl/ql,Dl/ql]:Pl?[Nl/Pl,Cl/Pl]:kl?[Sl/kl,El/kl]:[NaN,NaN];return Sl=El=kl=Nl=Cl=Pl=zl=Dl=ql=0,t}};function Fl(t,n){Sl+=t,El+=n,++kl}function Ol(){Rl.point=Il}function Il(t,n){Rl.point=Ul,Fl(Al=t,Tl=n)}function Ul(t,n){var e=t-Al,r=n-Tl,i=Ic(e*e+r*r);Nl+=i*(Al+t)/2,Cl+=i*(Tl+n)/2,Pl+=i,Fl(Al=t,Tl=n)}function Bl(){Rl.point=Fl}function Yl(){Rl.point=jl}function Ll(){Hl(wl,Ml)}function jl(t,n){Rl.point=Hl,Fl(wl=Al=t,Ml=Tl=n)}function Hl(t,n){var e=t-Al,r=n-Tl,i=Ic(e*e+r*r);Nl+=i*(Al+t)/2,Cl+=i*(Tl+n)/2,Pl+=i,zl+=(i=Tl*t-Al*n)*(Al+t),Dl+=i*(Tl+n),ql+=3*i,Fl(Al=t,Tl=n)}function Xl(t){this._context=t}Xl.prototype={_radius:4.5,pointRadius:function(t){return this._radius=t,this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._context.closePath(),this._point=NaN},point:function(t,n){switch(this._point){case 0:this._context.moveTo(t,n),this._point=1;break;case 1:this._context.lineTo(t,n);break;default:this._context.moveTo(t+this._radius,n),this._context.arc(t,n,this._radius,0,Ac)}},result:jc};var Gl,Vl,$l,Wl,Zl,Kl=new g,Ql={point:jc,lineStart:function(){Ql.point=Jl},lineEnd:function(){Gl&&th(Vl,$l),Ql.point=jc},polygonStart:function(){Gl=!0},polygonEnd:function(){Gl=null},result:function(){var t=+Kl;return Kl=new g,t}};function Jl(t,n){Ql.point=th,Vl=Wl=t,$l=Zl=n}function th(t,n){Wl-=t,Zl-=n,Kl.add(Ic(Wl*Wl+Zl*Zl)),Wl=t,Zl=n}function nh(){this._string=[]}function eh(t){return"m0,"+t+"a"+t+","+t+" 0 1,1 0,"+-2*t+"a"+t+","+t+" 0 1,1 0,"+2*t+"z"}function rh(t){return function(n){var e=new ih;for(var r in t)e[r]=t[r];return e.stream=n,e}}function ih(){}function oh(t,n,e){var r=t.clipExtent&&t.clipExtent();return t.scale(150).translate([0,0]),null!=r&&t.clipExtent(null),Wc(e,t.stream(xl)),n(xl.result()),null!=r&&t.clipExtent(r),t}function ah(t,n,e){return oh(t,(function(e){var r=n[1][0]-n[0][0],i=n[1][1]-n[0][1],o=Math.min(r/(e[1][0]-e[0][0]),i/(e[1][1]-e[0][1])),a=+n[0][0]+(r-o*(e[1][0]+e[0][0]))/2,u=+n[0][1]+(i-o*(e[1][1]+e[0][1]))/2;t.scale(150*o).translate([a,u])}),e)}function uh(t,n,e){return ah(t,[[0,0],n],e)}function ch(t,n,e){return oh(t,(function(e){var r=+n,i=r/(e[1][0]-e[0][0]),o=(r-i*(e[1][0]+e[0][0]))/2,a=-i*e[0][1];t.scale(150*i).translate([o,a])}),e)}function fh(t,n,e){return oh(t,(function(e){var r=+n,i=r/(e[1][1]-e[0][1]),o=-i*e[0][0],a=(r-i*(e[1][1]+e[0][1]))/2;t.scale(150*i).translate([o,a])}),e)}nh.prototype={_radius:4.5,_circle:eh(4.5),pointRadius:function(t){return(t=+t)!==this._radius&&(this._radius=t,this._circle=null),this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._string.push("Z"),this._point=NaN},point:function(t,n){switch(this._point){case 0:this._string.push("M",t,",",n),this._point=1;break;case 1:this._string.push("L",t,",",n);break;default:null==this._circle&&(this._circle=eh(this._radius)),this._string.push("M",t,",",n,this._circle)}},result:function(){if(this._string.length){var t=this._string.join("");return this._string=[],t}return null}},ih.prototype={constructor:ih,point:function(t,n){this.stream.point(t,n)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}};var sh=Cc(30*Sc);function lh(t,n){return+n?function(t,n){function e(r,i,o,a,u,c,f,s,l,h,d,p,g,y){var v=f-r,_=s-i,b=v*v+_*_;if(b>4*n&&g--){var m=a+h,x=u+d,w=c+p,M=Ic(m*m+x*x+w*w),A=Yc(w/=M),T=Ec(Ec(w)-1)n||Ec((v*N+_*C)/b-.5)>.3||a*h+u*d+c*p2?t[2]%360*Sc:0,N()):[y*Tc,v*Tc,_*Tc]},E.angle=function(t){return arguments.length?(b=t%360*Sc,N()):b*Tc},E.reflectX=function(t){return arguments.length?(m=t?-1:1,N()):m<0},E.reflectY=function(t){return arguments.length?(x=t?-1:1,N()):x<0},E.precision=function(t){return arguments.length?(a=lh(u,S=t*t),C()):Ic(S)},E.fitExtent=function(t,n){return ah(E,t,n)},E.fitSize=function(t,n){return uh(E,t,n)},E.fitWidth=function(t,n){return ch(E,t,n)},E.fitHeight=function(t,n){return fh(E,t,n)},function(){return n=t.apply(this,arguments),E.invert=n.invert&&k,N()}}function yh(t){var n=0,e=xc/3,r=gh(t),i=r(n,e);return i.parallels=function(t){return arguments.length?r(n=t[0]*Sc,e=t[1]*Sc):[n*Tc,e*Tc]},i}function vh(t,n){var e=Fc(t),r=(e+Fc(n))/2;if(Ec(r)0?n<-wc+bc&&(n=-wc+bc):n>wc-bc&&(n=wc-bc);var e=i/Rc(Sh(n),r);return[e*Fc(r*t),i-e*Cc(r*t)]}return o.invert=function(t,n){var e=i-n,o=Oc(r)*Ic(t*t+e*e),a=Nc(t,Ec(e))*Oc(e);return e*r<0&&(a-=xc*Oc(t)*Oc(e)),[a/r,2*kc(Rc(i/o,1/r))-wc]},o}function kh(t,n){return[t,n]}function Nh(t,n){var e=Cc(t),r=t===n?Fc(t):(e-Cc(n))/(n-t),i=e/r+t;if(Ec(r)=0;)n+=e[r].value;else n=1;t.value=n}function Xh(t,n){t instanceof Map?(t=[void 0,t],void 0===n&&(n=Vh)):void 0===n&&(n=Gh);for(var e,r,i,o,a,u=new Zh(t),c=[u];e=c.pop();)if((i=n(e.data))&&(a=(i=Array.from(i)).length))for(e.children=i,o=a-1;o>=0;--o)c.push(r=i[o]=new Zh(i[o])),r.parent=e,r.depth=e.depth+1;return u.eachBefore(Wh)}function Gh(t){return t.children}function Vh(t){return Array.isArray(t)?t[1]:null}function $h(t){void 0!==t.data.value&&(t.value=t.data.value),t.data=t.data.data}function Wh(t){var n=0;do{t.height=n}while((t=t.parent)&&t.height<++n)}function Zh(t){this.data=t,this.depth=this.height=0,this.parent=null}function Kh(t){for(var n,e,r=0,i=(t=function(t){for(var n,e,r=t.length;r;)e=Math.random()*r--|0,n=t[r],t[r]=t[e],t[e]=n;return t}(Array.from(t))).length,o=[];r0&&e*e>r*r+i*i}function nd(t,n){for(var e=0;e(a*=a)?(r=(f+a-i)/(2*f),o=Math.sqrt(Math.max(0,a/f-r*r)),e.x=t.x-r*u-o*c,e.y=t.y-r*c+o*u):(r=(f+i-a)/(2*f),o=Math.sqrt(Math.max(0,i/f-r*r)),e.x=n.x+r*u-o*c,e.y=n.y+r*c+o*u)):(e.x=n.x+e.r,e.y=n.y)}function ad(t,n){var e=t.r+n.r-1e-6,r=n.x-t.x,i=n.y-t.y;return e>0&&e*e>r*r+i*i}function ud(t){var n=t._,e=t.next._,r=n.r+e.r,i=(n.x*e.r+e.x*n.r)/r,o=(n.y*e.r+e.y*n.r)/r;return i*i+o*o}function cd(t){this._=t,this.next=null,this.previous=null}function fd(t){if(!(i=(t=function(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}(t)).length))return 0;var n,e,r,i,o,a,u,c,f,s,l;if((n=t[0]).x=0,n.y=0,!(i>1))return n.r;if(e=t[1],n.x=-e.r,e.x=n.r,e.y=0,!(i>2))return n.r+e.r;od(e,n,r=t[2]),n=new cd(n),e=new cd(e),r=new cd(r),n.next=r.previous=e,e.next=n.previous=r,r.next=e.previous=n;t:for(u=3;ubc&&--i>0);return[t/(.8707+(o=r*r)*(o*(o*o*o*(.003971-.001529*o)-.013791)-.131979)),r]},Ih.invert=xh(Yc),Uh.invert=xh((function(t){return 2*kc(t)})),Bh.invert=function(t,n){return[-n,2*kc(zc(t))-wc]},Zh.prototype=Xh.prototype={constructor:Zh,count:function(){return this.eachAfter(Hh)},each:function(t,n){let e=-1;for(const r of this)t.call(n,r,++e,this);return this},eachAfter:function(t,n){for(var e,r,i,o=this,a=[o],u=[],c=-1;o=a.pop();)if(u.push(o),e=o.children)for(r=0,i=e.length;r=0;--r)o.push(e[r]);return this},find:function(t,n){let e=-1;for(const r of this)if(t.call(n,r,++e,this))return r},sum:function(t){return this.eachAfter((function(n){for(var e=+t(n.data)||0,r=n.children,i=r&&r.length;--i>=0;)e+=r[i].value;n.value=e}))},sort:function(t){return this.eachBefore((function(n){n.children&&n.children.sort(t)}))},path:function(t){for(var n=this,e=function(t,n){if(t===n)return t;var e=t.ancestors(),r=n.ancestors(),i=null;t=e.pop(),n=r.pop();for(;t===n;)i=t,t=e.pop(),n=r.pop();return i}(n,t),r=[n];n!==e;)n=n.parent,r.push(n);for(var i=r.length;t!==e;)r.splice(i,0,t),t=t.parent;return r},ancestors:function(){for(var t=this,n=[t];t=t.parent;)n.push(t);return n},descendants:function(){return Array.from(this)},leaves:function(){var t=[];return this.eachBefore((function(n){n.children||t.push(n)})),t},links:function(){var t=this,n=[];return t.each((function(e){e!==t&&n.push({source:e.parent,target:e})})),n},copy:function(){return Xh(this).eachBefore($h)},[Symbol.iterator]:function*(){var t,n,e,r,i=this,o=[i];do{for(t=o.reverse(),o=[];i=t.pop();)if(yield i,n=i.children)for(e=0,r=n.length;eh&&(h=u),y=s*s*g,(d=Math.max(h/y,y/l))>p){s-=u;break}p=d}v.push(a={value:s,dice:c1?n:1)},e}(Pd);var qd=function t(n){function e(t,e,r,i,o){if((a=t._squarify)&&a.ratio===n)for(var a,u,c,f,s,l=-1,h=a.length,d=t.value;++l1?n:1)},e}(Pd);function Rd(t,n,e){return(n[0]-t[0])*(e[1]-t[1])-(n[1]-t[1])*(e[0]-t[0])}function Fd(t,n){return t[0]-n[0]||t[1]-n[1]}function Od(t){const n=t.length,e=[0,1];let r,i=2;for(r=2;r1&&Rd(t[e[i-2]],t[e[i-1]],t[r])<=0;)--i;e[i++]=r}return e.slice(0,i)}var Id=Math.random,Ud=function t(n){function e(t,e){return t=null==t?0:+t,e=null==e?1:+e,1===arguments.length?(e=t,t=0):e-=t,function(){return n()*e+t}}return e.source=t,e}(Id),Bd=function t(n){function e(t,e){return arguments.length<2&&(e=t,t=0),t=Math.floor(t),e=Math.floor(e)-t,function(){return Math.floor(n()*e+t)}}return e.source=t,e}(Id),Yd=function t(n){function e(t,e){var r,i;return t=null==t?0:+t,e=null==e?1:+e,function(){var o;if(null!=r)o=r,r=null;else do{r=2*n()-1,o=2*n()-1,i=r*r+o*o}while(!i||i>1);return t+e*o*Math.sqrt(-2*Math.log(i)/i)}}return e.source=t,e}(Id),Ld=function t(n){var e=Yd.source(n);function r(){var t=e.apply(this,arguments);return function(){return Math.exp(t())}}return r.source=t,r}(Id),jd=function t(n){function e(t){return(t=+t)<=0?()=>0:function(){for(var e=0,r=t;r>1;--r)e+=n();return e+r*n()}}return e.source=t,e}(Id),Hd=function t(n){var e=jd.source(n);function r(t){if(0==(t=+t))return n;var r=e(t);return function(){return r()/t}}return r.source=t,r}(Id),Xd=function t(n){function e(t){return function(){return-Math.log1p(-n())/t}}return e.source=t,e}(Id),Gd=function t(n){function e(t){if((t=+t)<0)throw new RangeError("invalid alpha");return t=1/-t,function(){return Math.pow(1-n(),t)}}return e.source=t,e}(Id),Vd=function t(n){function e(t){if((t=+t)<0||t>1)throw new RangeError("invalid p");return function(){return Math.floor(n()+t)}}return e.source=t,e}(Id),$d=function t(n){function e(t){if((t=+t)<0||t>1)throw new RangeError("invalid p");return 0===t?()=>1/0:1===t?()=>1:(t=Math.log1p(-t),function(){return 1+Math.floor(Math.log1p(-n())/t)})}return e.source=t,e}(Id),Wd=function t(n){var e=Yd.source(n)();function r(t,r){if((t=+t)<0)throw new RangeError("invalid k");if(0===t)return()=>0;if(r=null==r?1:+r,1===t)return()=>-Math.log1p(-n())*r;var i=(t<1?t+1:t)-1/3,o=1/(3*Math.sqrt(i)),a=t<1?()=>Math.pow(n(),1/t):()=>1;return function(){do{do{var t=e(),u=1+o*t}while(u<=0);u*=u*u;var c=1-n()}while(c>=1-.0331*t*t*t*t&&Math.log(c)>=.5*t*t+i*(1-u+Math.log(u)));return i*u*a()*r}}return r.source=t,r}(Id),Zd=function t(n){var e=Wd.source(n);function r(t,n){var r=e(t),i=e(n);return function(){var t=r();return 0===t?0:t/(t+i())}}return r.source=t,r}(Id),Kd=function t(n){var e=$d.source(n),r=Zd.source(n);function i(t,n){return t=+t,(n=+n)>=1?()=>t:n<=0?()=>0:function(){for(var i=0,o=t,a=n;o*a>16&&o*(1-a)>16;){var u=Math.floor((o+1)*a),c=r(u,o-u+1)();c<=a?(i+=u,o-=u,a=(a-c)/(1-c)):(o=u-1,a/=c)}for(var f=a<.5,s=e(f?a:1-a),l=s(),h=0;l<=o;++h)l+=s();return i+(f?h:o-h)}}return i.source=t,i}(Id),Qd=function t(n){function e(t,e,r){var i;return 0==(t=+t)?i=t=>-Math.log(t):(t=1/t,i=n=>Math.pow(n,t)),e=null==e?0:+e,r=null==r?1:+r,function(){return e+r*i(-Math.log1p(-n()))}}return e.source=t,e}(Id),Jd=function t(n){function e(t,e){return t=null==t?0:+t,e=null==e?1:+e,function(){return t+e*Math.tan(Math.PI*n())}}return e.source=t,e}(Id),tp=function t(n){function e(t,e){return t=null==t?0:+t,e=null==e?1:+e,function(){var r=n();return t+e*Math.log(r/(1-r))}}return e.source=t,e}(Id),np=function t(n){var e=Wd.source(n),r=Kd.source(n);function i(t){return function(){for(var i=0,o=t;o>16;){var a=Math.floor(.875*o),u=e(a)();if(u>o)return i+r(a-1,o/u)();i+=a,o-=u}for(var c=-Math.log1p(-n()),f=0;c<=o;++f)c-=Math.log1p(-n());return i+f}}return i.source=t,i}(Id);const ep=1/4294967296;function rp(t,n){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(n).domain(t)}return this}function ip(t,n){switch(arguments.length){case 0:break;case 1:"function"==typeof t?this.interpolator(t):this.range(t);break;default:this.domain(t),"function"==typeof n?this.interpolator(n):this.range(n)}return this}const op=Symbol("implicit");function ap(){var t=new Map,n=[],e=[],r=op;function i(i){var o=i+"",a=t.get(o);if(!a){if(r!==op)return r;t.set(o,a=n.push(i))}return e[(a-1)%e.length]}return i.domain=function(e){if(!arguments.length)return n.slice();n=[],t=new Map;for(const r of e){const e=r+"";t.has(e)||t.set(e,n.push(r))}return i},i.range=function(t){return arguments.length?(e=Array.from(t),i):e.slice()},i.unknown=function(t){return arguments.length?(r=t,i):r},i.copy=function(){return ap(n,e).unknown(r)},rp.apply(i,arguments),i}function up(){var t,n,e=ap().unknown(void 0),r=e.domain,i=e.range,o=0,a=1,u=!1,c=0,f=0,s=.5;function l(){var e=r().length,l=an&&(e=t,t=n,n=e),function(e){return Math.max(t,Math.min(n,e))}}(a[0],a[t-1])),r=t>2?pp:dp,i=o=null,l}function l(n){return null==n||isNaN(n=+n)?e:(i||(i=r(a.map(t),u,c)))(t(f(n)))}return l.invert=function(e){return f(n((o||(o=r(u,a.map(t),_r)))(e)))},l.domain=function(t){return arguments.length?(a=Array.from(t,fp),s()):a.slice()},l.range=function(t){return arguments.length?(u=Array.from(t),s()):u.slice()},l.rangeRound=function(t){return u=Array.from(t),c=Ar,s()},l.clamp=function(t){return arguments.length?(f=!!t||lp,s()):f!==lp},l.interpolate=function(t){return arguments.length?(c=t,s()):c},l.unknown=function(t){return arguments.length?(e=t,l):e},function(e,r){return t=e,n=r,s()}}function vp(){return yp()(lp,lp)}function _p(n,e,r,i){var o,a=F(n,e,r);switch((i=ac(null==i?",f":i)).type){case"s":var u=Math.max(Math.abs(n),Math.abs(e));return null!=i.precision||isNaN(o=vc(a,u))||(i.precision=o),t.formatPrefix(i,u);case"":case"e":case"g":case"p":case"r":null!=i.precision||isNaN(o=_c(a,Math.max(Math.abs(n),Math.abs(e))))||(i.precision=o-("e"===i.type));break;case"f":case"%":null!=i.precision||isNaN(o=yc(a))||(i.precision=o-2*("%"===i.type))}return t.format(i)}function bp(t){var n=t.domain;return t.ticks=function(t){var e=n();return q(e[0],e[e.length-1],null==t?10:t)},t.tickFormat=function(t,e){var r=n();return _p(r[0],r[r.length-1],null==t?10:t,e)},t.nice=function(e){null==e&&(e=10);var r,i,o=n(),a=0,u=o.length-1,c=o[a],f=o[u],s=10;for(f0;){if((i=R(c,f,e))===r)return o[a]=c,o[u]=f,n(o);if(i>0)c=Math.floor(c/i)*i,f=Math.ceil(f/i)*i;else{if(!(i<0))break;c=Math.ceil(c*i)/i,f=Math.floor(f*i)/i}r=i}return t},t}function mp(t,n){var e,r=0,i=(t=t.slice()).length-1,o=t[r],a=t[i];return a0){for(;h<=d;++h)for(s=1,f=r(h);sc)break;g.push(l)}}else for(;h<=d;++h)for(s=a-1,f=r(h);s>=1;--s)if(!((l=f*s)c)break;g.push(l)}2*g.length0))return u;do{u.push(a=new Date(+e)),n(e,o),t(e)}while(a=n)for(;t(n),!e(n);)n.setTime(n-1)}),(function(t,r){if(t>=t)if(r<0)for(;++r<=0;)for(;n(t,-1),!e(t););else for(;--r>=0;)for(;n(t,1),!e(t););}))},e&&(i.count=function(n,r){return Ip.setTime(+n),Up.setTime(+r),t(Ip),t(Up),Math.floor(e(Ip,Up))},i.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?i.filter(r?function(n){return r(n)%t==0}:function(n){return i.count(0,n)%t==0}):i:null}),i}var Yp=Bp((function(){}),(function(t,n){t.setTime(+t+n)}),(function(t,n){return n-t}));Yp.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?Bp((function(n){n.setTime(Math.floor(n/t)*t)}),(function(n,e){n.setTime(+n+e*t)}),(function(n,e){return(e-n)/t})):Yp:null};var Lp=Yp.range;const jp=1e3,Hp=6e4,Xp=36e5,Gp=864e5,Vp=6048e5,$p=2592e6,Wp=31536e6;var Zp=Bp((function(t){t.setTime(t-t.getMilliseconds())}),(function(t,n){t.setTime(+t+n*jp)}),(function(t,n){return(n-t)/jp}),(function(t){return t.getUTCSeconds()})),Kp=Zp.range,Qp=Bp((function(t){t.setTime(t-t.getMilliseconds()-t.getSeconds()*jp)}),(function(t,n){t.setTime(+t+n*Hp)}),(function(t,n){return(n-t)/Hp}),(function(t){return t.getMinutes()})),Jp=Qp.range,tg=Bp((function(t){t.setTime(t-t.getMilliseconds()-t.getSeconds()*jp-t.getMinutes()*Hp)}),(function(t,n){t.setTime(+t+n*Xp)}),(function(t,n){return(n-t)/Xp}),(function(t){return t.getHours()})),ng=tg.range,eg=Bp((t=>t.setHours(0,0,0,0)),((t,n)=>t.setDate(t.getDate()+n)),((t,n)=>(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*Hp)/Gp),(t=>t.getDate()-1)),rg=eg.range;function ig(t){return Bp((function(n){n.setDate(n.getDate()-(n.getDay()+7-t)%7),n.setHours(0,0,0,0)}),(function(t,n){t.setDate(t.getDate()+7*n)}),(function(t,n){return(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*Hp)/Vp}))}var og=ig(0),ag=ig(1),ug=ig(2),cg=ig(3),fg=ig(4),sg=ig(5),lg=ig(6),hg=og.range,dg=ag.range,pg=ug.range,gg=cg.range,yg=fg.range,vg=sg.range,_g=lg.range,bg=Bp((function(t){t.setDate(1),t.setHours(0,0,0,0)}),(function(t,n){t.setMonth(t.getMonth()+n)}),(function(t,n){return n.getMonth()-t.getMonth()+12*(n.getFullYear()-t.getFullYear())}),(function(t){return t.getMonth()})),mg=bg.range,xg=Bp((function(t){t.setMonth(0,1),t.setHours(0,0,0,0)}),(function(t,n){t.setFullYear(t.getFullYear()+n)}),(function(t,n){return n.getFullYear()-t.getFullYear()}),(function(t){return t.getFullYear()}));xg.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Bp((function(n){n.setFullYear(Math.floor(n.getFullYear()/t)*t),n.setMonth(0,1),n.setHours(0,0,0,0)}),(function(n,e){n.setFullYear(n.getFullYear()+e*t)})):null};var wg=xg.range,Mg=Bp((function(t){t.setUTCSeconds(0,0)}),(function(t,n){t.setTime(+t+n*Hp)}),(function(t,n){return(n-t)/Hp}),(function(t){return t.getUTCMinutes()})),Ag=Mg.range,Tg=Bp((function(t){t.setUTCMinutes(0,0,0)}),(function(t,n){t.setTime(+t+n*Xp)}),(function(t,n){return(n-t)/Xp}),(function(t){return t.getUTCHours()})),Sg=Tg.range,Eg=Bp((function(t){t.setUTCHours(0,0,0,0)}),(function(t,n){t.setUTCDate(t.getUTCDate()+n)}),(function(t,n){return(n-t)/Gp}),(function(t){return t.getUTCDate()-1})),kg=Eg.range;function Ng(t){return Bp((function(n){n.setUTCDate(n.getUTCDate()-(n.getUTCDay()+7-t)%7),n.setUTCHours(0,0,0,0)}),(function(t,n){t.setUTCDate(t.getUTCDate()+7*n)}),(function(t,n){return(n-t)/Vp}))}var Cg=Ng(0),Pg=Ng(1),zg=Ng(2),Dg=Ng(3),qg=Ng(4),Rg=Ng(5),Fg=Ng(6),Og=Cg.range,Ig=Pg.range,Ug=zg.range,Bg=Dg.range,Yg=qg.range,Lg=Rg.range,jg=Fg.range,Hg=Bp((function(t){t.setUTCDate(1),t.setUTCHours(0,0,0,0)}),(function(t,n){t.setUTCMonth(t.getUTCMonth()+n)}),(function(t,n){return n.getUTCMonth()-t.getUTCMonth()+12*(n.getUTCFullYear()-t.getUTCFullYear())}),(function(t){return t.getUTCMonth()})),Xg=Hg.range,Gg=Bp((function(t){t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)}),(function(t,n){t.setUTCFullYear(t.getUTCFullYear()+n)}),(function(t,n){return n.getUTCFullYear()-t.getUTCFullYear()}),(function(t){return t.getUTCFullYear()}));Gg.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Bp((function(n){n.setUTCFullYear(Math.floor(n.getUTCFullYear()/t)*t),n.setUTCMonth(0,1),n.setUTCHours(0,0,0,0)}),(function(n,e){n.setUTCFullYear(n.getUTCFullYear()+e*t)})):null};var Vg=Gg.range;function $g(t,n,r,i,o,a){const u=[[Zp,1,jp],[Zp,5,5e3],[Zp,15,15e3],[Zp,30,3e4],[a,1,Hp],[a,5,3e5],[a,15,9e5],[a,30,18e5],[o,1,Xp],[o,3,108e5],[o,6,216e5],[o,12,432e5],[i,1,Gp],[i,2,1728e5],[r,1,Vp],[n,1,$p],[n,3,7776e6],[t,1,Wp]];function c(n,r,i){const o=Math.abs(r-n)/i,a=e((([,,t])=>t)).right(u,o);if(a===u.length)return t.every(F(n/Wp,r/Wp,i));if(0===a)return Yp.every(Math.max(F(n,r,i),1));const[c,f]=u[o/u[a-1][2]=12)]},q:function(t){return 1+~~(t.getMonth()/3)},Q:bv,s:mv,S:By,u:Yy,U:Ly,V:Hy,w:Xy,W:Gy,x:null,X:null,y:Vy,Y:Wy,Z:Ky,"%":_v},m={a:function(t){return a[t.getUTCDay()]},A:function(t){return o[t.getUTCDay()]},b:function(t){return c[t.getUTCMonth()]},B:function(t){return u[t.getUTCMonth()]},c:null,d:Qy,e:Qy,f:rv,g:pv,G:yv,H:Jy,I:tv,j:nv,L:ev,m:iv,M:ov,p:function(t){return i[+(t.getUTCHours()>=12)]},q:function(t){return 1+~~(t.getUTCMonth()/3)},Q:bv,s:mv,S:av,u:uv,U:cv,V:sv,w:lv,W:hv,x:null,X:null,y:dv,Y:gv,Z:vv,"%":_v},x={a:function(t,n,e){var r=d.exec(n.slice(e));return r?(t.w=p.get(r[0].toLowerCase()),e+r[0].length):-1},A:function(t,n,e){var r=l.exec(n.slice(e));return r?(t.w=h.get(r[0].toLowerCase()),e+r[0].length):-1},b:function(t,n,e){var r=v.exec(n.slice(e));return r?(t.m=_.get(r[0].toLowerCase()),e+r[0].length):-1},B:function(t,n,e){var r=g.exec(n.slice(e));return r?(t.m=y.get(r[0].toLowerCase()),e+r[0].length):-1},c:function(t,e,r){return A(t,n,e,r)},d:wy,e:wy,f:ky,g:_y,G:vy,H:Ay,I:Ay,j:My,L:Ey,m:xy,M:Ty,p:function(t,n,e){var r=f.exec(n.slice(e));return r?(t.p=s.get(r[0].toLowerCase()),e+r[0].length):-1},q:my,Q:Cy,s:Py,S:Sy,u:dy,U:py,V:gy,w:hy,W:yy,x:function(t,n,r){return A(t,e,n,r)},X:function(t,n,e){return A(t,r,n,e)},y:_y,Y:vy,Z:by,"%":Ny};function w(t,n){return function(e){var r,i,o,a=[],u=-1,c=0,f=t.length;for(e instanceof Date||(e=new Date(+e));++u53)return null;"w"in o||(o.w=1),"Z"in o?(i=(r=ty(ny(o.y,0,1))).getUTCDay(),r=i>4||0===i?Pg.ceil(r):Pg(r),r=Eg.offset(r,7*(o.V-1)),o.y=r.getUTCFullYear(),o.m=r.getUTCMonth(),o.d=r.getUTCDate()+(o.w+6)%7):(i=(r=Jg(ny(o.y,0,1))).getDay(),r=i>4||0===i?ag.ceil(r):ag(r),r=eg.offset(r,7*(o.V-1)),o.y=r.getFullYear(),o.m=r.getMonth(),o.d=r.getDate()+(o.w+6)%7)}else("W"in o||"U"in o)&&("w"in o||(o.w="u"in o?o.u%7:"W"in o?1:0),i="Z"in o?ty(ny(o.y,0,1)).getUTCDay():Jg(ny(o.y,0,1)).getDay(),o.m=0,o.d="W"in o?(o.w+6)%7+7*o.W-(i+5)%7:o.w+7*o.U-(i+6)%7);return"Z"in o?(o.H+=o.Z/100|0,o.M+=o.Z%100,ty(o)):Jg(o)}}function A(t,n,e,r){for(var i,o,a=0,u=n.length,c=e.length;a=c)return-1;if(37===(i=n.charCodeAt(a++))){if(i=n.charAt(a++),!(o=x[i in iy?n.charAt(a++):i])||(r=o(t,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}return b.x=w(e,b),b.X=w(r,b),b.c=w(n,b),m.x=w(e,m),m.X=w(r,m),m.c=w(n,m),{format:function(t){var n=w(t+="",b);return n.toString=function(){return t},n},parse:function(t){var n=M(t+="",!1);return n.toString=function(){return t},n},utcFormat:function(t){var n=w(t+="",m);return n.toString=function(){return t},n},utcParse:function(t){var n=M(t+="",!0);return n.toString=function(){return t},n}}}var ry,iy={"-":"",_:" ",0:"0"},oy=/^\s*\d+/,ay=/^%/,uy=/[\\^$*+?|[\]().{}]/g;function cy(t,n,e){var r=t<0?"-":"",i=(r?-t:t)+"",o=i.length;return r+(o[t.toLowerCase(),n])))}function hy(t,n,e){var r=oy.exec(n.slice(e,e+1));return r?(t.w=+r[0],e+r[0].length):-1}function dy(t,n,e){var r=oy.exec(n.slice(e,e+1));return r?(t.u=+r[0],e+r[0].length):-1}function py(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.U=+r[0],e+r[0].length):-1}function gy(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.V=+r[0],e+r[0].length):-1}function yy(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.W=+r[0],e+r[0].length):-1}function vy(t,n,e){var r=oy.exec(n.slice(e,e+4));return r?(t.y=+r[0],e+r[0].length):-1}function _y(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.y=+r[0]+(+r[0]>68?1900:2e3),e+r[0].length):-1}function by(t,n,e){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(n.slice(e,e+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),e+r[0].length):-1}function my(t,n,e){var r=oy.exec(n.slice(e,e+1));return r?(t.q=3*r[0]-3,e+r[0].length):-1}function xy(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.m=r[0]-1,e+r[0].length):-1}function wy(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.d=+r[0],e+r[0].length):-1}function My(t,n,e){var r=oy.exec(n.slice(e,e+3));return r?(t.m=0,t.d=+r[0],e+r[0].length):-1}function Ay(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.H=+r[0],e+r[0].length):-1}function Ty(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.M=+r[0],e+r[0].length):-1}function Sy(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.S=+r[0],e+r[0].length):-1}function Ey(t,n,e){var r=oy.exec(n.slice(e,e+3));return r?(t.L=+r[0],e+r[0].length):-1}function ky(t,n,e){var r=oy.exec(n.slice(e,e+6));return r?(t.L=Math.floor(r[0]/1e3),e+r[0].length):-1}function Ny(t,n,e){var r=ay.exec(n.slice(e,e+1));return r?e+r[0].length:-1}function Cy(t,n,e){var r=oy.exec(n.slice(e));return r?(t.Q=+r[0],e+r[0].length):-1}function Py(t,n,e){var r=oy.exec(n.slice(e));return r?(t.s=+r[0],e+r[0].length):-1}function zy(t,n){return cy(t.getDate(),n,2)}function Dy(t,n){return cy(t.getHours(),n,2)}function qy(t,n){return cy(t.getHours()%12||12,n,2)}function Ry(t,n){return cy(1+eg.count(xg(t),t),n,3)}function Fy(t,n){return cy(t.getMilliseconds(),n,3)}function Oy(t,n){return Fy(t,n)+"000"}function Iy(t,n){return cy(t.getMonth()+1,n,2)}function Uy(t,n){return cy(t.getMinutes(),n,2)}function By(t,n){return cy(t.getSeconds(),n,2)}function Yy(t){var n=t.getDay();return 0===n?7:n}function Ly(t,n){return cy(og.count(xg(t)-1,t),n,2)}function jy(t){var n=t.getDay();return n>=4||0===n?fg(t):fg.ceil(t)}function Hy(t,n){return t=jy(t),cy(fg.count(xg(t),t)+(4===xg(t).getDay()),n,2)}function Xy(t){return t.getDay()}function Gy(t,n){return cy(ag.count(xg(t)-1,t),n,2)}function Vy(t,n){return cy(t.getFullYear()%100,n,2)}function $y(t,n){return cy((t=jy(t)).getFullYear()%100,n,2)}function Wy(t,n){return cy(t.getFullYear()%1e4,n,4)}function Zy(t,n){var e=t.getDay();return cy((t=e>=4||0===e?fg(t):fg.ceil(t)).getFullYear()%1e4,n,4)}function Ky(t){var n=t.getTimezoneOffset();return(n>0?"-":(n*=-1,"+"))+cy(n/60|0,"0",2)+cy(n%60,"0",2)}function Qy(t,n){return cy(t.getUTCDate(),n,2)}function Jy(t,n){return cy(t.getUTCHours(),n,2)}function tv(t,n){return cy(t.getUTCHours()%12||12,n,2)}function nv(t,n){return cy(1+Eg.count(Gg(t),t),n,3)}function ev(t,n){return cy(t.getUTCMilliseconds(),n,3)}function rv(t,n){return ev(t,n)+"000"}function iv(t,n){return cy(t.getUTCMonth()+1,n,2)}function ov(t,n){return cy(t.getUTCMinutes(),n,2)}function av(t,n){return cy(t.getUTCSeconds(),n,2)}function uv(t){var n=t.getUTCDay();return 0===n?7:n}function cv(t,n){return cy(Cg.count(Gg(t)-1,t),n,2)}function fv(t){var n=t.getUTCDay();return n>=4||0===n?qg(t):qg.ceil(t)}function sv(t,n){return t=fv(t),cy(qg.count(Gg(t),t)+(4===Gg(t).getUTCDay()),n,2)}function lv(t){return t.getUTCDay()}function hv(t,n){return cy(Pg.count(Gg(t)-1,t),n,2)}function dv(t,n){return cy(t.getUTCFullYear()%100,n,2)}function pv(t,n){return cy((t=fv(t)).getUTCFullYear()%100,n,2)}function gv(t,n){return cy(t.getUTCFullYear()%1e4,n,4)}function yv(t,n){var e=t.getUTCDay();return cy((t=e>=4||0===e?qg(t):qg.ceil(t)).getUTCFullYear()%1e4,n,4)}function vv(){return"+0000"}function _v(){return"%"}function bv(t){return+t}function mv(t){return Math.floor(+t/1e3)}function xv(n){return ry=ey(n),t.timeFormat=ry.format,t.timeParse=ry.parse,t.utcFormat=ry.utcFormat,t.utcParse=ry.utcParse,ry}t.timeFormat=void 0,t.timeParse=void 0,t.utcFormat=void 0,t.utcParse=void 0,xv({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});var wv="%Y-%m-%dT%H:%M:%S.%LZ";var Mv=Date.prototype.toISOString?function(t){return t.toISOString()}:t.utcFormat(wv);var Av=+new Date("2000-01-01T00:00:00.000Z")?function(t){var n=new Date(t);return isNaN(n)?null:n}:t.utcParse(wv);function Tv(t){return new Date(t)}function Sv(t){return t instanceof Date?+t:+new Date(+t)}function Ev(t,n,e,r,i,o,a,u,c,f){var s=vp(),l=s.invert,h=s.domain,d=f(".%L"),p=f(":%S"),g=f("%I:%M"),y=f("%I %p"),v=f("%a %d"),_=f("%b %d"),b=f("%B"),m=f("%Y");function x(t){return(c(t)hr(t[t.length-1]),Xv=new Array(3).concat("d8b365f5f5f55ab4ac","a6611adfc27d80cdc1018571","a6611adfc27df5f5f580cdc1018571","8c510ad8b365f6e8c3c7eae55ab4ac01665e","8c510ad8b365f6e8c3f5f5f5c7eae55ab4ac01665e","8c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e","8c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e","5430058c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e003c30","5430058c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e003c30").map(Dv),Gv=Hv(Xv),Vv=new Array(3).concat("af8dc3f7f7f77fbf7b","7b3294c2a5cfa6dba0008837","7b3294c2a5cff7f7f7a6dba0008837","762a83af8dc3e7d4e8d9f0d37fbf7b1b7837","762a83af8dc3e7d4e8f7f7f7d9f0d37fbf7b1b7837","762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b7837","762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b7837","40004b762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b783700441b","40004b762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b783700441b").map(Dv),$v=Hv(Vv),Wv=new Array(3).concat("e9a3c9f7f7f7a1d76a","d01c8bf1b6dab8e1864dac26","d01c8bf1b6daf7f7f7b8e1864dac26","c51b7de9a3c9fde0efe6f5d0a1d76a4d9221","c51b7de9a3c9fde0eff7f7f7e6f5d0a1d76a4d9221","c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221","c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221","8e0152c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221276419","8e0152c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221276419").map(Dv),Zv=Hv(Wv),Kv=new Array(3).concat("998ec3f7f7f7f1a340","5e3c99b2abd2fdb863e66101","5e3c99b2abd2f7f7f7fdb863e66101","542788998ec3d8daebfee0b6f1a340b35806","542788998ec3d8daebf7f7f7fee0b6f1a340b35806","5427888073acb2abd2d8daebfee0b6fdb863e08214b35806","5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b35806","2d004b5427888073acb2abd2d8daebfee0b6fdb863e08214b358067f3b08","2d004b5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b358067f3b08").map(Dv),Qv=Hv(Kv),Jv=new Array(3).concat("ef8a62f7f7f767a9cf","ca0020f4a58292c5de0571b0","ca0020f4a582f7f7f792c5de0571b0","b2182bef8a62fddbc7d1e5f067a9cf2166ac","b2182bef8a62fddbc7f7f7f7d1e5f067a9cf2166ac","b2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac","b2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac","67001fb2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac053061","67001fb2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac053061").map(Dv),t_=Hv(Jv),n_=new Array(3).concat("ef8a62ffffff999999","ca0020f4a582bababa404040","ca0020f4a582ffffffbababa404040","b2182bef8a62fddbc7e0e0e09999994d4d4d","b2182bef8a62fddbc7ffffffe0e0e09999994d4d4d","b2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d","b2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d","67001fb2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d1a1a1a","67001fb2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d1a1a1a").map(Dv),e_=Hv(n_),r_=new Array(3).concat("fc8d59ffffbf91bfdb","d7191cfdae61abd9e92c7bb6","d7191cfdae61ffffbfabd9e92c7bb6","d73027fc8d59fee090e0f3f891bfdb4575b4","d73027fc8d59fee090ffffbfe0f3f891bfdb4575b4","d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4","d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4","a50026d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4313695","a50026d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4313695").map(Dv),i_=Hv(r_),o_=new Array(3).concat("fc8d59ffffbf91cf60","d7191cfdae61a6d96a1a9641","d7191cfdae61ffffbfa6d96a1a9641","d73027fc8d59fee08bd9ef8b91cf601a9850","d73027fc8d59fee08bffffbfd9ef8b91cf601a9850","d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850","d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850","a50026d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850006837","a50026d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850006837").map(Dv),a_=Hv(o_),u_=new Array(3).concat("fc8d59ffffbf99d594","d7191cfdae61abdda42b83ba","d7191cfdae61ffffbfabdda42b83ba","d53e4ffc8d59fee08be6f59899d5943288bd","d53e4ffc8d59fee08bffffbfe6f59899d5943288bd","d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd","d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd","9e0142d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd5e4fa2","9e0142d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd5e4fa2").map(Dv),c_=Hv(u_),f_=new Array(3).concat("e5f5f999d8c92ca25f","edf8fbb2e2e266c2a4238b45","edf8fbb2e2e266c2a42ca25f006d2c","edf8fbccece699d8c966c2a42ca25f006d2c","edf8fbccece699d8c966c2a441ae76238b45005824","f7fcfde5f5f9ccece699d8c966c2a441ae76238b45005824","f7fcfde5f5f9ccece699d8c966c2a441ae76238b45006d2c00441b").map(Dv),s_=Hv(f_),l_=new Array(3).concat("e0ecf49ebcda8856a7","edf8fbb3cde38c96c688419d","edf8fbb3cde38c96c68856a7810f7c","edf8fbbfd3e69ebcda8c96c68856a7810f7c","edf8fbbfd3e69ebcda8c96c68c6bb188419d6e016b","f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d6e016b","f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d810f7c4d004b").map(Dv),h_=Hv(l_),d_=new Array(3).concat("e0f3dba8ddb543a2ca","f0f9e8bae4bc7bccc42b8cbe","f0f9e8bae4bc7bccc443a2ca0868ac","f0f9e8ccebc5a8ddb57bccc443a2ca0868ac","f0f9e8ccebc5a8ddb57bccc44eb3d32b8cbe08589e","f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe08589e","f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe0868ac084081").map(Dv),p_=Hv(d_),g_=new Array(3).concat("fee8c8fdbb84e34a33","fef0d9fdcc8afc8d59d7301f","fef0d9fdcc8afc8d59e34a33b30000","fef0d9fdd49efdbb84fc8d59e34a33b30000","fef0d9fdd49efdbb84fc8d59ef6548d7301f990000","fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301f990000","fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301fb300007f0000").map(Dv),y_=Hv(g_),v_=new Array(3).concat("ece2f0a6bddb1c9099","f6eff7bdc9e167a9cf02818a","f6eff7bdc9e167a9cf1c9099016c59","f6eff7d0d1e6a6bddb67a9cf1c9099016c59","f6eff7d0d1e6a6bddb67a9cf3690c002818a016450","fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016450","fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016c59014636").map(Dv),__=Hv(v_),b_=new Array(3).concat("ece7f2a6bddb2b8cbe","f1eef6bdc9e174a9cf0570b0","f1eef6bdc9e174a9cf2b8cbe045a8d","f1eef6d0d1e6a6bddb74a9cf2b8cbe045a8d","f1eef6d0d1e6a6bddb74a9cf3690c00570b0034e7b","fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0034e7b","fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0045a8d023858").map(Dv),m_=Hv(b_),x_=new Array(3).concat("e7e1efc994c7dd1c77","f1eef6d7b5d8df65b0ce1256","f1eef6d7b5d8df65b0dd1c77980043","f1eef6d4b9dac994c7df65b0dd1c77980043","f1eef6d4b9dac994c7df65b0e7298ace125691003f","f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125691003f","f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125698004367001f").map(Dv),w_=Hv(x_),M_=new Array(3).concat("fde0ddfa9fb5c51b8a","feebe2fbb4b9f768a1ae017e","feebe2fbb4b9f768a1c51b8a7a0177","feebe2fcc5c0fa9fb5f768a1c51b8a7a0177","feebe2fcc5c0fa9fb5f768a1dd3497ae017e7a0177","fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a0177","fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a017749006a").map(Dv),A_=Hv(M_),T_=new Array(3).concat("edf8b17fcdbb2c7fb8","ffffcca1dab441b6c4225ea8","ffffcca1dab441b6c42c7fb8253494","ffffccc7e9b47fcdbb41b6c42c7fb8253494","ffffccc7e9b47fcdbb41b6c41d91c0225ea80c2c84","ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea80c2c84","ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea8253494081d58").map(Dv),S_=Hv(T_),E_=new Array(3).concat("f7fcb9addd8e31a354","ffffccc2e69978c679238443","ffffccc2e69978c67931a354006837","ffffccd9f0a3addd8e78c67931a354006837","ffffccd9f0a3addd8e78c67941ab5d238443005a32","ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443005a32","ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443006837004529").map(Dv),k_=Hv(E_),N_=new Array(3).concat("fff7bcfec44fd95f0e","ffffd4fed98efe9929cc4c02","ffffd4fed98efe9929d95f0e993404","ffffd4fee391fec44ffe9929d95f0e993404","ffffd4fee391fec44ffe9929ec7014cc4c028c2d04","ffffe5fff7bcfee391fec44ffe9929ec7014cc4c028c2d04","ffffe5fff7bcfee391fec44ffe9929ec7014cc4c02993404662506").map(Dv),C_=Hv(N_),P_=new Array(3).concat("ffeda0feb24cf03b20","ffffb2fecc5cfd8d3ce31a1c","ffffb2fecc5cfd8d3cf03b20bd0026","ffffb2fed976feb24cfd8d3cf03b20bd0026","ffffb2fed976feb24cfd8d3cfc4e2ae31a1cb10026","ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cb10026","ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cbd0026800026").map(Dv),z_=Hv(P_),D_=new Array(3).concat("deebf79ecae13182bd","eff3ffbdd7e76baed62171b5","eff3ffbdd7e76baed63182bd08519c","eff3ffc6dbef9ecae16baed63182bd08519c","eff3ffc6dbef9ecae16baed64292c62171b5084594","f7fbffdeebf7c6dbef9ecae16baed64292c62171b5084594","f7fbffdeebf7c6dbef9ecae16baed64292c62171b508519c08306b").map(Dv),q_=Hv(D_),R_=new Array(3).concat("e5f5e0a1d99b31a354","edf8e9bae4b374c476238b45","edf8e9bae4b374c47631a354006d2c","edf8e9c7e9c0a1d99b74c47631a354006d2c","edf8e9c7e9c0a1d99b74c47641ab5d238b45005a32","f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45005a32","f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45006d2c00441b").map(Dv),F_=Hv(R_),O_=new Array(3).concat("f0f0f0bdbdbd636363","f7f7f7cccccc969696525252","f7f7f7cccccc969696636363252525","f7f7f7d9d9d9bdbdbd969696636363252525","f7f7f7d9d9d9bdbdbd969696737373525252252525","fffffff0f0f0d9d9d9bdbdbd969696737373525252252525","fffffff0f0f0d9d9d9bdbdbd969696737373525252252525000000").map(Dv),I_=Hv(O_),U_=new Array(3).concat("efedf5bcbddc756bb1","f2f0f7cbc9e29e9ac86a51a3","f2f0f7cbc9e29e9ac8756bb154278f","f2f0f7dadaebbcbddc9e9ac8756bb154278f","f2f0f7dadaebbcbddc9e9ac8807dba6a51a34a1486","fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a34a1486","fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a354278f3f007d").map(Dv),B_=Hv(U_),Y_=new Array(3).concat("fee0d2fc9272de2d26","fee5d9fcae91fb6a4acb181d","fee5d9fcae91fb6a4ade2d26a50f15","fee5d9fcbba1fc9272fb6a4ade2d26a50f15","fee5d9fcbba1fc9272fb6a4aef3b2ccb181d99000d","fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181d99000d","fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181da50f1567000d").map(Dv),L_=Hv(Y_),j_=new Array(3).concat("fee6cefdae6be6550d","feeddefdbe85fd8d3cd94701","feeddefdbe85fd8d3ce6550da63603","feeddefdd0a2fdae6bfd8d3ce6550da63603","feeddefdd0a2fdae6bfd8d3cf16913d948018c2d04","fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d948018c2d04","fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d94801a636037f2704").map(Dv),H_=Hv(j_);var X_=Lr(tr(300,.5,0),tr(-240,.5,1)),G_=Lr(tr(-100,.75,.35),tr(80,1.5,.8)),V_=Lr(tr(260,.75,.35),tr(80,1.5,.8)),$_=tr();var W_=ve(),Z_=Math.PI/3,K_=2*Math.PI/3;function Q_(t){var n=t.length;return function(e){return t[Math.max(0,Math.min(n-1,Math.floor(e*n)))]}}var J_=Q_(Dv("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725")),tb=Q_(Dv("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf")),nb=Q_(Dv("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4")),eb=Q_(Dv("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921"));function rb(t){return function(){return t}}var ib=Math.abs,ob=Math.atan2,ab=Math.cos,ub=Math.max,cb=Math.min,fb=Math.sin,sb=Math.sqrt,lb=1e-12,hb=Math.PI,db=hb/2,pb=2*hb;function gb(t){return t>1?0:t<-1?hb:Math.acos(t)}function yb(t){return t>=1?db:t<=-1?-db:Math.asin(t)}function vb(t){return t.innerRadius}function _b(t){return t.outerRadius}function bb(t){return t.startAngle}function mb(t){return t.endAngle}function xb(t){return t&&t.padAngle}function wb(t,n,e,r,i,o,a,u){var c=e-t,f=r-n,s=a-i,l=u-o,h=l*c-s*f;if(!(h*hC*C+P*P&&(A=S,T=E),{cx:A,cy:T,x01:-s,y01:-l,x11:A*(i/x-1),y11:T*(i/x-1)}}var Ab=Array.prototype.slice;function Tb(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}function Sb(t){this._context=t}function Eb(t){return new Sb(t)}function kb(t){return t[0]}function Nb(t){return t[1]}function Cb(t,n){var e=rb(!0),r=null,i=Eb,o=null;function a(a){var u,c,f,s=(a=Tb(a)).length,l=!1;for(null==r&&(o=i(f=fa())),u=0;u<=s;++u)!(u=s;--l)u.point(y[l],v[l]);u.lineEnd(),u.areaEnd()}g&&(y[f]=+t(h,f,c),v[f]=+n(h,f,c),u.point(r?+r(h,f,c):y[f],e?+e(h,f,c):v[f]))}if(d)return u=null,d+""||null}function f(){return Cb().defined(i).curve(a).context(o)}return t="function"==typeof t?t:void 0===t?kb:rb(+t),n="function"==typeof n?n:rb(void 0===n?0:+n),e="function"==typeof e?e:void 0===e?Nb:rb(+e),c.x=function(n){return arguments.length?(t="function"==typeof n?n:rb(+n),r=null,c):t},c.x0=function(n){return arguments.length?(t="function"==typeof n?n:rb(+n),c):t},c.x1=function(t){return arguments.length?(r=null==t?null:"function"==typeof t?t:rb(+t),c):r},c.y=function(t){return arguments.length?(n="function"==typeof t?t:rb(+t),e=null,c):n},c.y0=function(t){return arguments.length?(n="function"==typeof t?t:rb(+t),c):n},c.y1=function(t){return arguments.length?(e=null==t?null:"function"==typeof t?t:rb(+t),c):e},c.lineX0=c.lineY0=function(){return f().x(t).y(n)},c.lineY1=function(){return f().x(t).y(e)},c.lineX1=function(){return f().x(r).y(n)},c.defined=function(t){return arguments.length?(i="function"==typeof t?t:rb(!!t),c):i},c.curve=function(t){return arguments.length?(a=t,null!=o&&(u=a(o)),c):a},c.context=function(t){return arguments.length?(null==t?o=u=null:u=a(o=t),c):o},c}function zb(t,n){return nt?1:n>=t?0:NaN}function Db(t){return t}Sb.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:this._context.lineTo(t,n)}}};var qb=Fb(Eb);function Rb(t){this._curve=t}function Fb(t){function n(n){return new Rb(t(n))}return n._curve=t,n}function Ob(t){var n=t.curve;return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t.curve=function(t){return arguments.length?n(Fb(t)):n()._curve},t}function Ib(){return Ob(Cb().curve(qb))}function Ub(){var t=Pb().curve(qb),n=t.curve,e=t.lineX0,r=t.lineX1,i=t.lineY0,o=t.lineY1;return t.angle=t.x,delete t.x,t.startAngle=t.x0,delete t.x0,t.endAngle=t.x1,delete t.x1,t.radius=t.y,delete t.y,t.innerRadius=t.y0,delete t.y0,t.outerRadius=t.y1,delete t.y1,t.lineStartAngle=function(){return Ob(e())},delete t.lineX0,t.lineEndAngle=function(){return Ob(r())},delete t.lineX1,t.lineInnerRadius=function(){return Ob(i())},delete t.lineY0,t.lineOuterRadius=function(){return Ob(o())},delete t.lineY1,t.curve=function(t){return arguments.length?n(Fb(t)):n()._curve},t}function Bb(t,n){return[(n=+n)*Math.cos(t-=Math.PI/2),n*Math.sin(t)]}function Yb(t){return t.source}function Lb(t){return t.target}function jb(t){var n=Yb,e=Lb,r=kb,i=Nb,o=null;function a(){var a,u=Ab.call(arguments),c=n.apply(this,u),f=e.apply(this,u);if(o||(o=a=fa()),t(o,+r.apply(this,(u[0]=c,u)),+i.apply(this,u),+r.apply(this,(u[0]=f,u)),+i.apply(this,u)),a)return o=null,a+""||null}return a.source=function(t){return arguments.length?(n=t,a):n},a.target=function(t){return arguments.length?(e=t,a):e},a.x=function(t){return arguments.length?(r="function"==typeof t?t:rb(+t),a):r},a.y=function(t){return arguments.length?(i="function"==typeof t?t:rb(+t),a):i},a.context=function(t){return arguments.length?(o=null==t?null:t,a):o},a}function Hb(t,n,e,r,i){t.moveTo(n,e),t.bezierCurveTo(n=(n+r)/2,e,n,i,r,i)}function Xb(t,n,e,r,i){t.moveTo(n,e),t.bezierCurveTo(n,e=(e+i)/2,r,e,r,i)}function Gb(t,n,e,r,i){var o=Bb(n,e),a=Bb(n,e=(e+i)/2),u=Bb(r,e),c=Bb(r,i);t.moveTo(o[0],o[1]),t.bezierCurveTo(a[0],a[1],u[0],u[1],c[0],c[1])}Rb.prototype={areaStart:function(){this._curve.areaStart()},areaEnd:function(){this._curve.areaEnd()},lineStart:function(){this._curve.lineStart()},lineEnd:function(){this._curve.lineEnd()},point:function(t,n){this._curve.point(n*Math.sin(t),n*-Math.cos(t))}};var Vb={draw:function(t,n){var e=Math.sqrt(n/hb);t.moveTo(e,0),t.arc(0,0,e,0,pb)}},$b={draw:function(t,n){var e=Math.sqrt(n/5)/2;t.moveTo(-3*e,-e),t.lineTo(-e,-e),t.lineTo(-e,-3*e),t.lineTo(e,-3*e),t.lineTo(e,-e),t.lineTo(3*e,-e),t.lineTo(3*e,e),t.lineTo(e,e),t.lineTo(e,3*e),t.lineTo(-e,3*e),t.lineTo(-e,e),t.lineTo(-3*e,e),t.closePath()}},Wb=Math.sqrt(1/3),Zb=2*Wb,Kb={draw:function(t,n){var e=Math.sqrt(n/Zb),r=e*Wb;t.moveTo(0,-e),t.lineTo(r,0),t.lineTo(0,e),t.lineTo(-r,0),t.closePath()}},Qb=Math.sin(hb/10)/Math.sin(7*hb/10),Jb=Math.sin(pb/10)*Qb,tm=-Math.cos(pb/10)*Qb,nm={draw:function(t,n){var e=Math.sqrt(.8908130915292852*n),r=Jb*e,i=tm*e;t.moveTo(0,-e),t.lineTo(r,i);for(var o=1;o<5;++o){var a=pb*o/5,u=Math.cos(a),c=Math.sin(a);t.lineTo(c*e,-u*e),t.lineTo(u*r-c*i,c*r+u*i)}t.closePath()}},em={draw:function(t,n){var e=Math.sqrt(n),r=-e/2;t.rect(r,r,e,e)}},rm=Math.sqrt(3),im={draw:function(t,n){var e=-Math.sqrt(n/(3*rm));t.moveTo(0,2*e),t.lineTo(-rm*e,-e),t.lineTo(rm*e,-e),t.closePath()}},om=-.5,am=Math.sqrt(3)/2,um=1/Math.sqrt(12),cm=3*(um/2+1),fm={draw:function(t,n){var e=Math.sqrt(n/cm),r=e/2,i=e*um,o=r,a=e*um+e,u=-o,c=a;t.moveTo(r,i),t.lineTo(o,a),t.lineTo(u,c),t.lineTo(om*r-am*i,am*r+om*i),t.lineTo(om*o-am*a,am*o+om*a),t.lineTo(om*u-am*c,am*u+om*c),t.lineTo(om*r+am*i,om*i-am*r),t.lineTo(om*o+am*a,om*a-am*o),t.lineTo(om*u+am*c,om*c-am*u),t.closePath()}},sm=[Vb,$b,Kb,em,nm,im,fm];function lm(){}function hm(t,n,e){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+n)/6,(t._y0+4*t._y1+e)/6)}function dm(t){this._context=t}function pm(t){this._context=t}function gm(t){this._context=t}dm.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:hm(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:hm(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},pm.prototype={areaStart:lm,areaEnd:lm,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x2,this._y2),this._context.closePath();break;case 2:this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break;case 3:this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x2=t,this._y2=n;break;case 1:this._point=2,this._x3=t,this._y3=n;break;case 2:this._point=3,this._x4=t,this._y4=n,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+n)/6);break;default:hm(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},gm.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var e=(this._x0+4*this._x1+t)/6,r=(this._y0+4*this._y1+n)/6;this._line?this._context.lineTo(e,r):this._context.moveTo(e,r);break;case 3:this._point=4;default:hm(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}};class ym{constructor(t,n){this._context=t,this._x=n}areaStart(){this._line=0}areaEnd(){this._line=NaN}lineStart(){this._point=0}lineEnd(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line}point(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:this._x?this._context.bezierCurveTo(this._x0=(this._x0+t)/2,this._y0,this._x0,n,t,n):this._context.bezierCurveTo(this._x0,this._y0=(this._y0+n)/2,t,this._y0,t,n)}this._x0=t,this._y0=n}}function vm(t,n){this._basis=new dm(t),this._beta=n}vm.prototype={lineStart:function(){this._x=[],this._y=[],this._basis.lineStart()},lineEnd:function(){var t=this._x,n=this._y,e=t.length-1;if(e>0)for(var r,i=t[0],o=n[0],a=t[e]-i,u=n[e]-o,c=-1;++c<=e;)r=c/e,this._basis.point(this._beta*t[c]+(1-this._beta)*(i+r*a),this._beta*n[c]+(1-this._beta)*(o+r*u));this._x=this._y=null,this._basis.lineEnd()},point:function(t,n){this._x.push(+t),this._y.push(+n)}};var _m=function t(n){function e(t){return 1===n?new dm(t):new vm(t,n)}return e.beta=function(n){return t(+n)},e}(.85);function bm(t,n,e){t._context.bezierCurveTo(t._x1+t._k*(t._x2-t._x0),t._y1+t._k*(t._y2-t._y0),t._x2+t._k*(t._x1-n),t._y2+t._k*(t._y1-e),t._x2,t._y2)}function mm(t,n){this._context=t,this._k=(1-n)/6}mm.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:bm(this,this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2,this._x1=t,this._y1=n;break;case 2:this._point=3;default:bm(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var xm=function t(n){function e(t){return new mm(t,n)}return e.tension=function(n){return t(+n)},e}(0);function wm(t,n){this._context=t,this._k=(1-n)/6}wm.prototype={areaStart:lm,areaEnd:lm,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:bm(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Mm=function t(n){function e(t){return new wm(t,n)}return e.tension=function(n){return t(+n)},e}(0);function Am(t,n){this._context=t,this._k=(1-n)/6}Am.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:bm(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Tm=function t(n){function e(t){return new Am(t,n)}return e.tension=function(n){return t(+n)},e}(0);function Sm(t,n,e){var r=t._x1,i=t._y1,o=t._x2,a=t._y2;if(t._l01_a>lb){var u=2*t._l01_2a+3*t._l01_a*t._l12_a+t._l12_2a,c=3*t._l01_a*(t._l01_a+t._l12_a);r=(r*u-t._x0*t._l12_2a+t._x2*t._l01_2a)/c,i=(i*u-t._y0*t._l12_2a+t._y2*t._l01_2a)/c}if(t._l23_a>lb){var f=2*t._l23_2a+3*t._l23_a*t._l12_a+t._l12_2a,s=3*t._l23_a*(t._l23_a+t._l12_a);o=(o*f+t._x1*t._l23_2a-n*t._l12_2a)/s,a=(a*f+t._y1*t._l23_2a-e*t._l12_2a)/s}t._context.bezierCurveTo(r,i,o,a,t._x2,t._y2)}function Em(t,n){this._context=t,this._alpha=n}Em.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3;default:Sm(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var km=function t(n){function e(t){return n?new Em(t,n):new mm(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);function Nm(t,n){this._context=t,this._alpha=n}Nm.prototype={areaStart:lm,areaEnd:lm,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:Sm(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Cm=function t(n){function e(t){return n?new Nm(t,n):new wm(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);function Pm(t,n){this._context=t,this._alpha=n}Pm.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Sm(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var zm=function t(n){function e(t){return n?new Pm(t,n):new Am(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);function Dm(t){this._context=t}function qm(t){return t<0?-1:1}function Rm(t,n,e){var r=t._x1-t._x0,i=n-t._x1,o=(t._y1-t._y0)/(r||i<0&&-0),a=(e-t._y1)/(i||r<0&&-0),u=(o*i+a*r)/(r+i);return(qm(o)+qm(a))*Math.min(Math.abs(o),Math.abs(a),.5*Math.abs(u))||0}function Fm(t,n){var e=t._x1-t._x0;return e?(3*(t._y1-t._y0)/e-n)/2:n}function Om(t,n,e){var r=t._x0,i=t._y0,o=t._x1,a=t._y1,u=(o-r)/3;t._context.bezierCurveTo(r+u,i+u*n,o-u,a-u*e,o,a)}function Im(t){this._context=t}function Um(t){this._context=new Bm(t)}function Bm(t){this._context=t}function Ym(t){this._context=t}function Lm(t){var n,e,r=t.length-1,i=new Array(r),o=new Array(r),a=new Array(r);for(i[0]=0,o[0]=2,a[0]=t[0]+2*t[1],n=1;n=0;--n)i[n]=(a[n]-i[n+1])/o[n];for(o[r-1]=(t[r]+i[r-1])/2,n=0;n1)for(var e,r,i,o=1,a=t[n[0]],u=a.length;o=0;)e[n]=n;return e}function Gm(t,n){return t[n]}function Vm(t){const n=[];return n.key=t,n}function $m(t){var n=t.map(Wm);return Xm(t).sort((function(t,e){return n[t]-n[e]}))}function Wm(t){for(var n,e=-1,r=0,i=t.length,o=-1/0;++eo&&(o=n,r=e);return r}function Zm(t){var n=t.map(Km);return Xm(t).sort((function(t,e){return n[t]-n[e]}))}function Km(t){for(var n,e=0,r=-1,i=t.length;++r=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:if(this._t<=0)this._context.lineTo(this._x,n),this._context.lineTo(t,n);else{var e=this._x*(1-this._t)+t*this._t;this._context.lineTo(e,this._y),this._context.lineTo(e,n)}}this._x=t,this._y=n}};var Qm=t=>()=>t;function Jm(t,{sourceEvent:n,target:e,transform:r,dispatch:i}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:n,enumerable:!0,configurable:!0},target:{value:e,enumerable:!0,configurable:!0},transform:{value:r,enumerable:!0,configurable:!0},_:{value:i}})}function tx(t,n,e){this.k=t,this.x=n,this.y=e}tx.prototype={constructor:tx,scale:function(t){return 1===t?this:new tx(this.k*t,this.x,this.y)},translate:function(t,n){return 0===t&0===n?this:new tx(this.k,this.x+this.k*t,this.y+this.k*n)},apply:function(t){return[t[0]*this.k+this.x,t[1]*this.k+this.y]},applyX:function(t){return t*this.k+this.x},applyY:function(t){return t*this.k+this.y},invert:function(t){return[(t[0]-this.x)/this.k,(t[1]-this.y)/this.k]},invertX:function(t){return(t-this.x)/this.k},invertY:function(t){return(t-this.y)/this.k},rescaleX:function(t){return t.copy().domain(t.range().map(this.invertX,this).map(t.invert,t))},rescaleY:function(t){return t.copy().domain(t.range().map(this.invertY,this).map(t.invert,t))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}};var nx=new tx(1,0,0);function ex(t){for(;!t.__zoom;)if(!(t=t.parentNode))return nx;return t.__zoom}function rx(t){t.stopImmediatePropagation()}function ix(t){t.preventDefault(),t.stopImmediatePropagation()}function ox(t){return!(t.ctrlKey&&"wheel"!==t.type||t.button)}function ax(){var t=this;return t instanceof SVGElement?(t=t.ownerSVGElement||t).hasAttribute("viewBox")?[[(t=t.viewBox.baseVal).x,t.y],[t.x+t.width,t.y+t.height]]:[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]:[[0,0],[t.clientWidth,t.clientHeight]]}function ux(){return this.__zoom||nx}function cx(t){return-t.deltaY*(1===t.deltaMode?.05:t.deltaMode?1:.002)*(t.ctrlKey?10:1)}function fx(){return navigator.maxTouchPoints||"ontouchstart"in this}function sx(t,n,e){var r=t.invertX(n[0][0])-e[0][0],i=t.invertX(n[1][0])-e[1][0],o=t.invertY(n[0][1])-e[0][1],a=t.invertY(n[1][1])-e[1][1];return t.translate(i>r?(r+i)/2:Math.min(0,r)||Math.max(0,i),a>o?(o+a)/2:Math.min(0,o)||Math.max(0,a))}ex.prototype=tx.prototype,t.Adder=g,t.Delaunay=nu,t.FormatSpecifier=uc,t.InternMap=y,t.InternSet=v,t.Voronoi=Wa,t.active=function(t,n){var e,r,i=t.__transition;if(i)for(r in n=null==n?null:n+"",i)if((e=i[r]).state>1&&e.name===n)return new ji([[t]],_o,n,+r);return null},t.arc=function(){var t=vb,n=_b,e=rb(0),r=null,i=bb,o=mb,a=xb,u=null;function c(){var c,f,s=+t.apply(this,arguments),l=+n.apply(this,arguments),h=i.apply(this,arguments)-db,d=o.apply(this,arguments)-db,p=ib(d-h),g=d>h;if(u||(u=c=fa()),llb)if(p>pb-lb)u.moveTo(l*ab(h),l*fb(h)),u.arc(0,0,l,h,d,!g),s>lb&&(u.moveTo(s*ab(d),s*fb(d)),u.arc(0,0,s,d,h,g));else{var y,v,_=h,b=d,m=h,x=d,w=p,M=p,A=a.apply(this,arguments)/2,T=A>lb&&(r?+r.apply(this,arguments):sb(s*s+l*l)),S=cb(ib(l-s)/2,+e.apply(this,arguments)),E=S,k=S;if(T>lb){var N=yb(T/s*fb(A)),C=yb(T/l*fb(A));(w-=2*N)>lb?(m+=N*=g?1:-1,x-=N):(w=0,m=x=(h+d)/2),(M-=2*C)>lb?(_+=C*=g?1:-1,b-=C):(M=0,_=b=(h+d)/2)}var P=l*ab(_),z=l*fb(_),D=s*ab(x),q=s*fb(x);if(S>lb){var R,F=l*ab(b),O=l*fb(b),I=s*ab(m),U=s*fb(m);if(plb?k>lb?(y=Mb(I,U,P,z,l,k,g),v=Mb(F,O,D,q,l,k,g),u.moveTo(y.cx+y.x01,y.cy+y.y01),klb&&w>lb?E>lb?(y=Mb(D,q,F,O,s,-E,g),v=Mb(P,z,I,U,s,-E,g),u.lineTo(y.cx+y.x01,y.cy+y.y01),E>a,f=i+2*u>>a,s=wa(20);function l(r){var i=new Float32Array(c*f),l=new Float32Array(c*f);r.forEach((function(r,o,s){var l=+t(r,o,s)+u>>a,h=+n(r,o,s)+u>>a,d=+e(r,o,s);l>=0&&l=0&&h>a),Ca({width:c,height:f,data:l},{width:c,height:f,data:i},o>>a),Na({width:c,height:f,data:i},{width:c,height:f,data:l},o>>a),Ca({width:c,height:f,data:l},{width:c,height:f,data:i},o>>a),Na({width:c,height:f,data:i},{width:c,height:f,data:l},o>>a),Ca({width:c,height:f,data:l},{width:c,height:f,data:i},o>>a);var d=s(i);if(!Array.isArray(d)){var p=B(i);d=F(0,p,d),(d=Z(0,Math.floor(p/d)*d,d)).shift()}return ka().thresholds(d).size([c,f])(i).map(h)}function h(t){return t.value*=Math.pow(2,-2*a),t.coordinates.forEach(d),t}function d(t){t.forEach(p)}function p(t){t.forEach(g)}function g(t){t[0]=t[0]*Math.pow(2,a)-u,t[1]=t[1]*Math.pow(2,a)-u}function y(){return c=r+2*(u=3*o)>>a,f=i+2*u>>a,l}return l.x=function(n){return arguments.length?(t="function"==typeof n?n:wa(+n),l):t},l.y=function(t){return arguments.length?(n="function"==typeof t?t:wa(+t),l):n},l.weight=function(t){return arguments.length?(e="function"==typeof t?t:wa(+t),l):e},l.size=function(t){if(!arguments.length)return[r,i];var n=+t[0],e=+t[1];if(!(n>=0&&e>=0))throw new Error("invalid size");return r=n,i=e,y()},l.cellSize=function(t){if(!arguments.length)return 1<=1))throw new Error("invalid cell size");return a=Math.floor(Math.log(t)/Math.LN2),y()},l.thresholds=function(t){return arguments.length?(s="function"==typeof t?t:Array.isArray(t)?wa(ma.call(t)):wa(t),l):s},l.bandwidth=function(t){if(!arguments.length)return Math.sqrt(o*(o+1));if(!((t=+t)>=0))throw new Error("invalid bandwidth");return o=Math.round((Math.sqrt(4*t*t+1)-1)/2),y()},l},t.contours=ka,t.count=c,t.create=function(t){return Dn(At(t).call(document.documentElement))},t.creator=At,t.cross=function(...t){const n="function"==typeof t[t.length-1]&&function(t){return n=>t(...n)}(t.pop()),e=(t=t.map(l)).map(f),r=t.length-1,i=new Array(r+1).fill(0),o=[];if(r<0||e.some(s))return o;for(;;){o.push(i.map(((n,e)=>t[e][n])));let a=r;for(;++i[a]===e[a];){if(0===a)return n?o.map(n):o;i[a--]=0}}},t.csv=Pu,t.csvFormat=hu,t.csvFormatBody=du,t.csvFormatRow=gu,t.csvFormatRows=pu,t.csvFormatValue=yu,t.csvParse=su,t.csvParseRows=lu,t.cubehelix=tr,t.cumsum=function(t,n){var e=0,r=0;return Float64Array.from(t,void 0===n?t=>e+=+t||0:i=>e+=+n(i,r++,t)||0)},t.curveBasis=function(t){return new dm(t)},t.curveBasisClosed=function(t){return new pm(t)},t.curveBasisOpen=function(t){return new gm(t)},t.curveBumpX=function(t){return new ym(t,!0)},t.curveBumpY=function(t){return new ym(t,!1)},t.curveBundle=_m,t.curveCardinal=xm,t.curveCardinalClosed=Mm,t.curveCardinalOpen=Tm,t.curveCatmullRom=km,t.curveCatmullRomClosed=Cm,t.curveCatmullRomOpen=zm,t.curveLinear=Eb,t.curveLinearClosed=function(t){return new Dm(t)},t.curveMonotoneX=function(t){return new Im(t)},t.curveMonotoneY=function(t){return new Um(t)},t.curveNatural=function(t){return new Ym(t)},t.curveStep=function(t){return new jm(t,.5)},t.curveStepAfter=function(t){return new jm(t,1)},t.curveStepBefore=function(t){return new jm(t,0)},t.descending=function(t,n){return nt?1:n>=t?0:NaN},t.deviation=d,t.difference=function(t,...n){t=new Set(t);for(const e of n)for(const n of e)t.delete(n);return t},t.disjoint=function(t,n){const e=n[Symbol.iterator](),r=new Set;for(const n of t){if(r.has(n))return!1;let t,i;for(;({value:t,done:i}=e.next())&&!i;){if(Object.is(n,t))return!1;r.add(t)}}return!0},t.dispatch=pt,t.drag=function(){var t,n,e,r,i=Xn,o=Gn,a=Vn,u=$n,c={},f=pt("start","drag","end"),s=0,l=0;function h(t){t.on("mousedown.drag",d).filter(u).on("touchstart.drag",y).on("touchmove.drag",v).on("touchend.drag touchcancel.drag",_).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function d(a,u){if(!r&&i.call(this,a,u)){var c=b(this,o.call(this,a,u),a,u,"mouse");c&&(Dn(a.view).on("mousemove.drag",p,!0).on("mouseup.drag",g,!0),Yn(a.view),Un(a),e=!1,t=a.clientX,n=a.clientY,c("start",a))}}function p(r){if(Bn(r),!e){var i=r.clientX-t,o=r.clientY-n;e=i*i+o*o>l}c.mouse("drag",r)}function g(t){Dn(t.view).on("mousemove.drag mouseup.drag",null),Ln(t.view,e),Bn(t),c.mouse("end",t)}function y(t,n){if(i.call(this,t,n)){var e,r,a=t.changedTouches,u=o.call(this,t,n),c=a.length;for(e=0;e+t,t.easePoly=Ki,t.easePolyIn=Wi,t.easePolyInOut=Ki,t.easePolyOut=Zi,t.easeQuad=Vi,t.easeQuadIn=function(t){return t*t},t.easeQuadInOut=Vi,t.easeQuadOut=function(t){return t*(2-t)},t.easeSin=to,t.easeSinIn=function(t){return 1==+t?1:1-Math.cos(t*Ji)},t.easeSinInOut=to,t.easeSinOut=function(t){return Math.sin(t*Ji)},t.every=function(t,n){if("function"!=typeof n)throw new TypeError("test is not a function");let e=-1;for(const r of t)if(!n(r,++e,t))return!1;return!0},t.extent=p,t.fcumsum=function(t,n){const e=new g;let r=-1;return Float64Array.from(t,void 0===n?t=>e.add(+t||0):i=>e.add(+n(i,++r,t)||0))},t.filter=function(t,n){if("function"!=typeof n)throw new TypeError("test is not a function");const e=[];let r=-1;for(const i of t)n(i,++r,t)&&e.push(i);return e},t.forceCenter=function(t,n){var e,r=1;function i(){var i,o,a=e.length,u=0,c=0;for(i=0;if+p||os+p||ac.index){var g=f-u.x-u.vx,y=s-u.y-u.vy,v=g*g+y*y;vt.r&&(t.r=t[n].r)}function c(){if(n){var r,i,o=n.length;for(e=new Array(o),r=0;r[u(t,n,r),t])));for(a=0,i=new Array(f);a=u)){(t.data!==n||t.next)&&(0===l&&(p+=(l=Vu(e))*l),0===h&&(p+=(h=Vu(e))*h),p(t=(1664525*t+1013904223)%Qu)/Qu}();function l(){h(),f.call("tick",n),e1?(null==e?u.delete(t):u.set(t,p(e)),n):u.get(t)},find:function(n,e,r){var i,o,a,u,c,f=0,s=t.length;for(null==r?r=1/0:r*=r,f=0;f1?(f.on(t,e),n):f.on(t)}}},t.forceX=function(t){var n,e,r,i=Gu(.1);function o(t){for(var i,o=0,a=n.length;o=.12&&i<.234&&r>=-.425&&r<-.214?u:i>=.166&&i<.234&&r>=-.214&&r<-.115?c:a).invert(t)},s.stream=function(e){return t&&n===e?t:(r=[a.stream(n=e),u.stream(e),c.stream(e)],i=r.length,t={point:function(t,n){for(var e=-1;++eKf(r[0],r[1])&&(r[1]=i[1]),Kf(i[0],r[1])>Kf(r[0],r[1])&&(r[0]=i[0])):o.push(r=i);for(a=-1/0,n=0,r=o[e=o.length-1];n<=e;r=i,++n)i=o[n],(u=Kf(r[1],i[0]))>a&&(a=u,nf=i[0],rf=r[1])}return lf=hf=null,nf===1/0||ef===1/0?[[NaN,NaN],[NaN,NaN]]:[[nf,ef],[rf,of]]},t.geoCentroid=function(t){Ef=kf=Nf=Cf=Pf=zf=Df=qf=0,Rf=new g,Ff=new g,Of=new g,Wc(t,ts);var n=+Rf,e=+Ff,r=+Of,i=Dc(n,e,r);return i2?t[2]+90:90]):[(t=e())[0],t[1],t[2]-90]},e([0,0,90]).scale(159.155)},t.geoTransverseMercatorRaw=Bh,t.gray=function(t,n){return new Fe(t,0,0,null==n?1:n)},t.greatest=function(t,e=n){let r,i=!1;if(1===e.length){let o;for(const a of t){const t=e(a);(i?n(t,o)>0:0===n(t,t))&&(r=a,o=t,i=!0)}}else for(const n of t)(i?e(n,r)>0:0===e(n,n))&&(r=n,i=!0);return r},t.greatestIndex=function(t,e=n){if(1===e.length)return G(t,e);let r,i=-1,o=-1;for(const n of t)++o,(i<0?0===e(n,n):e(n,r)>0)&&(r=n,i=o);return i},t.group=M,t.groupSort=function(t,e,r){return(1===e.length?k(A(t,e,r),(([t,e],[r,i])=>n(e,i)||n(t,r))):k(M(t,r),(([t,r],[i,o])=>e(r,o)||n(t,i)))).map((([t])=>t))},t.groups=function(t,...n){return S(t,Array.from,w,n)},t.hcl=Le,t.hierarchy=Xh,t.histogram=U,t.hsl=Ae,t.html=Fu,t.image=function(t,n){return new Promise((function(e,r){var i=new Image;for(var o in n)i[o]=n[o];i.onerror=r,i.onload=function(){e(i)},i.src=t}))},t.index=function(t,...n){return S(t,w,T,n)},t.indexes=function(t,...n){return S(t,Array.from,T,n)},t.interpolate=Mr,t.interpolateArray=function(t,n){return(gr(n)?pr:yr)(t,n)},t.interpolateBasis=rr,t.interpolateBasisClosed=ir,t.interpolateBlues=q_,t.interpolateBrBG=Gv,t.interpolateBuGn=s_,t.interpolateBuPu=h_,t.interpolateCividis=function(t){return t=Math.max(0,Math.min(1,t)),"rgb("+Math.max(0,Math.min(255,Math.round(-4.54-t*(35.34-t*(2381.73-t*(6402.7-t*(7024.72-2710.57*t)))))))+", "+Math.max(0,Math.min(255,Math.round(32.49+t*(170.73+t*(52.82-t*(131.46-t*(176.58-67.37*t)))))))+", "+Math.max(0,Math.min(255,Math.round(81.24+t*(442.36-t*(2482.43-t*(6167.24-t*(6614.94-2475.67*t)))))))+")"},t.interpolateCool=V_,t.interpolateCubehelix=Yr,t.interpolateCubehelixDefault=X_,t.interpolateCubehelixLong=Lr,t.interpolateDate=vr,t.interpolateDiscrete=function(t){var n=t.length;return function(e){return t[Math.max(0,Math.min(n-1,Math.floor(e*n)))]}},t.interpolateGnBu=p_,t.interpolateGreens=F_,t.interpolateGreys=I_,t.interpolateHcl=Ir,t.interpolateHclLong=Ur,t.interpolateHsl=Rr,t.interpolateHslLong=Fr,t.interpolateHue=function(t,n){var e=ur(+t,+n);return function(t){var n=e(t);return n-360*Math.floor(n/360)}},t.interpolateInferno=nb,t.interpolateLab=function(t,n){var e=fr((t=Re(t)).l,(n=Re(n)).l),r=fr(t.a,n.a),i=fr(t.b,n.b),o=fr(t.opacity,n.opacity);return function(n){return t.l=e(n),t.a=r(n),t.b=i(n),t.opacity=o(n),t+""}},t.interpolateMagma=tb,t.interpolateNumber=_r,t.interpolateNumberArray=pr,t.interpolateObject=br,t.interpolateOrRd=y_,t.interpolateOranges=H_,t.interpolatePRGn=$v,t.interpolatePiYG=Zv,t.interpolatePlasma=eb,t.interpolatePuBu=m_,t.interpolatePuBuGn=__,t.interpolatePuOr=Qv,t.interpolatePuRd=w_,t.interpolatePurples=B_,t.interpolateRainbow=function(t){(t<0||t>1)&&(t-=Math.floor(t));var n=Math.abs(t-.5);return $_.h=360*t-100,$_.s=1.5-1.5*n,$_.l=.8-.9*n,$_+""},t.interpolateRdBu=t_,t.interpolateRdGy=e_,t.interpolateRdPu=A_,t.interpolateRdYlBu=i_,t.interpolateRdYlGn=a_,t.interpolateReds=L_,t.interpolateRgb=sr,t.interpolateRgbBasis=hr,t.interpolateRgbBasisClosed=dr,t.interpolateRound=Ar,t.interpolateSinebow=function(t){var n;return t=(.5-t)*Math.PI,W_.r=255*(n=Math.sin(t))*n,W_.g=255*(n=Math.sin(t+Z_))*n,W_.b=255*(n=Math.sin(t+K_))*n,W_+""},t.interpolateSpectral=c_,t.interpolateString=wr,t.interpolateTransformCss=Cr,t.interpolateTransformSvg=Pr,t.interpolateTurbo=function(t){return t=Math.max(0,Math.min(1,t)),"rgb("+Math.max(0,Math.min(255,Math.round(34.61+t*(1172.33-t*(10793.56-t*(33300.12-t*(38394.49-14825.05*t)))))))+", "+Math.max(0,Math.min(255,Math.round(23.31+t*(557.33+t*(1225.33-t*(3574.96-t*(1073.77+707.56*t)))))))+", "+Math.max(0,Math.min(255,Math.round(27.2+t*(3211.1-t*(15327.97-t*(27814-t*(22569.18-6838.66*t)))))))+")"},t.interpolateViridis=J_,t.interpolateWarm=G_,t.interpolateYlGn=k_,t.interpolateYlGnBu=S_,t.interpolateYlOrBr=C_,t.interpolateYlOrRd=z_,t.interpolateZoom=Dr,t.interrupt=gi,t.intersection=function(t,...n){t=new Set(t),n=n.map(et);t:for(const e of t)for(const r of n)if(!r.has(e)){t.delete(e);continue t}return t},t.interval=function(t,n,e){var r=new ei,i=n;return null==n?(r.restart(t,n,e),r):(r._restart=r.restart,r.restart=function(t,n,e){n=+n,e=null==e?ti():+e,r._restart((function o(a){a+=i,r._restart(o,i+=n,e),t(a)}),n,e)},r.restart(t,n,e),r)},t.isoFormat=Mv,t.isoParse=Av,t.json=function(t,n){return fetch(t,n).then(Du)},t.lab=Re,t.lch=function(t,n,e,r){return 1===arguments.length?Ye(t):new je(e,n,t,null==r?1:r)},t.least=function(t,e=n){let r,i=!1;if(1===e.length){let o;for(const a of t){const t=e(a);(i?n(t,o)<0:0===n(t,t))&&(r=a,o=t,i=!0)}}else for(const n of t)(i?e(n,r)<0:0===e(n,n))&&(r=n,i=!0);return r},t.leastIndex=K,t.line=Cb,t.lineRadial=Ib,t.linkHorizontal=function(){return jb(Hb)},t.linkRadial=function(){var t=jb(Gb);return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t},t.linkVertical=function(){return jb(Xb)},t.local=Rn,t.map=function(t,n){if("function"!=typeof t[Symbol.iterator])throw new TypeError("values is not iterable");if("function"!=typeof n)throw new TypeError("mapper is not a function");return Array.from(t,((e,r)=>n(e,r,t)))},t.matcher=Ct,t.max=B,t.maxIndex=G,t.mean=function(t,n){let e=0,r=0;if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&(++e,r+=n);else{let i=-1;for(let o of t)null!=(o=n(o,++i,t))&&(o=+o)>=o&&(++e,r+=o)}if(e)return r/e},t.median=function(t,n){return H(t,.5,n)},t.merge=V,t.min=Y,t.minIndex=$,t.namespace=xt,t.namespaces=mt,t.nice=O,t.now=ti,t.pack=function(){var t=null,n=1,e=1,r=hd;function i(i){return i.x=n/2,i.y=e/2,t?i.eachBefore(gd(t)).eachAfter(yd(r,.5)).eachBefore(vd(1)):i.eachBefore(gd(pd)).eachAfter(yd(hd,1)).eachAfter(yd(r,i.r/Math.min(n,e))).eachBefore(vd(Math.min(n,e)/(2*i.r))),i}return i.radius=function(n){return arguments.length?(t=sd(n),i):t},i.size=function(t){return arguments.length?(n=+t[0],e=+t[1],i):[n,e]},i.padding=function(t){return arguments.length?(r="function"==typeof t?t:dd(+t),i):r},i},t.packEnclose=Kh,t.packSiblings=function(t){return fd(t),t},t.pairs=function(t,n=W){const e=[];let r,i=!1;for(const o of t)i&&e.push(n(r,o)),r=o,i=!0;return e},t.partition=function(){var t=1,n=1,e=0,r=!1;function i(i){var o=i.height+1;return i.x0=i.y0=e,i.x1=t,i.y1=n/o,i.eachBefore(function(t,n){return function(r){r.children&&bd(r,r.x0,t*(r.depth+1)/n,r.x1,t*(r.depth+2)/n);var i=r.x0,o=r.y0,a=r.x1-e,u=r.y1-e;a0&&(d+=l);for(null!=n?p.sort((function(t,e){return n(g[t],g[e])})):null!=e&&p.sort((function(t,n){return e(a[t],a[n])})),u=0,f=d?(v-h*b)/d:0;u0?l*f:0)+b,g[c]={data:a[c],index:u,value:l,startAngle:y,endAngle:s,padAngle:_};return g}return a.value=function(n){return arguments.length?(t="function"==typeof n?n:rb(+n),a):t},a.sortValues=function(t){return arguments.length?(n=t,e=null,a):n},a.sort=function(t){return arguments.length?(e=t,n=null,a):e},a.startAngle=function(t){return arguments.length?(r="function"==typeof t?t:rb(+t),a):r},a.endAngle=function(t){return arguments.length?(i="function"==typeof t?t:rb(+t),a):i},a.padAngle=function(t){return arguments.length?(o="function"==typeof t?t:rb(+t),a):o},a},t.piecewise=jr,t.pointRadial=Bb,t.pointer=In,t.pointers=function(t,n){return t.target&&(t=On(t),void 0===n&&(n=t.currentTarget),t=t.touches||[t]),Array.from(t,(t=>In(t,n)))},t.polygonArea=function(t){for(var n,e=-1,r=t.length,i=t[r-1],o=0;++eu!=f>u&&a<(c-e)*(u-r)/(f-r)+e&&(s=!s),c=e,f=r;return s},t.polygonHull=function(t){if((e=t.length)<3)return null;var n,e,r=new Array(e),i=new Array(e);for(n=0;n=0;--n)f.push(t[r[o[n]][2]]);for(n=+u;n(n=1664525*n+1013904223|0,ep*(n>>>0))},t.randomLogNormal=Ld,t.randomLogistic=tp,t.randomNormal=Yd,t.randomPareto=Gd,t.randomPoisson=np,t.randomUniform=Ud,t.randomWeibull=Qd,t.range=Z,t.reduce=function(t,n,e){if("function"!=typeof n)throw new TypeError("reducer is not a function");const r=t[Symbol.iterator]();let i,o,a=-1;if(arguments.length<3){if(({done:i,value:e}=r.next()),i)return;++a}for(;({done:i,value:o}=r.next()),!i;)e=n(e,o,++a,t);return e},t.reverse=function(t){if("function"!=typeof t[Symbol.iterator])throw new TypeError("values is not iterable");return Array.from(t).reverse()},t.rgb=ve,t.ribbon=function(){return ba()},t.ribbonArrow=function(){return ba(_a)},t.rollup=A,t.rollups=function(t,n,...e){return S(t,Array.from,n,e)},t.scaleBand=up,t.scaleDiverging=function t(){var n=bp(Pv()(lp));return n.copy=function(){return Nv(n,t())},ip.apply(n,arguments)},t.scaleDivergingLog=function t(){var n=Ep(Pv()).domain([.1,1,10]);return n.copy=function(){return Nv(n,t()).base(n.base())},ip.apply(n,arguments)},t.scaleDivergingPow=zv,t.scaleDivergingSqrt=function(){return zv.apply(null,arguments).exponent(.5)},t.scaleDivergingSymlog=function t(){var n=Cp(Pv());return n.copy=function(){return Nv(n,t()).constant(n.constant())},ip.apply(n,arguments)},t.scaleIdentity=function t(n){var e;function r(t){return null==t||isNaN(t=+t)?e:t}return r.invert=r,r.domain=r.range=function(t){return arguments.length?(n=Array.from(t,fp),r):n.slice()},r.unknown=function(t){return arguments.length?(e=t,r):e},r.copy=function(){return t(n).unknown(e)},n=arguments.length?Array.from(n,fp):[0,1],bp(r)},t.scaleImplicit=op,t.scaleLinear=function t(){var n=vp();return n.copy=function(){return gp(n,t())},rp.apply(n,arguments),bp(n)},t.scaleLog=function t(){var n=Ep(yp()).domain([1,10]);return n.copy=function(){return gp(n,t()).base(n.base())},rp.apply(n,arguments),n},t.scaleOrdinal=ap,t.scalePoint=function(){return cp(up.apply(null,arguments).paddingInner(1))},t.scalePow=Rp,t.scaleQuantile=function t(){var e,r=[],i=[],a=[];function u(){var t=0,n=Math.max(1,i.length);for(a=new Array(n-1);++t0?a[n-1]:r[0],n=i?[a[i-1],r]:[a[n-1],a[n]]},c.unknown=function(t){return arguments.length?(n=t,c):c},c.thresholds=function(){return a.slice()},c.copy=function(){return t().domain([e,r]).range(u).unknown(n)},rp.apply(bp(c),arguments)},t.scaleRadial=function t(){var n,e=vp(),r=[0,1],i=!1;function o(t){var r=Op(e(t));return isNaN(r)?n:i?Math.round(r):r}return o.invert=function(t){return e.invert(Fp(t))},o.domain=function(t){return arguments.length?(e.domain(t),o):e.domain()},o.range=function(t){return arguments.length?(e.range((r=Array.from(t,fp)).map(Fp)),o):r.slice()},o.rangeRound=function(t){return o.range(t).round(!0)},o.round=function(t){return arguments.length?(i=!!t,o):i},o.clamp=function(t){return arguments.length?(e.clamp(t),o):e.clamp()},o.unknown=function(t){return arguments.length?(n=t,o):n},o.copy=function(){return t(e.domain(),r).round(i).clamp(e.clamp()).unknown(n)},rp.apply(o,arguments),bp(o)},t.scaleSequential=function t(){var n=bp(kv()(lp));return n.copy=function(){return Nv(n,t())},ip.apply(n,arguments)},t.scaleSequentialLog=function t(){var n=Ep(kv()).domain([1,10]);return n.copy=function(){return Nv(n,t()).base(n.base())},ip.apply(n,arguments)},t.scaleSequentialPow=Cv,t.scaleSequentialQuantile=function t(){var e=[],r=lp;function i(t){if(null!=t&&!isNaN(t=+t))return r((o(e,t,1)-1)/(e.length-1))}return i.domain=function(t){if(!arguments.length)return e.slice();e=[];for(let n of t)null==n||isNaN(n=+n)||e.push(n);return e.sort(n),i},i.interpolator=function(t){return arguments.length?(r=t,i):r},i.range=function(){return e.map(((t,n)=>r(n/(e.length-1))))},i.quantiles=function(t){return Array.from({length:t+1},((n,r)=>H(e,r/t)))},i.copy=function(){return t(r).domain(e)},ip.apply(i,arguments)},t.scaleSequentialSqrt=function(){return Cv.apply(null,arguments).exponent(.5)},t.scaleSequentialSymlog=function t(){var n=Cp(kv());return n.copy=function(){return Nv(n,t()).constant(n.constant())},ip.apply(n,arguments)},t.scaleSqrt=function(){return Rp.apply(null,arguments).exponent(.5)},t.scaleSymlog=function t(){var n=Cp(yp());return n.copy=function(){return gp(n,t()).constant(n.constant())},rp.apply(n,arguments)},t.scaleThreshold=function t(){var n,e=[.5],r=[0,1],i=1;function a(t){return null!=t&&t<=t?r[o(e,t,0,i)]:n}return a.domain=function(t){return arguments.length?(e=Array.from(t),i=Math.min(e.length,r.length-1),a):e.slice()},a.range=function(t){return arguments.length?(r=Array.from(t),i=Math.min(e.length,r.length-1),a):r.slice()},a.invertExtent=function(t){var n=r.indexOf(t);return[e[n-1],e[n]]},a.unknown=function(t){return arguments.length?(n=t,a):n},a.copy=function(){return t().domain(e).range(r).unknown(n)},rp.apply(a,arguments)},t.scaleTime=function(){return rp.apply(Ev(Kg,Qg,xg,bg,og,eg,tg,Qp,Zp,t.timeFormat).domain([new Date(2e3,0,1),new Date(2e3,0,2)]),arguments)},t.scaleUtc=function(){return rp.apply(Ev(Wg,Zg,Gg,Hg,Cg,Eg,Tg,Mg,Zp,t.utcFormat).domain([Date.UTC(2e3,0,1),Date.UTC(2e3,0,2)]),arguments)},t.scan=function(t,n){const e=K(t,n);return e<0?void 0:e},t.schemeAccent=Rv,t.schemeBlues=D_,t.schemeBrBG=Xv,t.schemeBuGn=f_,t.schemeBuPu=l_,t.schemeCategory10=qv,t.schemeDark2=Fv,t.schemeGnBu=d_,t.schemeGreens=R_,t.schemeGreys=O_,t.schemeOrRd=g_,t.schemeOranges=j_,t.schemePRGn=Vv,t.schemePaired=Ov,t.schemePastel1=Iv,t.schemePastel2=Uv,t.schemePiYG=Wv,t.schemePuBu=b_,t.schemePuBuGn=v_,t.schemePuOr=Kv,t.schemePuRd=x_,t.schemePurples=U_,t.schemeRdBu=Jv,t.schemeRdGy=n_,t.schemeRdPu=M_,t.schemeRdYlBu=r_,t.schemeRdYlGn=o_,t.schemeReds=Y_,t.schemeSet1=Bv,t.schemeSet2=Yv,t.schemeSet3=Lv,t.schemeSpectral=u_,t.schemeTableau10=jv,t.schemeYlGn=E_,t.schemeYlGnBu=T_,t.schemeYlOrBr=N_,t.schemeYlOrRd=P_,t.select=Dn,t.selectAll=function(t){return"string"==typeof t?new Pn([document.querySelectorAll(t)],[document.documentElement]):new Pn([null==t?[]:Et(t)],Cn)},t.selection=zn,t.selector=St,t.selectorAll=Nt,t.shuffle=Q,t.shuffler=J,t.some=function(t,n){if("function"!=typeof n)throw new TypeError("test is not a function");let e=-1;for(const r of t)if(n(r,++e,t))return!0;return!1},t.sort=k,t.stack=function(){var t=rb([]),n=Xm,e=Hm,r=Gm;function i(i){var o,a,u=Array.from(t.apply(this,arguments),Vm),c=u.length,f=-1;for(const t of i)for(o=0,++f;o0)for(var e,r,i,o,a,u,c=0,f=t[n[0]].length;c0?(r[0]=o,r[1]=o+=i):i<0?(r[1]=a,r[0]=a+=i):(r[0]=0,r[1]=i)},t.stackOffsetExpand=function(t,n){if((r=t.length)>0){for(var e,r,i,o=0,a=t[0].length;o0){for(var e,r=0,i=t[n[0]],o=i.length;r0&&(r=(e=t[n[0]]).length)>0){for(var e,r,i,o=0,a=1;a0)throw new Error("cycle");return o}return e.id=function(n){return arguments.length?(t=ld(n),e):t},e.parentId=function(t){return arguments.length?(n=ld(t),e):n},e},t.style=Jt,t.subset=function(t,n){return rt(n,t)},t.sum=function(t,n){let e=0;if(void 0===n)for(let n of t)(n=+n)&&(e+=n);else{let r=-1;for(let i of t)(i=+n(i,++r,t))&&(e+=i)}return e},t.superset=rt,t.svg=Ou,t.symbol=function(t,n){var e=null;function r(){var r;if(e||(e=r=fa()),t.apply(this,arguments).draw(e,+n.apply(this,arguments)),r)return e=null,r+""||null}return t="function"==typeof t?t:rb(t||Vb),n="function"==typeof n?n:rb(void 0===n?64:+n),r.type=function(n){return arguments.length?(t="function"==typeof n?n:rb(n),r):t},r.size=function(t){return arguments.length?(n="function"==typeof t?t:rb(+t),r):n},r.context=function(t){return arguments.length?(e=null==t?null:t,r):e},r},t.symbolCircle=Vb,t.symbolCross=$b,t.symbolDiamond=Kb,t.symbolSquare=em,t.symbolStar=nm,t.symbolTriangle=im,t.symbolWye=fm,t.symbols=sm,t.text=Nu,t.thresholdFreedmanDiaconis=function(t,n,e){return Math.ceil((e-n)/(2*(H(t,.75)-H(t,.25))*Math.pow(c(t),-1/3)))},t.thresholdScott=function(t,n,e){return Math.ceil((e-n)/(3.5*d(t)*Math.pow(c(t),-1/3)))},t.thresholdSturges=I,t.tickFormat=_p,t.tickIncrement=R,t.tickStep=F,t.ticks=q,t.timeDay=eg,t.timeDays=rg,t.timeFormatDefaultLocale=xv,t.timeFormatLocale=ey,t.timeFriday=sg,t.timeFridays=vg,t.timeHour=tg,t.timeHours=ng,t.timeInterval=Bp,t.timeMillisecond=Yp,t.timeMilliseconds=Lp,t.timeMinute=Qp,t.timeMinutes=Jp,t.timeMonday=ag,t.timeMondays=dg,t.timeMonth=bg,t.timeMonths=mg,t.timeSaturday=lg,t.timeSaturdays=_g,t.timeSecond=Zp,t.timeSeconds=Kp,t.timeSunday=og,t.timeSundays=hg,t.timeThursday=fg,t.timeThursdays=yg,t.timeTickInterval=Qg,t.timeTicks=Kg,t.timeTuesday=ug,t.timeTuesdays=pg,t.timeWednesday=cg,t.timeWednesdays=gg,t.timeWeek=og,t.timeWeeks=hg,t.timeYear=xg,t.timeYears=wg,t.timeout=ci,t.timer=ri,t.timerFlush=ii,t.transition=Hi,t.transpose=tt,t.tree=function(){var t=Ad,n=1,e=1,r=null;function i(i){var c=function(t){for(var n,e,r,i,o,a=new Nd(t,0),u=[a];n=u.pop();)if(r=n._.children)for(n.children=new Array(o=r.length),i=o-1;i>=0;--i)u.push(e=n.children[i]=new Nd(r[i],i)),e.parent=n;return(a.parent=new Nd(null,0)).children=[a],a}(i);if(c.eachAfter(o),c.parent.m=-c.z,c.eachBefore(a),r)i.eachBefore(u);else{var f=i,s=i,l=i;i.eachBefore((function(t){t.xs.x&&(s=t),t.depth>l.depth&&(l=t)}));var h=f===s?1:t(f,s)/2,d=h-f.x,p=n/(s.x+h+d),g=e/(l.depth||1);i.eachBefore((function(t){t.x=(t.x+d)*p,t.y=t.depth*g}))}return i}function o(n){var e=n.children,r=n.parent.children,i=n.i?r[n.i-1]:null;if(e){!function(t){for(var n,e=0,r=0,i=t.children,o=i.length;--o>=0;)(n=i[o]).z+=e,n.m+=e,e+=n.s+(r+=n.c)}(n);var o=(e[0].z+e[e.length-1].z)/2;i?(n.z=i.z+t(n._,i._),n.m=n.z-o):n.z=o}else i&&(n.z=i.z+t(n._,i._));n.parent.A=function(n,e,r){if(e){for(var i,o=n,a=n,u=e,c=o.parent.children[0],f=o.m,s=a.m,l=u.m,h=c.m;u=Sd(u),o=Td(o),u&&o;)c=Td(c),(a=Sd(a)).a=n,(i=u.z+l-o.z-f+t(u._,o._))>0&&(Ed(kd(u,n,r),n,i),f+=i,s+=i),l+=u.m,f+=o.m,h+=c.m,s+=a.m;u&&!Sd(a)&&(a.t=u,a.m+=l-s),o&&!Td(c)&&(c.t=o,c.m+=f-h,r=n)}return r}(n,i,n.parent.A||r[0])}function a(t){t._.x=t.z+t.parent.m,t.m+=t.parent.m}function u(t){t.x*=n,t.y=t.depth*e}return i.separation=function(n){return arguments.length?(t=n,i):t},i.size=function(t){return arguments.length?(r=!1,n=+t[0],e=+t[1],i):r?null:[n,e]},i.nodeSize=function(t){return arguments.length?(r=!0,n=+t[0],e=+t[1],i):r?[n,e]:null},i},t.treemap=function(){var t=Dd,n=!1,e=1,r=1,i=[0],o=hd,a=hd,u=hd,c=hd,f=hd;function s(t){return t.x0=t.y0=0,t.x1=e,t.y1=r,t.eachBefore(l),i=[0],n&&t.eachBefore(_d),t}function l(n){var e=i[n.depth],r=n.x0+e,s=n.y0+e,l=n.x1-e,h=n.y1-e;l=e-1){var s=u[n];return s.x0=i,s.y0=o,s.x1=a,void(s.y1=c)}var l=f[n],h=r/2+l,d=n+1,p=e-1;for(;d>>1;f[g]c-o){var _=r?(i*v+a*y)/r:a;t(n,d,y,i,o,_,c),t(d,e,v,_,o,a,c)}else{var b=r?(o*v+c*y)/r:c;t(n,d,y,i,o,a,b),t(d,e,v,i,b,a,c)}}(0,c,t.value,n,e,r,i)},t.treemapDice=bd,t.treemapResquarify=qd,t.treemapSlice=Cd,t.treemapSliceDice=function(t,n,e,r,i){(1&t.depth?Cd:bd)(t,n,e,r,i)},t.treemapSquarify=Dd,t.tsv=zu,t.tsvFormat=mu,t.tsvFormatBody=xu,t.tsvFormatRow=Mu,t.tsvFormatRows=wu,t.tsvFormatValue=Au,t.tsvParse=_u,t.tsvParseRows=bu,t.union=function(...t){const n=new Set;for(const e of t)for(const t of e)n.add(t);return n},t.utcDay=Eg,t.utcDays=kg,t.utcFriday=Rg,t.utcFridays=Lg,t.utcHour=Tg,t.utcHours=Sg,t.utcMillisecond=Yp,t.utcMilliseconds=Lp,t.utcMinute=Mg,t.utcMinutes=Ag,t.utcMonday=Pg,t.utcMondays=Ig,t.utcMonth=Hg,t.utcMonths=Xg,t.utcSaturday=Fg,t.utcSaturdays=jg,t.utcSecond=Zp,t.utcSeconds=Kp,t.utcSunday=Cg,t.utcSundays=Og,t.utcThursday=qg,t.utcThursdays=Yg,t.utcTickInterval=Zg,t.utcTicks=Wg,t.utcTuesday=zg,t.utcTuesdays=Ug,t.utcWednesday=Dg,t.utcWednesdays=Bg,t.utcWeek=Cg,t.utcWeeks=Og,t.utcYear=Gg,t.utcYears=Vg,t.variance=h,t.version="6.7.0",t.window=Wt,t.xml=Ru,t.zip=function(){return tt(arguments)},t.zoom=function(){var t,n,e,r=ox,i=ax,o=sx,a=cx,u=fx,c=[0,1/0],f=[[-1/0,-1/0],[1/0,1/0]],s=250,l=Dr,h=pt("start","zoom","end"),d=500,p=0,g=10;function y(t){t.property("__zoom",ux).on("wheel.zoom",M).on("mousedown.zoom",A).on("dblclick.zoom",T).filter(u).on("touchstart.zoom",S).on("touchmove.zoom",E).on("touchend.zoom touchcancel.zoom",k).style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function v(t,n){return(n=Math.max(c[0],Math.min(c[1],n)))===t.k?t:new tx(n,t.x,t.y)}function _(t,n,e){var r=n[0]-e[0]*t.k,i=n[1]-e[1]*t.k;return r===t.x&&i===t.y?t:new tx(t.k,r,i)}function b(t){return[(+t[0][0]+ +t[1][0])/2,(+t[0][1]+ +t[1][1])/2]}function m(t,n,e,r){t.on("start.zoom",(function(){x(this,arguments).event(r).start()})).on("interrupt.zoom end.zoom",(function(){x(this,arguments).event(r).end()})).tween("zoom",(function(){var t=this,o=arguments,a=x(t,o).event(r),u=i.apply(t,o),c=null==e?b(u):"function"==typeof e?e.apply(t,o):e,f=Math.max(u[1][0]-u[0][0],u[1][1]-u[0][1]),s=t.__zoom,h="function"==typeof n?n.apply(t,o):n,d=l(s.invert(c).concat(f/s.k),h.invert(c).concat(f/h.k));return function(t){if(1===t)t=h;else{var n=d(t),e=f/n[2];t=new tx(e,c[0]-n[0]*e,c[1]-n[1]*e)}a.zoom(null,t)}}))}function x(t,n,e){return!e&&t.__zooming||new w(t,n)}function w(t,n){this.that=t,this.args=n,this.active=0,this.sourceEvent=null,this.extent=i.apply(t,n),this.taps=0}function M(t,...n){if(r.apply(this,arguments)){var e=x(this,n).event(t),i=this.__zoom,u=Math.max(c[0],Math.min(c[1],i.k*Math.pow(2,a.apply(this,arguments)))),s=In(t);if(e.wheel)e.mouse[0][0]===s[0]&&e.mouse[0][1]===s[1]||(e.mouse[1]=i.invert(e.mouse[0]=s)),clearTimeout(e.wheel);else{if(i.k===u)return;e.mouse=[s,i.invert(s)],gi(this),e.start()}ix(t),e.wheel=setTimeout(l,150),e.zoom("mouse",o(_(v(i,u),e.mouse[0],e.mouse[1]),e.extent,f))}function l(){e.wheel=null,e.end()}}function A(t,...n){if(!e&&r.apply(this,arguments)){var i=x(this,n,!0).event(t),a=Dn(t.view).on("mousemove.zoom",h,!0).on("mouseup.zoom",d,!0),u=In(t,c),c=t.currentTarget,s=t.clientX,l=t.clientY;Yn(t.view),rx(t),i.mouse=[u,this.__zoom.invert(u)],gi(this),i.start()}function h(t){if(ix(t),!i.moved){var n=t.clientX-s,e=t.clientY-l;i.moved=n*n+e*e>p}i.event(t).zoom("mouse",o(_(i.that.__zoom,i.mouse[0]=In(t,c),i.mouse[1]),i.extent,f))}function d(t){a.on("mousemove.zoom mouseup.zoom",null),Ln(t.view,i.moved),ix(t),i.event(t).end()}}function T(t,...n){if(r.apply(this,arguments)){var e=this.__zoom,a=In(t.changedTouches?t.changedTouches[0]:t,this),u=e.invert(a),c=e.k*(t.shiftKey?.5:2),l=o(_(v(e,c),a,u),i.apply(this,n),f);ix(t),s>0?Dn(this).transition().duration(s).call(m,l,a,t):Dn(this).call(y.transform,l,a,t)}}function S(e,...i){if(r.apply(this,arguments)){var o,a,u,c,f=e.touches,s=f.length,l=x(this,i,e.changedTouches.length===s).event(e);for(rx(e),a=0;a