AӪk߭/Kb
8h
U}v<\sl#h3uDJkXK1Ni|^vq  ~MBD 8TL\y۝VHE'EeyG} =(_Fcʜ8UYM׾%Nqpc(>-( m2qv:	2d9C^^cIG]D	4qs$**-g둬R.3nؙ'B>NwulQ,]6֠hnKAV&|k4fm33!<ӂj#jҕʦ[1\ *ǺUVRm΁5X𹨓UdWV@%)sSOuD+iW
Pp1ho@.F-V<:n{I[4
Ǌ/'NGd]VKE
h:41+&c1Sz{T>.an!:`	#L'qe`3Nᾣ1#ɎzLX]ΥvHCdvtV}%fj O\*0U4GT4ba3Nᾣ1#Ɏz++ĆQ0S#d$'I4xlԛ+EZ_P 7]n>)&L5۽Rjn)Զm߭Ha7`	bzshlEE=1w𫍃YS!3LV[#R??GT)"܃3LqA%	}zFi_X
lXA4*M퍀{G)ʚBK8F@;%]0cgr6]g}8ksi/|`iP.T+o#(/=ޣUcEKdEb)Ǵ/Z	pNGZw~p ~rkW(X1OO&$棄Ϗ$1o8Xkц	1GgA.:M̢So	KyO9:A#{	r#*^Q6^h_{y*+~F0(>^
Arp!g葶9H5~6F+%kiuo"ǜ8'QSc|Oq%E㿃axReK,&9J5FMpРi7c=2s$d6%_4^n-lWѧRxkXlPH9Dc(i,OGڢ9W$Xi=rD*z1;A-@Gyf5@;d`e\ȉ^:x!/C {M~,#9NRQzc@q%O`AzDB)y@FkXzi5sޅ<'ߌ0͐?%O&R	Z𛯎cx*kE"Ko4z Qӵko;M1_GQ^:O`/LM A9CyX'/ݢ3 >_ݤ|HSʂǸAUD}kV>du!L'JKޕsD\xb3ޠ.|`#<5(XCHI)jgɊy#ݥ0vE9 hfj4qVnո0m5-ZUktPVA\q-a6<c2W8"OdC|dtEO8=MٔAyPe,Shy1d-HjkVJƩ07<̏@o-Kj!)75bO3%׊{awi=|(SGDs){OgWG5QN>uw6ӇHgM`P<kݼ3db8u1HQwSD<.ЅyXbfKw~Wcm-ٝ$s^Dιt.<@_%K,&9J5FMpРi7c=2s$d6%_4^	LQWC@,YYNTփmހej-d5
T`ʴkfmwz^@D%ܣ8B#F:Kꥂϒz px719Ê#X5HIf 
J/%1c୐"ps|%F1,jgq.:l>\Zvbhzº"plRK6(,0b*!8>	xe%~Q@oɒ'LT %#LQ?-FÀHZ,l[Sd&]ևGdmgjd]| DCd<>ΘO=K7E~@GYnz$Il),f9T4_o=eWN@GҝxTtAy.bcPlSGV?Jkbw74}9[p8c(HOETgY:=s`+TV10{)rY3^ߵ)<0!kLe7*_B9k]I2\B},gqISdE!=\(Zﮂ9)
܆aqX77/ChdK^6?XGɄ\eXNL.yg)Z^O2G>LPhf>h#x))OQ
ɣ!ϦE@K[KB'7HZ(|sG474='jgG|Pпh 	]i
_/s.'/(S{Q~,a[soq~ѺSVfk6|(euéK3`:o~s"uJRc$1t[6dL*/(S{ؒK3`:o~s.wxS2+}_f&R/Ъ6*A)\à:EVLoU5!yCTEEŇ:en챀`FѩYO)ҔTyY^3Nᾣ1#Ɏzq-f9\HB5Rr˨ #߭9#\ײ!n2yGM3Nᾣ1#Ɏz;1 ^4$x Zm
b7ả4еy˘nؓߍd5BY %H&(JS ᎗~4:.{A{hxz"~A۪IO93Nᾣ1#Ɏz+р ĩ"!/W3D	+MY(G) _Gg,{O:s\L=8ӼYsХaC)<ƘnMmJ佸c]w>h=3Eyȕ(:[L=8ӼYsЧ0Z*532L=8ӼYsB(R<n J&K"̓l.u,:-B Jw=tіάr,DK{	*d26%Vm&7r3Tk2	Iՠ1ͽM~0@eQdLBؒ%(X>Β^ż3?|x3Nᾣ1#Ɏz FOz$yI.ݏꓰ#:
dOT|m֨ob	n	F6sb'Dٍz<G~HʷiD8b#DOaS9v&dlR#;I'pr3Nᾣ1#Ɏz8A9߷AdĄ
\3?OD3Nᾣ1#Ɏz*cJ*(8wveR;ڄ*r[=ߨ@ܗј؛f3Nᾣ1#Ɏz            is_array($ret->options[$lastOptionId])
                                ? $ret->options[$lastOptionId]['name']
                                : $ret->options[$lastOptionId]
                            ),
                            $token
                        );
                        $lastOptionId = $lastAssignedId++;
                    }
                } else {
                    // There is no option to be processed.
                    break;
                }
            }

            if ($state === 0) {
                if (!is_array($lastOption)) {
                    // This is a just keyword option without any value.
                    // This is the beginning and the end of it.
                    $ret->options[$lastOptionId] = $token->value;
                    $lastOption = null;
                    $state = 0;
                } elseif (($lastOption[1] === 'var') || ($lastOption[1] === 'var=')) {
                    // This is a keyword that is followed by a value.
                    // This is only the beginning. The value is parsed in state
                    // 1 and 2. State 1 is used to skip the first equals sign
                    // and state 2 to parse the actual value.
                    $ret->options[$lastOptionId] = array(
                        // @var string The name of the option.
                        'name' => $token->value,
                        // @var bool Whether it contains an equal sign.
                        //           This is used by the builder to rebuild it.
                        'equals' => $lastOption[1] === 'var=',
                        // @var string Raw value.
                        'expr' => '',
                        // @var string Processed value.
                        'value' => '',
                    );
                    $state = 1;
                } elseif ($lastOption[1] === 'expr' || $lastOption[1] === 'expr=') {
                    // This is a keyword that is followed by an expression.
                    // The expression is used by the specialized parser.

                    // Skipping this option in order to parse the expression.
                    ++$list->idx;
                    $ret->options[$lastOptionId] = array(
                        // @var string The name of the option.
                        'name' => $token->value,
                        // @var bool Whether it contains an equal sign.
                        //           This is used by the builder to rebuild it.
                        'equals' => $lastOption[1] === 'expr=',
                        // @var Expression The parsed expression.
                        'expr' => '',
                    );
                    $state = 1;
                }
            } elseif ($state === 1) {
                $state = 2;
                if ($token->token === '=') {
                    $ret->options[$lastOptionId]['equals'] = true;
                    continue;
                }
            }

            // This is outside the `elseif` group above because the change might
            // change this iteration.
            if ($state === 2) {
                if ($lastOption[1] === 'expr' || $lastOption[1] === 'expr=') {
                    $ret->options[$lastOptionId]['expr'] = Expression::parse(
                        $parser,
                        $list,
                        empty($lastOption[2]) ? array() : $lastOption[2]
                    );
                    $ret->options[$lastOptionId]['value']
                        = $ret->options[$lastOptionId]['expr']->expr;
                    $lastOption = null;
                    $state = 0;
                } else {
                    if ($token->token === '(') {
                        ++$brackets;
                    } elseif ($token->token === ')') {
                        --$brackets;
                    }

                    $ret->options[$lastOptionId]['expr'] .= $token->token;

                    if (!((($token->token === '(') && ($brackets === 1))
                        || (($token->token === ')') && ($brackets === 0)))
                    ) {
                        // First pair of brackets is being skipped.
                        $ret->options[$lastOptionId]['value'] .= $token->value;
                    }

                    // Checking if we finished parsing.
                    if ($brackets === 0) {
                        $lastOption = null;
                    }
                }
            }
        }

        /*
         * We reached the end of statement without getting a value
         * for an option for which a value was required
         */
        if ($state === 1
            && $lastOption
            && ($lastOption[1] == 'expr'
            || $lastOption[1] == 'var'
            || $lastOption[1] == 'var='
            || $lastOption[1] == 'expr=')
        ) {
            $parser->error(
                sprintf(
                    __('Value/Expression for the option %1$s was expected.'),
                    $ret->options[$lastOptionId]['name']
                ),
                $list->tokens[$list->idx - 1]
            );
        }

        if (empty($options['_UNSORTED'])) {
            ksort($ret->options);
        }

        --$list->idx;

        return $ret;
    }

    /**
     * @param OptionsArray $component the component to be built
     * @param array        $options   parameters for building
     *
     * @return string
     */
    public static function build($component, array $options = array())
    {
        if (empty($component->options)) {
            return '';
        }

        $options = array();
        foreach ($component->options as $option) {
            if (!is_array($option)) {
                $options[] = $option;
            } else {
                $options[] = $option['name']
                    . ((!empty($option['equals']) && $option['equals']) ? '=' : ' ')
                    . (!empty($option['expr']) ? $option['expr'] : $option['value']);
            }
        }

        return implode(' ', $options);
    }

    /**
     * Checks if it has the specified option and returns it value or true.
     *
     * @param string $key     the key to be checked
     * @param bool   $getExpr Gets the expression instead of the value.
     *                        The value is the processed form of the expression.
     *
     * @return mixed
     */
    public function has($key, $getExpr = false)
    {
        foreach ($this->options as $option) {
            if (is_array($option)) {
                if (!strcasecmp($key, $option['name'])) {
                    return $getExpr ? $option['expr'] : $option['value'];
                }
            } elseif (!strcasecmp($key, $option)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Removes the option from the array.
     *
     * @param string $key the key to be removed
     *
     * @return bool whether the key was found and deleted or not
     */
    public function remove($key)
    {
        foreach ($this->options as $idx => $option) {
            if (is_array($option)) {
                if (!strcasecmp($key, $option['name'])) {
                    unset($this->options[$idx]);

                    return true;
                }
            } elseif (!strcasecmp($key, $option)) {
                unset($this->options[$idx]);

                return true;
            }
        }

        return false;
    }

    /**
     * Merges the specified options with these ones. Values with same ID will be
     * replaced.
     *
     * @param array|OptionsArray $options the options to be merged
     */
    public function merge($options)
    {
        if (is_array($options)) {
            $this->options = array_merge_recursive($this->options, $options);
        } elseif ($options instanceof self) {
            $this->options = array_merge_recursive($this->options, $options->options);
        }
    }

    /**
     * Checks tf there are no options set.
     *
     * @return bool
     */
    public function isEmpty()
    {
        return empty($this->options);
    }
}
