diff --git a/.nojekyll b/.nojekyll
new file mode 100644
index 000000000..e69de29bb
diff --git a/404.html b/404.html
new file mode 100644
index 000000000..bfc634b9a
--- /dev/null
+++ b/404.html
@@ -0,0 +1,140 @@
+#error-wrap {
+ display: flex;
+ justify-content: center;
+ width: 100%;
+ margin-top: 1rem;
+ position: relative;
+maxWidth768()
+}
+#error-wrap .error-content {
+ box-shadow: none !important;
+ border-radius: 12px;
+ background: var(--anzhiyu-card-bg) !important;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+ margin: 0px 1rem;
+ height: 22rem;
+ max-width: 800px;
+ border-radius: 5px;
+ background: var(--anzhiyu-card-bg);
+ box-shadow: var(--card-box-shadow);
+ transition: all 0.3s ease 0s;
+ border: var(--style-border-always);
+ position: relative;
+ width: 100%;
+maxWidth768()
+}
+#error-wrap .error-content .error-img {
+ flex: 1 1 0%;
+ height: 90%;
+ width: 600px;
+ border-top-left-radius: 8px;
+ border-bottom-left-radius: 8px;
+ background-color: #307af6;
+ background-position: center center;
+ background-size: cover;
+ height: 100%;
+maxWidth768()
+}
+#error-wrap .error-content .error-info {
+ flex: 1 1 0%;
+ padding: 0.5rem;
+ text-align: center;
+ font-size: 14px;
+ font-family: $font-family;
+maxWidth768()
+}
+#error-wrap .error-content .error-info .error_title {
+ font-size: 9em;
+ line-height: 1;
+maxWidth768()
+}
+#error-wrap .error-content .error-info .error_subtitle {
+ word-break: break-word;
+ font-size: 1.6em;
+ -webkit-line-clamp: 2;
+}
+#error-wrap .error-content .error-info a {
+ display: inline-block;
+ margin-top: 0.5rem;
+ padding: 0.3rem 1.5rem;
+ background: var(--btn-bg);
+ color: var(--btn-color);
+}
+#error-wrap .error-content .error-info a i {
+ padding-right: 0.3rem;
+}
+.button--animated {
+ border-radius: 8px !important;
+ transition: 0.3s;
+ position: relative;
+ z-index: 1;
+ transition: color 1s ease 0s;
+}
+#body-wrap .error-box {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ padding: 1rem;
+ padding-top: 0px;
+ position: relative;
+}
+#body-wrap .error-box .aside-list {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: nowrap;
+ margin: 1rem;
+ max-width: 100%;
+maxWidth768()
+}
+#body-wrap .error-box .aside-list .aside-list-group {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ max-width: 800px;
+ margin: 0 auto;
+ justify-content: space-between;
+}
+#body-wrap .error-box .aside-list .aside-list-item {
+ padding: 0.5rem 0;
+ width: 49%;
+}
+#body-wrap .error-box .aside-list .aside-list-item .thumbnail {
+ overflow: hidden;
+ width: 100%;
+ height: 200px;
+ background: var(--anzhiyu-card-bg);
+ display: flex;
+ border-radius: 12px;
+maxWidth768()
+}
+#body-wrap .error-box .aside-list .aside-list-item img {
+ width: 100%;
+ object-fit: cover;
+ border-radius: 12px;
+ transition: 0.3s;
+ transition: filter 300ms ease-in 0.2s, transform 0.6s;
+}
+#body-wrap .error-box .aside-list .aside-list-item:hover img {
+ transform: scale(1.1);
+ filter: brightness(0.82);
+}
+#body-wrap .error-box .aside-list .aside-list-item .content .title {
+ -webkit-line-clamp: 2;
+ overflow: hidden;
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ line-height: 1.5;
+ justify-content: center;
+ align-items: flex-end;
+ align-content: center;
+ padding-top: 0.5rem;
+ font-size: 16px;
+ font-weight: bold;
+}
+#body-wrap .error-box .aside-list .aside-list-item .content time {
+ display: none;
+}
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 000000000..3b7b82d0d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,427 @@
+Attribution-ShareAlike 4.0 International
+
+=======================================================================
+
+Creative Commons Corporation ("Creative Commons") is not a law firm and
+does not provide legal services or legal advice. Distribution of
+Creative Commons public licenses does not create a lawyer-client or
+other relationship. Creative Commons makes its licenses and related
+information available on an "as-is" basis. Creative Commons gives no
+warranties regarding its licenses, any material licensed under their
+terms and conditions, or any related information. Creative Commons
+disclaims all liability for damages resulting from their use to the
+fullest extent possible.
+
+Using Creative Commons Public Licenses
+
+Creative Commons public licenses provide a standard set of terms and
+conditions that creators and other rights holders may use to share
+original works of authorship and other material subject to copyright
+and certain other rights specified in the public license below. The
+following considerations are for informational purposes only, are not
+exhaustive, and do not form part of our licenses.
+
+ Considerations for licensors: Our public licenses are
+ intended for use by those authorized to give the public
+ permission to use material in ways otherwise restricted by
+ copyright and certain other rights. Our licenses are
+ irrevocable. Licensors should read and understand the terms
+ and conditions of the license they choose before applying it.
+ Licensors should also secure all rights necessary before
+ applying our licenses so that the public can reuse the
+ material as expected. Licensors should clearly mark any
+ material not subject to the license. This includes other CC-
+ licensed material, or material used under an exception or
+ limitation to copyright. More considerations for licensors:
+ wiki.creativecommons.org/Considerations_for_licensors
+
+ Considerations for the public: By using one of our public
+ licenses, a licensor grants the public permission to use the
+ licensed material under specified terms and conditions. If
+ the licensor's permission is not necessary for any reason--for
+ example, because of any applicable exception or limitation to
+ copyright--then that use is not regulated by the license. Our
+ licenses grant only permissions under copyright and certain
+ other rights that a licensor has authority to grant. Use of
+ the licensed material may still be restricted for other
+ reasons, including because others have copyright or other
+ rights in the material. A licensor may make special requests,
+ such as asking that all changes be marked or described.
+ Although not required by our licenses, you are encouraged to
+ respect those requests where reasonable. More_considerations
+ for the public:
+ wiki.creativecommons.org/Considerations_for_licensees
+
+=======================================================================
+
+Creative Commons Attribution-ShareAlike 4.0 International Public
+License
+
+By exercising the Licensed Rights (defined below), You accept and agree
+to be bound by the terms and conditions of this Creative Commons
+Attribution-ShareAlike 4.0 International Public License ("Public
+License"). To the extent this Public License may be interpreted as a
+contract, You are granted the Licensed Rights in consideration of Your
+acceptance of these terms and conditions, and the Licensor grants You
+such rights in consideration of benefits the Licensor receives from
+making the Licensed Material available under these terms and
+conditions.
+
+
+Section 1 -- Definitions.
+
+ a. Adapted Material means material subject to Copyright and Similar
+ Rights that is derived from or based upon the Licensed Material
+ and in which the Licensed Material is translated, altered,
+ arranged, transformed, or otherwise modified in a manner requiring
+ permission under the Copyright and Similar Rights held by the
+ Licensor. For purposes of this Public License, where the Licensed
+ Material is a musical work, performance, or sound recording,
+ Adapted Material is always produced where the Licensed Material is
+ synched in timed relation with a moving image.
+
+ b. Adapter's License means the license You apply to Your Copyright
+ and Similar Rights in Your contributions to Adapted Material in
+ accordance with the terms and conditions of this Public License.
+
+ c. BY-SA Compatible License means a license listed at
+ creativecommons.org/compatiblelicenses, approved by Creative
+ Commons as essentially the equivalent of this Public License.
+
+ d. Copyright and Similar Rights means copyright and/or similar rights
+ closely related to copyright including, without limitation,
+ performance, broadcast, sound recording, and Sui Generis Database
+ Rights, without regard to how the rights are labeled or
+ categorized. For purposes of this Public License, the rights
+ specified in Section 2(b)(1)-(2) are not Copyright and Similar
+ Rights.
+
+ e. Effective Technological Measures means those measures that, in the
+ absence of proper authority, may not be circumvented under laws
+ fulfilling obligations under Article 11 of the WIPO Copyright
+ Treaty adopted on December 20, 1996, and/or similar international
+ agreements.
+
+ f. Exceptions and Limitations means fair use, fair dealing, and/or
+ any other exception or limitation to Copyright and Similar Rights
+ that applies to Your use of the Licensed Material.
+
+ g. License Elements means the license attributes listed in the name
+ of a Creative Commons Public License. The License Elements of this
+ Public License are Attribution and ShareAlike.
+
+ h. Licensed Material means the artistic or literary work, database,
+ or other material to which the Licensor applied this Public
+ License.
+
+ i. Licensed Rights means the rights granted to You subject to the
+ terms and conditions of this Public License, which are limited to
+ all Copyright and Similar Rights that apply to Your use of the
+ Licensed Material and that the Licensor has authority to license.
+
+ j. Licensor means the individual(s) or entity(ies) granting rights
+ under this Public License.
+
+ k. Share means to provide material to the public by any means or
+ process that requires permission under the Licensed Rights, such
+ as reproduction, public display, public performance, distribution,
+ dissemination, communication, or importation, and to make material
+ available to the public including in ways that members of the
+ public may access the material from a place and at a time
+ individually chosen by them.
+
+ l. Sui Generis Database Rights means rights other than copyright
+ resulting from Directive 96/9/EC of the European Parliament and of
+ the Council of 11 March 1996 on the legal protection of databases,
+ as amended and/or succeeded, as well as other essentially
+ equivalent rights anywhere in the world.
+
+ m. You means the individual or entity exercising the Licensed Rights
+ under this Public License. Your has a corresponding meaning.
+
+
+Section 2 -- Scope.
+
+ a. License grant.
+
+ 1. Subject to the terms and conditions of this Public License,
+ the Licensor hereby grants You a worldwide, royalty-free,
+ non-sublicensable, non-exclusive, irrevocable license to
+ exercise the Licensed Rights in the Licensed Material to:
+
+ a. reproduce and Share the Licensed Material, in whole or
+ in part; and
+
+ b. produce, reproduce, and Share Adapted Material.
+
+ 2. Exceptions and Limitations. For the avoidance of doubt, where
+ Exceptions and Limitations apply to Your use, this Public
+ License does not apply, and You do not need to comply with
+ its terms and conditions.
+
+ 3. Term. The term of this Public License is specified in Section
+ 6(a).
+
+ 4. Media and formats; technical modifications allowed. The
+ Licensor authorizes You to exercise the Licensed Rights in
+ all media and formats whether now known or hereafter created,
+ and to make technical modifications necessary to do so. The
+ Licensor waives and/or agrees not to assert any right or
+ authority to forbid You from making technical modifications
+ necessary to exercise the Licensed Rights, including
+ technical modifications necessary to circumvent Effective
+ Technological Measures. For purposes of this Public License,
+ simply making modifications authorized by this Section 2(a)
+ (4) never produces Adapted Material.
+
+ 5. Downstream recipients.
+
+ a. Offer from the Licensor -- Licensed Material. Every
+ recipient of the Licensed Material automatically
+ receives an offer from the Licensor to exercise the
+ Licensed Rights under the terms and conditions of this
+ Public License.
+
+ b. Additional offer from the Licensor -- Adapted Material.
+ Every recipient of Adapted Material from You
+ automatically receives an offer from the Licensor to
+ exercise the Licensed Rights in the Adapted Material
+ under the conditions of the Adapter's License You apply.
+
+ c. No downstream restrictions. You may not offer or impose
+ any additional or different terms or conditions on, or
+ apply any Effective Technological Measures to, the
+ Licensed Material if doing so restricts exercise of the
+ Licensed Rights by any recipient of the Licensed
+ Material.
+
+ 6. No endorsement. Nothing in this Public License constitutes or
+ may be construed as permission to assert or imply that You
+ are, or that Your use of the Licensed Material is, connected
+ with, or sponsored, endorsed, or granted official status by,
+ the Licensor or others designated to receive attribution as
+ provided in Section 3(a)(1)(A)(i).
+
+ b. Other rights.
+
+ 1. Moral rights, such as the right of integrity, are not
+ licensed under this Public License, nor are publicity,
+ privacy, and/or other similar personality rights; however, to
+ the extent possible, the Licensor waives and/or agrees not to
+ assert any such rights held by the Licensor to the limited
+ extent necessary to allow You to exercise the Licensed
+ Rights, but not otherwise.
+
+ 2. Patent and trademark rights are not licensed under this
+ Public License.
+
+ 3. To the extent possible, the Licensor waives any right to
+ collect royalties from You for the exercise of the Licensed
+ Rights, whether directly or through a collecting society
+ under any voluntary or waivable statutory or compulsory
+ licensing scheme. In all other cases the Licensor expressly
+ reserves any right to collect such royalties.
+
+
+Section 3 -- License Conditions.
+
+Your exercise of the Licensed Rights is expressly made subject to the
+following conditions.
+
+ a. Attribution.
+
+ 1. If You Share the Licensed Material (including in modified
+ form), You must:
+
+ a. retain the following if it is supplied by the Licensor
+ with the Licensed Material:
+
+ i. identification of the creator(s) of the Licensed
+ Material and any others designated to receive
+ attribution, in any reasonable manner requested by
+ the Licensor (including by pseudonym if
+ designated);
+
+ ii. a copyright notice;
+
+ iii. a notice that refers to this Public License;
+
+ iv. a notice that refers to the disclaimer of
+ warranties;
+
+ v. a URI or hyperlink to the Licensed Material to the
+ extent reasonably practicable;
+
+ b. indicate if You modified the Licensed Material and
+ retain an indication of any previous modifications; and
+
+ c. indicate the Licensed Material is licensed under this
+ Public License, and include the text of, or the URI or
+ hyperlink to, this Public License.
+
+ 2. You may satisfy the conditions in Section 3(a)(1) in any
+ reasonable manner based on the medium, means, and context in
+ which You Share the Licensed Material. For example, it may be
+ reasonable to satisfy the conditions by providing a URI or
+ hyperlink to a resource that includes the required
+ information.
+
+ 3. If requested by the Licensor, You must remove any of the
+ information required by Section 3(a)(1)(A) to the extent
+ reasonably practicable.
+
+ b. ShareAlike.
+
+ In addition to the conditions in Section 3(a), if You Share
+ Adapted Material You produce, the following conditions also apply.
+
+ 1. The Adapter's License You apply must be a Creative Commons
+ license with the same License Elements, this version or
+ later, or a BY-SA Compatible License.
+
+ 2. You must include the text of, or the URI or hyperlink to, the
+ Adapter's License You apply. You may satisfy this condition
+ in any reasonable manner based on the medium, means, and
+ context in which You Share Adapted Material.
+
+ 3. You may not offer or impose any additional or different terms
+ or conditions on, or apply any Effective Technological
+ Measures to, Adapted Material that restrict exercise of the
+ rights granted under the Adapter's License You apply.
+
+
+Section 4 -- Sui Generis Database Rights.
+
+Where the Licensed Rights include Sui Generis Database Rights that
+apply to Your use of the Licensed Material:
+
+ a. for the avoidance of doubt, Section 2(a)(1) grants You the right
+ to extract, reuse, reproduce, and Share all or a substantial
+ portion of the contents of the database;
+
+ b. if You include all or a substantial portion of the database
+ contents in a database in which You have Sui Generis Database
+ Rights, then the database in which You have Sui Generis Database
+ Rights (but not its individual contents) is Adapted Material,
+
+ including for purposes of Section 3(b); and
+ c. You must comply with the conditions in Section 3(a) if You Share
+ all or a substantial portion of the contents of the database.
+
+For the avoidance of doubt, this Section 4 supplements and does not
+replace Your obligations under this Public License where the Licensed
+Rights include other Copyright and Similar Rights.
+
+
+Section 5 -- Disclaimer of Warranties and Limitation of Liability.
+
+ a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
+ EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
+ AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
+ ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
+ IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
+ WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
+ ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
+ KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
+ ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
+
+ b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
+ TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
+ NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
+ INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
+ COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
+ USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
+ ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
+ DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
+ IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
+
+ c. The disclaimer of warranties and limitation of liability provided
+ above shall be interpreted in a manner that, to the extent
+ possible, most closely approximates an absolute disclaimer and
+ waiver of all liability.
+
+
+Section 6 -- Term and Termination.
+
+ a. This Public License applies for the term of the Copyright and
+ Similar Rights licensed here. However, if You fail to comply with
+ this Public License, then Your rights under this Public License
+ terminate automatically.
+
+ b. Where Your right to use the Licensed Material has terminated under
+ Section 6(a), it reinstates:
+
+ 1. automatically as of the date the violation is cured, provided
+ it is cured within 30 days of Your discovery of the
+ violation; or
+
+ 2. upon express reinstatement by the Licensor.
+
+ For the avoidance of doubt, this Section 6(b) does not affect any
+ right the Licensor may have to seek remedies for Your violations
+ of this Public License.
+
+ c. For the avoidance of doubt, the Licensor may also offer the
+ Licensed Material under separate terms or conditions or stop
+ distributing the Licensed Material at any time; however, doing so
+ will not terminate this Public License.
+
+ d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
+ License.
+
+
+Section 7 -- Other Terms and Conditions.
+
+ a. The Licensor shall not be bound by any additional or different
+ terms or conditions communicated by You unless expressly agreed.
+
+ b. Any arrangements, understandings, or agreements regarding the
+ Licensed Material not stated herein are separate from and
+ independent of the terms and conditions of this Public License.
+
+
+Section 8 -- Interpretation.
+
+ a. For the avoidance of doubt, this Public License does not, and
+ shall not be interpreted to, reduce, limit, restrict, or impose
+ conditions on any use of the Licensed Material that could lawfully
+ be made without permission under this Public License.
+
+ b. To the extent possible, if any provision of this Public License is
+ deemed unenforceable, it shall be automatically reformed to the
+ minimum extent necessary to make it enforceable. If the provision
+ cannot be reformed, it shall be severed from this Public License
+ without affecting the enforceability of the remaining terms and
+ conditions.
+
+ c. No term or condition of this Public License will be waived and no
+ failure to comply consented to unless expressly agreed to by the
+ Licensor.
+
+ d. Nothing in this Public License constitutes or may be interpreted
+ as a limitation upon, or waiver of, any privileges and immunities
+ that apply to the Licensor or You, including from the legal
+ processes of any jurisdiction or authority.
+
+
+=======================================================================
+
+Creative Commons is not a party to its public
+licenses. Notwithstanding, Creative Commons may elect to apply one of
+its public licenses to material it publishes and in those instances
+will be considered the “Licensor.” The text of the Creative Commons
+public licenses is dedicated to the public domain under the CC0 Public
+Domain Dedication. Except for the limited purpose of indicating that
+material is shared under a Creative Commons public license or as
+otherwise permitted by the Creative Commons policies published at
+creativecommons.org/policies, Creative Commons does not authorize the
+use of the trademark "Creative Commons" or any other trademark or logo
+of Creative Commons without its prior written consent including,
+without limitation, in connection with any unauthorized modifications
+to any of its public licenses or any other arrangements,
+understandings, or agreements concerning use of licensed material. For
+the avoidance of doubt, this paragraph does not form part of the
+public licenses.
+
+Creative Commons may be contacted at creativecommons.org.
diff --git a/LICENSE_CN b/LICENSE_CN
new file mode 100644
index 000000000..fc5638bfb
--- /dev/null
+++ b/LICENSE_CN
@@ -0,0 +1,109 @@
+知识共享组织(“Creative Commons”,“知识共享”) 不是一家律师事务所,也不对外提供法律服务或建议。提供知识共享公共许可协议(Creative Commons Public License,以下缩写为CCPL)并不导致“律师—当事人”关系或其他法律关系的建立。知识共享(Creative Commons)按其现状提供协议文本和相关信息。知识共享(Creative Commons)对于其许可协议,或通过该协议提供的作品(material)或相关信息不提供任何担保,在最大可能程度内,也不对因使用其协议或通过该协议提供的作品(material)或信息而造成的损失承担损害赔偿责任。
+
+知识共享公共许可协议(CCPL)的使用
+
+知识共享公共许可协议(CCPL)提供一套标准化的条款供作者或其他权利人使用,以便他们分享其原创作品(original works of authorship)和其他受著作权和以下公共许可协议中提及的其他特定权利保护的作品(material)。以下考虑因素仅供参考,并未列举穷尽,也不属于本公共许可协议文本的一部分。
+
+许可人应当考虑的因素: 知识共享(Creative Commons)公共许可协议是供那些有权许可公众以著作权或其他特定权利所保护的方式使用其作品(material)的权利人使用的。知识共享(Creative Commons)协议不可撤销。因此,在采用协议前,许可人应当阅读并理解其所选择的协议条款。许可人亦应确保其享有所授予公众的权利,以便公众能够顺利的使用授权作品(material)。许可人应当清楚地注明协议所不适用的作品(material),包括其他采用知识共享协议授权的作品(material),或基于著作权的例外或限制而使用的作品(material)。许可人应该考虑的其他因素
+
+
+公众应当考虑的因素: 许可人通过使用知识共享(Creative Commons)协议,授权公众依据特定的条款和条件使用授权作品(Licensed Material)。如果因为任何原因不需要授权即可使用该作品(material)——例如基于著作权的例外或限制——那么该使用并不受本协议约束。许可人通过知识共享(Creative Commons)协议只能授予基于著作权或其他特定权利有权授予的许可。使用授权作品(Licensed Material)可能还受到其他情形的限制,包括第三方对该作品(material)所享有的著作权或其他权利。许可人可能做出其他特别要求,比如要求使用者标注或说明对作品(material)的所有更改。即使本协议没有要求,知识共享(Creative Commons)仍然鼓励您遵守许可人的合理要求。 公众应当考虑的其他因素
+
+
+知识共享 (Creative Commons) 署名—相同方式共享 4.0公共许可协议国际版
+通过行使本协议所授予的权利(定义如下),您接受并同意受到知识共享(Creative Commons)署名—相同方式共享4.0国际公共许可协议(以下简称“本公共许可协议”)的约束。从合同解释的角度来看,您获得授权的对价是接受本协议的条款,许可人授予您这些权利的对价是可以通过采用本协议条款发布授权作品(material)而获得利益。
+
+第一条 定义
+
+演绎作品(Adapted Material): 指受到著作权与类似权利保护的,基于授权作品(Licensed Material)而创作的作品(material),例如对授权作品(Licensed Material)的翻译、改编、编排、改写或其他依据著作权与类似权利需要获得所有人许可的修改。为本公共许可协议之目的,当授权作品(Licensed Material)为音乐作品、表演或录音时,将其依时间序列关系与动态影像配合一致而形成的作品,视为演绎作品(Adapted Material)。
+演绎作者的许可: 指您依据本公共许可协议对在演绎作品(Adapted Material)中自己所贡献的部分所享有的著作权与类似权利进行授权的协议。
+署名—相同方式共享兼容协议: 指在 creativecommons.org/compatiblelicenses 上列出且经知识共享组织(Creative Commons)认可、实质上与本公共许可协议相当的协议。
+著作权与类似权利: 指著作权和/或与著作权紧密联系的类似权利。类似权利包括但不限于:表演者权、广播组织权、录音录像制作者权、以及数据库特别权利,而不论上述权利的定义和归类如何。为本公共许可协议之目的, 第二条b款第(1)项与第(2)项 所列权利不属于著作权与类似权利。
+有效的技术措施: 指根据各司法管辖区遵循《世界知识产权组织版权条约》(1996年12月20日通过)第十一条或类似国际协定项下的义务所制定的法律,在没有适当的授权的情况下,禁止使用者规避的技术措施。
+例外与限制: 指合理使用(Fair Dealing and Fair Use)和/或其他适用于您对授权作品(Licensed Material)的使用的著作权与类似权利的例外或限制。
+授权要素: 指知识共享公共许可协议(CCPL)名称中所包含的协议特征。本公共许可协议的授权要素包括:署名和相同方式共享。
+授权作品(Licensed Material): 指许可人通过本公共许可协议授权的文学、艺术作品(artistic or literary work),数据库或其他作品(material)。
+协议所授予的权利: 指依据本公共许可协议的条款和条件所授予您的各项权利,限于适用于您对授权作品(Licensed Material)的使用且许可人有权许可的著作权与类似权利。
+许可人: 指通过本公共许可协议进行授权的个人或组织。
+分享: 指以需要“协议所授予的权利”许可的任何方法或程序向公众提供作品(material),包括复制、公共展示、公开表演、发行、散布、传播、进口或提供作品(material)给公众以便其能在其选定的时间和地点接收作品(material)。
+数据库特别权利: 指除了著作权之外,衍生于1996年3月11日通过的《欧洲议会与欧盟理事会关于数据库法律保护的指令》(Directive 96/9/EC)及其修改或后续版本的权利,或其他国家或地区本质上与之等同的权利。
+您: 指依据本公共许可协议行使其所获得授予之权利的个人或机构。 “您的” 有相应的含义。
+第二条 授权范围
+
+授权
+根据本公共许可协议的条款,许可人授予您在全球范围内,免费的、不可再许可、非独占、不可撤销的许可,以对授权作品(Licensed Material)行使以下“协议所授予的权利”:
+复制和分享授权作品(Licensed Material)的全部或部分;以及
+创作、复制和分享演绎作品(Adapted Material)。
+例外和限制 为避免疑义,若著作权的例外和限制适用于您对授权作品(Licensed Material)的使用,本公共许可协议将不适用,您也无须遵守本公共许可协议之条款。
+期限 本公共许可协议的期限规定于第六条 a 款。
+媒介和形式;允许的技术修改 许可人授权您在任何媒介以任何形式(不论目前已知的或未来出现的)行使本协议授予的权利,并为之进行必要的技术修改。许可人放弃和/或同意不主张任何权利以阻止您为了行使协议项下权利进行必要的技术修改,包括为规避有效技术措施所必须的技术修改。为了本公共许可协议之目的, 基于第二条a款第(4)项 进行的技术修改不构成演绎作品(Adapted Material)。
+后续接受者
+来自许可人的要约——授权作品(Licensed Material) 本授权作品(Licensed Material)的每一个后续接受者都自动取得许可人的要约,以按照本公共许可协议的条款行使协议授予的权利。
+来自许可人的额外要约——演绎作品(Adapted Material) 您基于授权作品(Licensed Material)创作的演绎作品(Adapted Material)的每一个后续接受者都自动取得许可人的要约,以按照您所适用的“演绎作者的许可”协议的条款行使协议所授予的权利。
+禁止下游限制 若会限制授权作品(Licensed Material)后续接受者行使本协议所授予的权利,则您不得对授权作品(Licensed Material)提出或增加任何额外的或不同的条款,或使用任何有效技术措施。
+并非背书 本公共许可协议不构成、或不得被解释为允许您声明或主张:您或您对授权作品(Licensed Material)的使用与许可人或 第三条a款第(1)项(A)目(i)所规定要求提供署名的权利人相关联,或得到其赞助、同意或被授予正式地位。
+其他权利
+
+依据本公共许可协议,著作人身权,例如保护作品完整权、形象权、隐私权或其他类似的人格权利,不在许可范围内。但是,在条件允许的情况下,许可人可以在必要范围内放弃和/或同意不主张其权利,以便您行使本协议所授予的权利。
+本公共许可协议不适用于任何专利权或商标权许可。
+在自愿的或可放弃的法定或强制许可机制下,许可人在最大可能范围内放弃对您因行使本协议所授予的权利而产生的使用费的权利,不论是直接收取或通过集体管理组织收取。在其他任何情况下,许可人明确保留收取使用费的任何权利。
+第三条 授权条件
+
+您行使被许可的权利明确受以下条件限制:
+
+署名
+
+若您分享本授权作品(Licensed Material)(包含修改格式),您必须:
+
+保留如下标识(如果许可人提供授权作品(Licensed Material)的同时提供如下标识):
+以许可人要求的任何合理方式,标识本授权作品(Licensed Material)创作者和其他被指定署名的人的身份(包括指定的笔名);
+著作权声明;
+有关本公共许可协议的声明;
+有关免责的声明;
+在合理可行情况下,本授权作品(Licensed Material)的网址(URI)或超链接;
+表明您是否修改本授权作品(Licensed Material)及保留任何先前修改的标记;及
+表明授权作品(Licensed Material)依据本公共许可协议授权,并提供本公共许可协议全文,或者本公共许可协议的网址(URI)或超链接。
+依据您分享本授权作品(Licensed Material)的媒介、方法及情況,您可以采用任何合理方式满足第三条a款第(1)项的条件 。 例如,提供包含所要求信息来源的网址(URI)或超链接可算是合理地满足此处的条件。
+如果许可人要求,您必须在合理可行的范围内移除第三条a款第(1)项(A)目 所要求的任何信息。
+相同方式共享
+除第三条a款的条件外,如果您分享您创作的演绎作品(Adapted Material),则下列条件也适用:
+
+您适用的“演绎作者的许可”协议必须是与本许可协议具有相同授权要素的知识共享(Creative Commons)许可协议(可以是本版本或后续版本),或者其他与“署名-相同方式共享”协议兼容的许可协议。
+您必须提供您适用的“演绎作者的许可”协议全文或者该许可协议的网址(URI)或超链接。依据您分享您的演绎作品(Adapted Material)所使用的媒介、方法及情況,您可以采用任何合理方式满足此条件。
+您不得提出或施加任何附加或不同的条款或条件、或在演绎作品(Adapted Material)上应用任何有效的技术措施,以限制使用者行使依您所适用的“演绎作者的许可”协议所授予的权利。
+第四条 数据库特别权利
+
+当协议所授予的权利包含数据库特别权利,而该数据库特别权利适用于您对授权作品(Licensed Material)的使用时:
+
+为避免疑义, 第二条a款第(1) 项授权您,摘录、再利用、复制和分享全部或绝大部分数据库资料;
+如果您将数据库资料的全部或绝大部分纳入您享有数据库特别权利的另一数据库,则您享有数据库特别权利的该数据库(而非其中的单个内容)视为演绎作品(Adapted Material),适用第三条b款的要求;
+如果您分享全部或大部分该数据库的资料,您必须遵守 第三条a款 规定的条件。
+为避免疑义,当协议所授予的权利包含其他著作权与类似权利时,第四条补充且不取代本公共许可协议所规定的您的义务。
+第五条 免责声明及责任限制条款
+
+除非许可人另有保证,否则在最大可能范围内,许可人按其现状和现有之基础提供授权作品(Licensed Material),且没有就授权作品(Licensed Material)做出任何形式的陈述或保证:无论明示、默示、法定或其他形式,包括但不限于任何有关本授权作品(Licensed Material)的权属保证、可交易性、适于特定目的、未侵害他人权利、没有潜在或其他瑕疵、精确性或是否有错误,不管是否已知或可发现。当免责声明全部或部分不被允许时,此免责声明可能不适用于您。
+在最大可能范围内, 对于任何因本公共许可协议或使用授权作品(Licensed Material)引起的直接的、特殊的、间接的、附随的、连带的、惩罚性的、警告性的,或其他的损失、成本、费用或损害,许可人不对您负任何法律上或其他的责任(包括但不限于过失责任)。当责任限制部分或全部不被允许时,该限制不适用于您。
+前述免责及责任限制声明,应尽可能以最接近于完全排除全部责任的方式解释。
+第六条 期限与终止
+
+本公共许可协议在著作权与类似权利存续期间内有效。然而,如果您没有遵守此公共许可协议,则您依据此公共许可协议享有的权利自动终止。
+当您使用本授权作品(Licensed Material)的权利根据第六条a款终止时,您的权利在下述情况下恢复:
+
+自违反协议的行为纠正之日起自动恢复,但须在您发现违反情形后30日内纠正;或
+根据许可人明示恢复权利的意思表达。
+为避免疑义,本公共许可协议第六条b款 不影响许可人就您违反本公共许可协议的行为寻求法律救济。
+为避免疑义,许可人也可在任何时间,以另外的条款或条件提供本授权作品(Licensed Material),或者停止传播本授权作品(Licensed Material);然而,许可人此种行为不会终止本公共许可协议。
+本协议第一、五、六、七及第八条,不因本公共许可协议终止而失效。
+第七条 其他条款和条件
+
+除非明示同意,否则许可人不受您表达的任何附加或不同条款或条件约束。
+本公共许可协议未提及的关于授权作品(Licensed Material)之任何安排、共识或协议,不属于且独立于本公共许可协议的条款及条件。
+第八条 解释
+
+为避免疑义,本许可协议不会也不应被解释为减少、限制、约束或施加条件于无需本公共许可协议授权即可依法行使的对授权作品(Licensed Material)的任何使用。
+在最大可能范围内,如果本公共许可协议的任何条款被视为无法执行,该条款在必要的最小限度内,自动调整至可以执行。如果该条款不能被调整,其应自本公共许可协议中排除适用,不影响其余条款的效力。
+除非许可人明示同意,本公共许可协议的任何条款或条件均不得放弃。
+本公共许可协议条款不构成、也不得被解释为限制或者放弃适用于许可人或您的特权或豁免,包括豁免于任何司法管辖区或行政机构的法律程序。
+知识共享组织(Creative Commons)不是其公共许可协议的一方 。尽管如此,知识共享组织(Creative Commons)可以选择其中一种公共许可协议适用于其发表的作品(material),在此种情况下被视为许可人。知识共享公共许可协议 (CCPL)的文本属于公共领域,适用CC0公共许可协议。 除用于向公众表明本作品(material)是依照知识共享(Creative Commons)公共许可协议授权,或发布于creativecommons.org/policies的知识共享组织(Creative Commons)政策另有规定允许以外,如未经知识共享组织(Creative Commons)事先书面同意,任何一方均不得使用“知识共享”(Creative Commons)商标和其他相关商标及标识。包括但不限于,将该商标或标识用于对知识共享公共许可协议(CCPL)做出的未经同意的修改,或与授权作品(Licensed Material)使用有关的其他安排、共识或协议。为避免疑义,本段关于商标的限制性规定不构成公共许可协议之一部分。
+
+您可以点击此处联系知识共享(Creative Commons)组织。
diff --git a/README.MD b/README.MD
new file mode 100644
index 000000000..71f4df3f4
--- /dev/null
+++ b/README.MD
@@ -0,0 +1,11 @@
+## Hexo站点文件
+[![Deploy Hexo Themes Anzhiyu on GitHub Pages](https://github.com/Guangsudalao/Blog/actions/workflows/Deploy-Hexo-Themes-Anzhiyu.yml/badge.svg?branch=anzhiyu)](https://github.com/Guangsudalao/Blog/actions/workflows/Deploy-Hexo-Themes-Anzhiyu.yml)
+[![Netlify Status](https://api.netlify.com/api/v1/badges/572a3754-36c6-43ef-ab1b-03260b99144b/deploy-status)](https://app.netlify.com/sites/gsdl/deploys)
+# 构建[Hexo](https://github.com/hexojs/hexo)与主题[Hexo-Themes-Anzhiyu](https://github.com/anzhiyu-c/hexo-theme-anzhiyu)
+## 主线
+https://blog.dlya.top
+## 备用线路
+https://guangsudalao.github.io
+https://gsdl.vercel.app
+https://gsdl.netlify.app
+https://gsdl.pages.dev
diff --git a/about/index.html b/about/index.html
new file mode 100644
index 000000000..d6291ccad
--- /dev/null
+++ b/about/index.html
@@ -0,0 +1,522 @@
+
关于我 | 光速大佬的小站
+
关于我 访问统计
+
+
+
+
+
+
状态 • 不定时coding。 • 游戏:Minecraft、原神、Terraria
+
©版权 本博客所有文章除特别声明外,均采用知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议 进行许可。如果你要转载文章,请询问作者,并以相同方式进行非商业性搬运,且转载请注明出处!
+
联系我
+
+
\ No newline at end of file
diff --git a/ads.txt b/ads.txt
new file mode 100644
index 000000000..32f9f8a35
--- /dev/null
+++ b/ads.txt
@@ -0,0 +1 @@
+google.com, pub-1740703495762927, DIRECT, f08c47fec0942fa0
diff --git a/anzhiyu/random.js b/anzhiyu/random.js
new file mode 100644
index 000000000..a71102990
--- /dev/null
+++ b/anzhiyu/random.js
@@ -0,0 +1,56 @@
+var posts=["posts/2022.11.12/","posts/3coutryfun/","posts/cloudreve-render/","posts/free-hosting/","posts/freenom_with_cloudflare/","posts/github_2fa/","posts/hexo-auto-deploy-2/","posts/hexo-auto-deploy/","posts/planetscale_ban/","posts/runexe/","posts/vercel_better_ip/","posts/vercel_github/","posts/vercel_short_link/","posts/warn-domain/"];function toRandomPost(){
+ pjax.loadUrl('/'+posts[Math.floor(Math.random() * posts.length)]);
+ };var friend_link_list=[{"name":"岚天小窝","link":"https://blog.ltya.top","avatar":"https://cravatar.cn/avatar/5af06b461740fb2bd7467b8561399703?s=500&d=mm&r=g","descr":"一只鸽子住在这里~"},{"name":"Qinai’s Blog","link":"https://slqinai.cn","avatar":"https://q1.qlogo.cn/g?b=qq&nk=2140137093&s=640","descr":"夜の星もいつかは消える"},{"name":"邪帝家族的博客","link":"https://blog.whitegx.top","avatar":"https://cravatar.cn/avatar/9ba5c3066897b869698d222cde022ee8?s=500&d=mm&r=g","descr":"还没想好说什么~"},{"name":"序炁的博客","link":"https://www.ordchaos.com","avatar":"https://www.ordchaos.com/img/avatar.jpg","descr":"A dragon that born in the Order and Chaos."},{"name":"LZHの小窝","link":"https://blog.lzh.life","avatar":"https://cravatar.cn/avatar/ed393c2e64cb768039a62f8f14178ca0?s=500&d=mm&r=g","descr":"凡是过往,皆为序章"}];
+ var refreshNum = 1;
+ function friendChainRandomTransmission() {
+ const randomIndex = Math.floor(Math.random() * friend_link_list.length);
+ const { name, link } = friend_link_list.splice(randomIndex, 1)[0];
+ Snackbar.show({
+ text:
+ "点击前往按钮进入随机一个友链,不保证跳转网站的安全性和可用性。本次随机到的是本站友链:「" + name + "」",
+ duration: 8000,
+ pos: "top-center",
+ actionText: "前往",
+ onActionClick: function (element) {
+ element.style.opacity = 0;
+ window.open(link, "_blank");
+ },
+ });
+ }
+ function addFriendLinksInFooter() {
+ var footerRandomFriendsBtn = document.getElementById("footer-random-friends-btn");
+ if(!footerRandomFriendsBtn) return;
+ footerRandomFriendsBtn.style.opacity = "0.2";
+ footerRandomFriendsBtn.style.transitionDuration = "0.3s";
+ footerRandomFriendsBtn.style.transform = "rotate(" + 360 * refreshNum++ + "deg)";
+ const finalLinkList = [];
+
+ let count = 0;
+
+ while (friend_link_list.length && count < 3) {
+ const randomIndex = Math.floor(Math.random() * friend_link_list.length);
+ const { name, link, avatar } = friend_link_list.splice(randomIndex, 1)[0];
+
+ finalLinkList.push({
+ name,
+ link,
+ avatar,
+ });
+ count++;
+ }
+
+ let html = finalLinkList
+ .map(({ name, link }) => {
+ const returnInfo = ""
+ return returnInfo;
+ })
+ .join("");
+
+ html += "";
+
+ document.getElementById("friend-links-in-footer").innerHTML = html;
+
+ setTimeout(()=>{
+ footerRandomFriendsBtn.style.opacity = "1";
+ }, 300)
+ };
\ No newline at end of file
diff --git a/archives/2021/08/index.html b/archives/2021/08/index.html
new file mode 100644
index 000000000..ec16114a4
--- /dev/null
+++ b/archives/2021/08/index.html
@@ -0,0 +1,481 @@
+八月 2021 | 光速大佬的小站
+
\ No newline at end of file
diff --git a/archives/2021/09/index.html b/archives/2021/09/index.html
new file mode 100644
index 000000000..a23a8f5f6
--- /dev/null
+++ b/archives/2021/09/index.html
@@ -0,0 +1,481 @@
+九月 2021 | 光速大佬的小站
+
\ No newline at end of file
diff --git a/archives/2021/11/index.html b/archives/2021/11/index.html
new file mode 100644
index 000000000..47b73cb2c
--- /dev/null
+++ b/archives/2021/11/index.html
@@ -0,0 +1,481 @@
+十一月 2021 | 光速大佬的小站
+
\ No newline at end of file
diff --git a/archives/2021/index.html b/archives/2021/index.html
new file mode 100644
index 000000000..9a77e0413
--- /dev/null
+++ b/archives/2021/index.html
@@ -0,0 +1,481 @@
+2021 | 光速大佬的小站
+
\ No newline at end of file
diff --git a/archives/2022/03/index.html b/archives/2022/03/index.html
new file mode 100644
index 000000000..b00f35078
--- /dev/null
+++ b/archives/2022/03/index.html
@@ -0,0 +1,481 @@
+三月 2022 | 光速大佬的小站
+
\ No newline at end of file
diff --git a/archives/2022/06/index.html b/archives/2022/06/index.html
new file mode 100644
index 000000000..8b560a7df
--- /dev/null
+++ b/archives/2022/06/index.html
@@ -0,0 +1,481 @@
+六月 2022 | 光速大佬的小站
+
\ No newline at end of file
diff --git a/archives/2022/07/index.html b/archives/2022/07/index.html
new file mode 100644
index 000000000..6ae94f366
--- /dev/null
+++ b/archives/2022/07/index.html
@@ -0,0 +1,481 @@
+七月 2022 | 光速大佬的小站
+
\ No newline at end of file
diff --git a/archives/2022/08/index.html b/archives/2022/08/index.html
new file mode 100644
index 000000000..c080882e0
--- /dev/null
+++ b/archives/2022/08/index.html
@@ -0,0 +1,481 @@
+八月 2022 | 光速大佬的小站
+
\ No newline at end of file
diff --git a/archives/2022/11/index.html b/archives/2022/11/index.html
new file mode 100644
index 000000000..1483005f2
--- /dev/null
+++ b/archives/2022/11/index.html
@@ -0,0 +1,481 @@
+十一月 2022 | 光速大佬的小站
+
\ No newline at end of file
diff --git a/archives/2022/index.html b/archives/2022/index.html
new file mode 100644
index 000000000..df17b8747
--- /dev/null
+++ b/archives/2022/index.html
@@ -0,0 +1,481 @@
+2022 | 光速大佬的小站
+
\ No newline at end of file
diff --git a/archives/2023/01/index.html b/archives/2023/01/index.html
new file mode 100644
index 000000000..c99c67aec
--- /dev/null
+++ b/archives/2023/01/index.html
@@ -0,0 +1,481 @@
+一月 2023 | 光速大佬的小站
+
\ No newline at end of file
diff --git a/archives/2023/04/index.html b/archives/2023/04/index.html
new file mode 100644
index 000000000..3b374e45a
--- /dev/null
+++ b/archives/2023/04/index.html
@@ -0,0 +1,481 @@
+四月 2023 | 光速大佬的小站
+
\ No newline at end of file
diff --git a/archives/2023/07/index.html b/archives/2023/07/index.html
new file mode 100644
index 000000000..afc19ea85
--- /dev/null
+++ b/archives/2023/07/index.html
@@ -0,0 +1,481 @@
+七月 2023 | 光速大佬的小站
+
\ No newline at end of file
diff --git a/archives/2023/index.html b/archives/2023/index.html
new file mode 100644
index 000000000..982e67f82
--- /dev/null
+++ b/archives/2023/index.html
@@ -0,0 +1,481 @@
+2023 | 光速大佬的小站
+
\ No newline at end of file
diff --git a/archives/2024/02/index.html b/archives/2024/02/index.html
new file mode 100644
index 000000000..f2fe7f6f5
--- /dev/null
+++ b/archives/2024/02/index.html
@@ -0,0 +1,481 @@
+二月 2024 | 光速大佬的小站
+
\ No newline at end of file
diff --git a/archives/2024/index.html b/archives/2024/index.html
new file mode 100644
index 000000000..e1ad990f4
--- /dev/null
+++ b/archives/2024/index.html
@@ -0,0 +1,481 @@
+2024 | 光速大佬的小站
+
\ No newline at end of file
diff --git a/archives/404/index.html b/archives/404/index.html
new file mode 100644
index 000000000..8b0613d32
--- /dev/null
+++ b/archives/404/index.html
@@ -0,0 +1,473 @@
+404 | 光速大佬的小站
+
\ No newline at end of file
diff --git a/archives/go_link/index.html b/archives/go_link/index.html
new file mode 100644
index 000000000..cf6ef95d8
--- /dev/null
+++ b/archives/go_link/index.html
@@ -0,0 +1,514 @@
+外链跳转收集 | 光速大佬的小站
+
\ No newline at end of file
diff --git a/archives/higher_song.html b/archives/higher_song.html
new file mode 100644
index 000000000..e69de29bb
diff --git a/archives/index.html b/archives/index.html
new file mode 100644
index 000000000..a27de43f0
--- /dev/null
+++ b/archives/index.html
@@ -0,0 +1,481 @@
+归档 | 光速大佬的小站
+
\ No newline at end of file
diff --git a/archives/page/2/index.html b/archives/page/2/index.html
new file mode 100644
index 000000000..47898e416
--- /dev/null
+++ b/archives/page/2/index.html
@@ -0,0 +1,481 @@
+归档 | 光速大佬的小站
+
\ No newline at end of file
diff --git a/archives/yuanshen_ganyu.html b/archives/yuanshen_ganyu.html
new file mode 100644
index 000000000..e69de29bb
diff --git a/archives/yuanshen_zhongli.html b/archives/yuanshen_zhongli.html
new file mode 100644
index 000000000..e69de29bb
diff --git a/assets/algolia/algoliasearch.js b/assets/algolia/algoliasearch.js
new file mode 100644
index 000000000..7707138f1
--- /dev/null
+++ b/assets/algolia/algoliasearch.js
@@ -0,0 +1,7190 @@
+/*! algoliasearch 3.35.1 | © 2014, 2015 Algolia SAS | github.com/algolia/algoliasearch-client-js */
+(function(f){var g;if(typeof window!=='undefined'){g=window}else if(typeof self!=='undefined'){g=self}g.ALGOLIA_MIGRATION_LAYER=f()})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;owindow.ALGOLIA_SUPPORTS_DOCWRITE = true\x3C/script>');
+
+ if (window.ALGOLIA_SUPPORTS_DOCWRITE === true) {
+ document.write('\x3Cscript src="' + v2ScriptUrl + '">\x3C/script>');
+ scriptLoaded('document.write')();
+ } else {
+ loadScript(v2ScriptUrl, scriptLoaded('DOMElement'));
+ }
+ } catch (e) {
+ loadScript(v2ScriptUrl, scriptLoaded('DOMElement'));
+ }
+}
+
+function scriptLoaded(method) {
+ return function log() {
+ var message = 'AlgoliaSearch: loaded V2 script using ' + method;
+
+ if (window.console && window.console.log) {
+ window.console.log(message);
+ }
+ };
+}
+
+},{"1":1}],4:[function(require,module,exports){
+'use strict';
+
+/* eslint no-unused-vars: [2, {"vars": "local"}] */
+
+module.exports = oldGlobals;
+
+// put old window.AlgoliaSearch.. into window. again so that
+// users upgrading to V3 without changing their code, will be warned
+function oldGlobals() {
+ var message = '-- AlgoliaSearch V2 => V3 error --\n' +
+ 'You are trying to use a new version of the AlgoliaSearch JavaScript client with an old notation.\n' +
+ 'Please read our migration guide at https://github.com/algolia/algoliasearch-client-js/wiki/Migration-guide-from-2.x.x-to-3.x.x\n' +
+ '-- /AlgoliaSearch V2 => V3 error --';
+
+ window.AlgoliaSearch = function() {
+ throw new Error(message);
+ };
+
+ window.AlgoliaSearchHelper = function() {
+ throw new Error(message);
+ };
+
+ window.AlgoliaExplainResults = function() {
+ throw new Error(message);
+ };
+}
+
+},{}],5:[function(require,module,exports){
+'use strict';
+
+// This script will be browserified and prepended to the normal build
+// directly in window, not wrapped in any module definition
+// To avoid cases where we are loaded with /latest/ along with
+migrationLayer("algoliasearch");
+
+// Now onto the V2 related code:
+// If the client is using /latest/$BUILDNAME.min.js, load V2 of the library
+//
+// Otherwise, setup a migration layer that will throw on old constructors like
+// new AlgoliaSearch().
+// So that users upgrading from v2 to v3 will have a clear information
+// message on what to do if they did not read the migration guide
+function migrationLayer(buildName) {
+ var isUsingLatest = require(2);
+ var loadV2 = require(3);
+ var oldGlobals = require(4);
+
+ if (isUsingLatest(buildName)) {
+ loadV2(buildName);
+ } else {
+ oldGlobals();
+ }
+}
+
+},{"2":2,"3":3,"4":4}]},{},[5])(5)
+});(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.algoliasearch = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= v31,
+ * and the Firebug extension (any Firefox version) are known
+ * to support "%c" CSS customizations.
+ *
+ * TODO: add a `localStorage` variable to explicitly enable/disable colors
+ */
+
+function useColors() {
+ // NB: In an Electron preload script, document will be defined but not fully
+ // initialized. Since we know we're in Chrome, we'll just detect this case
+ // explicitly
+ if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') {
+ return true;
+ }
+
+ // is webkit? http://stackoverflow.com/a/16459606/376773
+ // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
+ return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||
+ // is firebug? http://stackoverflow.com/a/398120/376773
+ (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||
+ // is firefox >= v31?
+ // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
+ (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||
+ // double check webkit in userAgent just in case we are in a worker
+ (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
+}
+
+/**
+ * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
+ */
+
+exports.formatters.j = function(v) {
+ try {
+ return JSON.stringify(v);
+ } catch (err) {
+ return '[UnexpectedJSONParseError]: ' + err.message;
+ }
+};
+
+
+/**
+ * Colorize log arguments if enabled.
+ *
+ * @api public
+ */
+
+function formatArgs(args) {
+ var useColors = this.useColors;
+
+ args[0] = (useColors ? '%c' : '')
+ + this.namespace
+ + (useColors ? ' %c' : ' ')
+ + args[0]
+ + (useColors ? '%c ' : ' ')
+ + '+' + exports.humanize(this.diff);
+
+ if (!useColors) return;
+
+ var c = 'color: ' + this.color;
+ args.splice(1, 0, c, 'color: inherit')
+
+ // the final "%c" is somewhat tricky, because there could be other
+ // arguments passed either before or after the %c, so we need to
+ // figure out the correct index to insert the CSS into
+ var index = 0;
+ var lastC = 0;
+ args[0].replace(/%[a-zA-Z%]/g, function(match) {
+ if ('%%' === match) return;
+ index++;
+ if ('%c' === match) {
+ // we only are interested in the *last* %c
+ // (the user may have provided their own)
+ lastC = index;
+ }
+ });
+
+ args.splice(lastC, 0, c);
+}
+
+/**
+ * Invokes `console.log()` when available.
+ * No-op when `console.log` is not a "function".
+ *
+ * @api public
+ */
+
+function log() {
+ // this hackery is required for IE8/9, where
+ // the `console.log` function doesn't have 'apply'
+ return 'object' === typeof console
+ && console.log
+ && Function.prototype.apply.call(console.log, console, arguments);
+}
+
+/**
+ * Save `namespaces`.
+ *
+ * @param {String} namespaces
+ * @api private
+ */
+
+function save(namespaces) {
+ try {
+ if (null == namespaces) {
+ exports.storage.removeItem('debug');
+ } else {
+ exports.storage.debug = namespaces;
+ }
+ } catch(e) {}
+}
+
+/**
+ * Load `namespaces`.
+ *
+ * @return {String} returns the previously persisted debug modes
+ * @api private
+ */
+
+function load() {
+ var r;
+ try {
+ r = exports.storage.debug;
+ } catch(e) {}
+
+ // If debug isn't set in LS, and we're in Electron, try to load $DEBUG
+ if (!r && typeof process !== 'undefined' && 'env' in process) {
+ r = process.env.DEBUG;
+ }
+
+ return r;
+}
+
+/**
+ * Enable namespaces listed in `localStorage.debug` initially.
+ */
+
+exports.enable(load());
+
+/**
+ * Localstorage attempts to return the localstorage.
+ *
+ * This is necessary because safari throws
+ * when a user disables cookies/localstorage
+ * and you attempt to access it.
+ *
+ * @return {LocalStorage}
+ * @api private
+ */
+
+function localstorage() {
+ try {
+ return window.localStorage;
+ } catch (e) {}
+}
+
+}).call(this,require(12))
+},{"12":12,"2":2}],2:[function(require,module,exports){
+
+/**
+ * This is the common logic for both the Node.js and web browser
+ * implementations of `debug()`.
+ *
+ * Expose `debug()` as the module.
+ */
+
+exports = module.exports = createDebug.debug = createDebug['default'] = createDebug;
+exports.coerce = coerce;
+exports.disable = disable;
+exports.enable = enable;
+exports.enabled = enabled;
+exports.humanize = require(9);
+
+/**
+ * The currently active debug mode names, and names to skip.
+ */
+
+exports.names = [];
+exports.skips = [];
+
+/**
+ * Map of special "%n" handling functions, for the debug "format" argument.
+ *
+ * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N".
+ */
+
+exports.formatters = {};
+
+/**
+ * Previous log timestamp.
+ */
+
+var prevTime;
+
+/**
+ * Select a color.
+ * @param {String} namespace
+ * @return {Number}
+ * @api private
+ */
+
+function selectColor(namespace) {
+ var hash = 0, i;
+
+ for (i in namespace) {
+ hash = ((hash << 5) - hash) + namespace.charCodeAt(i);
+ hash |= 0; // Convert to 32bit integer
+ }
+
+ return exports.colors[Math.abs(hash) % exports.colors.length];
+}
+
+/**
+ * Create a debugger with the given `namespace`.
+ *
+ * @param {String} namespace
+ * @return {Function}
+ * @api public
+ */
+
+function createDebug(namespace) {
+
+ function debug() {
+ // disabled?
+ if (!debug.enabled) return;
+
+ var self = debug;
+
+ // set `diff` timestamp
+ var curr = +new Date();
+ var ms = curr - (prevTime || curr);
+ self.diff = ms;
+ self.prev = prevTime;
+ self.curr = curr;
+ prevTime = curr;
+
+ // turn the `arguments` into a proper Array
+ var args = new Array(arguments.length);
+ for (var i = 0; i < args.length; i++) {
+ args[i] = arguments[i];
+ }
+
+ args[0] = exports.coerce(args[0]);
+
+ if ('string' !== typeof args[0]) {
+ // anything else let's inspect with %O
+ args.unshift('%O');
+ }
+
+ // apply any `formatters` transformations
+ var index = 0;
+ args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) {
+ // if we encounter an escaped % then don't increase the array index
+ if (match === '%%') return match;
+ index++;
+ var formatter = exports.formatters[format];
+ if ('function' === typeof formatter) {
+ var val = args[index];
+ match = formatter.call(self, val);
+
+ // now we need to remove `args[index]` since it's inlined in the `format`
+ args.splice(index, 1);
+ index--;
+ }
+ return match;
+ });
+
+ // apply env-specific formatting (colors, etc.)
+ exports.formatArgs.call(self, args);
+
+ var logFn = debug.log || exports.log || console.log.bind(console);
+ logFn.apply(self, args);
+ }
+
+ debug.namespace = namespace;
+ debug.enabled = exports.enabled(namespace);
+ debug.useColors = exports.useColors();
+ debug.color = selectColor(namespace);
+
+ // env-specific initialization logic for debug instances
+ if ('function' === typeof exports.init) {
+ exports.init(debug);
+ }
+
+ return debug;
+}
+
+/**
+ * Enables a debug mode by namespaces. This can include modes
+ * separated by a colon and wildcards.
+ *
+ * @param {String} namespaces
+ * @api public
+ */
+
+function enable(namespaces) {
+ exports.save(namespaces);
+
+ exports.names = [];
+ exports.skips = [];
+
+ var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/);
+ var len = split.length;
+
+ for (var i = 0; i < len; i++) {
+ if (!split[i]) continue; // ignore empty strings
+ namespaces = split[i].replace(/\*/g, '.*?');
+ if (namespaces[0] === '-') {
+ exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
+ } else {
+ exports.names.push(new RegExp('^' + namespaces + '$'));
+ }
+ }
+}
+
+/**
+ * Disable debug output.
+ *
+ * @api public
+ */
+
+function disable() {
+ exports.enable('');
+}
+
+/**
+ * Returns true if the given mode name is enabled, false otherwise.
+ *
+ * @param {String} name
+ * @return {Boolean}
+ * @api public
+ */
+
+function enabled(name) {
+ var i, len;
+ for (i = 0, len = exports.skips.length; i < len; i++) {
+ if (exports.skips[i].test(name)) {
+ return false;
+ }
+ }
+ for (i = 0, len = exports.names.length; i < len; i++) {
+ if (exports.names[i].test(name)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Coerce `val`.
+ *
+ * @param {Mixed} val
+ * @return {Mixed}
+ * @api private
+ */
+
+function coerce(val) {
+ if (val instanceof Error) return val.stack || val.message;
+ return val;
+}
+
+},{"9":9}],3:[function(require,module,exports){
+(function (process,global){
+/*!
+ * @overview es6-promise - a tiny implementation of Promises/A+.
+ * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)
+ * @license Licensed under MIT license
+ * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE
+ * @version 4.1.1
+ */
+
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+ typeof define === 'function' && define.amd ? define(factory) :
+ (global.ES6Promise = factory());
+}(this, (function () { 'use strict';
+
+function objectOrFunction(x) {
+ var type = typeof x;
+ return x !== null && (type === 'object' || type === 'function');
+}
+
+function isFunction(x) {
+ return typeof x === 'function';
+}
+
+var _isArray = undefined;
+if (Array.isArray) {
+ _isArray = Array.isArray;
+} else {
+ _isArray = function (x) {
+ return Object.prototype.toString.call(x) === '[object Array]';
+ };
+}
+
+var isArray = _isArray;
+
+var len = 0;
+var vertxNext = undefined;
+var customSchedulerFn = undefined;
+
+var asap = function asap(callback, arg) {
+ queue[len] = callback;
+ queue[len + 1] = arg;
+ len += 2;
+ if (len === 2) {
+ // If len is 2, that means that we need to schedule an async flush.
+ // If additional callbacks are queued before the queue is flushed, they
+ // will be processed by this flush that we are scheduling.
+ if (customSchedulerFn) {
+ customSchedulerFn(flush);
+ } else {
+ scheduleFlush();
+ }
+ }
+};
+
+function setScheduler(scheduleFn) {
+ customSchedulerFn = scheduleFn;
+}
+
+function setAsap(asapFn) {
+ asap = asapFn;
+}
+
+var browserWindow = typeof window !== 'undefined' ? window : undefined;
+var browserGlobal = browserWindow || {};
+var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;
+var isNode = typeof self === 'undefined' && typeof process !== 'undefined' && ({}).toString.call(process) === '[object process]';
+
+// test for web worker but not in IE10
+var isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined';
+
+// node
+function useNextTick() {
+ // node version 0.10.x displays a deprecation warning when nextTick is used recursively
+ // see https://github.com/cujojs/when/issues/410 for details
+ return function () {
+ return process.nextTick(flush);
+ };
+}
+
+// vertx
+function useVertxTimer() {
+ if (typeof vertxNext !== 'undefined') {
+ return function () {
+ vertxNext(flush);
+ };
+ }
+
+ return useSetTimeout();
+}
+
+function useMutationObserver() {
+ var iterations = 0;
+ var observer = new BrowserMutationObserver(flush);
+ var node = document.createTextNode('');
+ observer.observe(node, { characterData: true });
+
+ return function () {
+ node.data = iterations = ++iterations % 2;
+ };
+}
+
+// web worker
+function useMessageChannel() {
+ var channel = new MessageChannel();
+ channel.port1.onmessage = flush;
+ return function () {
+ return channel.port2.postMessage(0);
+ };
+}
+
+function useSetTimeout() {
+ // Store setTimeout reference so es6-promise will be unaffected by
+ // other code modifying setTimeout (like sinon.useFakeTimers())
+ var globalSetTimeout = setTimeout;
+ return function () {
+ return globalSetTimeout(flush, 1);
+ };
+}
+
+var queue = new Array(1000);
+function flush() {
+ for (var i = 0; i < len; i += 2) {
+ var callback = queue[i];
+ var arg = queue[i + 1];
+
+ callback(arg);
+
+ queue[i] = undefined;
+ queue[i + 1] = undefined;
+ }
+
+ len = 0;
+}
+
+function attemptVertx() {
+ try {
+ var r = require;
+ var vertx = r('vertx');
+ vertxNext = vertx.runOnLoop || vertx.runOnContext;
+ return useVertxTimer();
+ } catch (e) {
+ return useSetTimeout();
+ }
+}
+
+var scheduleFlush = undefined;
+// Decide what async method to use to triggering processing of queued callbacks:
+if (isNode) {
+ scheduleFlush = useNextTick();
+} else if (BrowserMutationObserver) {
+ scheduleFlush = useMutationObserver();
+} else if (isWorker) {
+ scheduleFlush = useMessageChannel();
+} else if (browserWindow === undefined && typeof require === 'function') {
+ scheduleFlush = attemptVertx();
+} else {
+ scheduleFlush = useSetTimeout();
+}
+
+function then(onFulfillment, onRejection) {
+ var _arguments = arguments;
+
+ var parent = this;
+
+ var child = new this.constructor(noop);
+
+ if (child[PROMISE_ID] === undefined) {
+ makePromise(child);
+ }
+
+ var _state = parent._state;
+
+ if (_state) {
+ (function () {
+ var callback = _arguments[_state - 1];
+ asap(function () {
+ return invokeCallback(_state, child, callback, parent._result);
+ });
+ })();
+ } else {
+ subscribe(parent, child, onFulfillment, onRejection);
+ }
+
+ return child;
+}
+
+/**
+ `Promise.resolve` returns a promise that will become resolved with the
+ passed `value`. It is shorthand for the following:
+
+ ```javascript
+ let promise = new Promise(function(resolve, reject){
+ resolve(1);
+ });
+
+ promise.then(function(value){
+ // value === 1
+ });
+ ```
+
+ Instead of writing the above, your code now simply becomes the following:
+
+ ```javascript
+ let promise = Promise.resolve(1);
+
+ promise.then(function(value){
+ // value === 1
+ });
+ ```
+
+ @method resolve
+ @static
+ @param {Any} value value that the returned promise will be resolved with
+ Useful for tooling.
+ @return {Promise} a promise that will become fulfilled with the given
+ `value`
+*/
+function resolve$1(object) {
+ /*jshint validthis:true */
+ var Constructor = this;
+
+ if (object && typeof object === 'object' && object.constructor === Constructor) {
+ return object;
+ }
+
+ var promise = new Constructor(noop);
+ resolve(promise, object);
+ return promise;
+}
+
+var PROMISE_ID = Math.random().toString(36).substring(16);
+
+function noop() {}
+
+var PENDING = void 0;
+var FULFILLED = 1;
+var REJECTED = 2;
+
+var GET_THEN_ERROR = new ErrorObject();
+
+function selfFulfillment() {
+ return new TypeError("You cannot resolve a promise with itself");
+}
+
+function cannotReturnOwn() {
+ return new TypeError('A promises callback cannot return that same promise.');
+}
+
+function getThen(promise) {
+ try {
+ return promise.then;
+ } catch (error) {
+ GET_THEN_ERROR.error = error;
+ return GET_THEN_ERROR;
+ }
+}
+
+function tryThen(then$$1, value, fulfillmentHandler, rejectionHandler) {
+ try {
+ then$$1.call(value, fulfillmentHandler, rejectionHandler);
+ } catch (e) {
+ return e;
+ }
+}
+
+function handleForeignThenable(promise, thenable, then$$1) {
+ asap(function (promise) {
+ var sealed = false;
+ var error = tryThen(then$$1, thenable, function (value) {
+ if (sealed) {
+ return;
+ }
+ sealed = true;
+ if (thenable !== value) {
+ resolve(promise, value);
+ } else {
+ fulfill(promise, value);
+ }
+ }, function (reason) {
+ if (sealed) {
+ return;
+ }
+ sealed = true;
+
+ reject(promise, reason);
+ }, 'Settle: ' + (promise._label || ' unknown promise'));
+
+ if (!sealed && error) {
+ sealed = true;
+ reject(promise, error);
+ }
+ }, promise);
+}
+
+function handleOwnThenable(promise, thenable) {
+ if (thenable._state === FULFILLED) {
+ fulfill(promise, thenable._result);
+ } else if (thenable._state === REJECTED) {
+ reject(promise, thenable._result);
+ } else {
+ subscribe(thenable, undefined, function (value) {
+ return resolve(promise, value);
+ }, function (reason) {
+ return reject(promise, reason);
+ });
+ }
+}
+
+function handleMaybeThenable(promise, maybeThenable, then$$1) {
+ if (maybeThenable.constructor === promise.constructor && then$$1 === then && maybeThenable.constructor.resolve === resolve$1) {
+ handleOwnThenable(promise, maybeThenable);
+ } else {
+ if (then$$1 === GET_THEN_ERROR) {
+ reject(promise, GET_THEN_ERROR.error);
+ GET_THEN_ERROR.error = null;
+ } else if (then$$1 === undefined) {
+ fulfill(promise, maybeThenable);
+ } else if (isFunction(then$$1)) {
+ handleForeignThenable(promise, maybeThenable, then$$1);
+ } else {
+ fulfill(promise, maybeThenable);
+ }
+ }
+}
+
+function resolve(promise, value) {
+ if (promise === value) {
+ reject(promise, selfFulfillment());
+ } else if (objectOrFunction(value)) {
+ handleMaybeThenable(promise, value, getThen(value));
+ } else {
+ fulfill(promise, value);
+ }
+}
+
+function publishRejection(promise) {
+ if (promise._onerror) {
+ promise._onerror(promise._result);
+ }
+
+ publish(promise);
+}
+
+function fulfill(promise, value) {
+ if (promise._state !== PENDING) {
+ return;
+ }
+
+ promise._result = value;
+ promise._state = FULFILLED;
+
+ if (promise._subscribers.length !== 0) {
+ asap(publish, promise);
+ }
+}
+
+function reject(promise, reason) {
+ if (promise._state !== PENDING) {
+ return;
+ }
+ promise._state = REJECTED;
+ promise._result = reason;
+
+ asap(publishRejection, promise);
+}
+
+function subscribe(parent, child, onFulfillment, onRejection) {
+ var _subscribers = parent._subscribers;
+ var length = _subscribers.length;
+
+ parent._onerror = null;
+
+ _subscribers[length] = child;
+ _subscribers[length + FULFILLED] = onFulfillment;
+ _subscribers[length + REJECTED] = onRejection;
+
+ if (length === 0 && parent._state) {
+ asap(publish, parent);
+ }
+}
+
+function publish(promise) {
+ var subscribers = promise._subscribers;
+ var settled = promise._state;
+
+ if (subscribers.length === 0) {
+ return;
+ }
+
+ var child = undefined,
+ callback = undefined,
+ detail = promise._result;
+
+ for (var i = 0; i < subscribers.length; i += 3) {
+ child = subscribers[i];
+ callback = subscribers[i + settled];
+
+ if (child) {
+ invokeCallback(settled, child, callback, detail);
+ } else {
+ callback(detail);
+ }
+ }
+
+ promise._subscribers.length = 0;
+}
+
+function ErrorObject() {
+ this.error = null;
+}
+
+var TRY_CATCH_ERROR = new ErrorObject();
+
+function tryCatch(callback, detail) {
+ try {
+ return callback(detail);
+ } catch (e) {
+ TRY_CATCH_ERROR.error = e;
+ return TRY_CATCH_ERROR;
+ }
+}
+
+function invokeCallback(settled, promise, callback, detail) {
+ var hasCallback = isFunction(callback),
+ value = undefined,
+ error = undefined,
+ succeeded = undefined,
+ failed = undefined;
+
+ if (hasCallback) {
+ value = tryCatch(callback, detail);
+
+ if (value === TRY_CATCH_ERROR) {
+ failed = true;
+ error = value.error;
+ value.error = null;
+ } else {
+ succeeded = true;
+ }
+
+ if (promise === value) {
+ reject(promise, cannotReturnOwn());
+ return;
+ }
+ } else {
+ value = detail;
+ succeeded = true;
+ }
+
+ if (promise._state !== PENDING) {
+ // noop
+ } else if (hasCallback && succeeded) {
+ resolve(promise, value);
+ } else if (failed) {
+ reject(promise, error);
+ } else if (settled === FULFILLED) {
+ fulfill(promise, value);
+ } else if (settled === REJECTED) {
+ reject(promise, value);
+ }
+}
+
+function initializePromise(promise, resolver) {
+ try {
+ resolver(function resolvePromise(value) {
+ resolve(promise, value);
+ }, function rejectPromise(reason) {
+ reject(promise, reason);
+ });
+ } catch (e) {
+ reject(promise, e);
+ }
+}
+
+var id = 0;
+function nextId() {
+ return id++;
+}
+
+function makePromise(promise) {
+ promise[PROMISE_ID] = id++;
+ promise._state = undefined;
+ promise._result = undefined;
+ promise._subscribers = [];
+}
+
+function Enumerator$1(Constructor, input) {
+ this._instanceConstructor = Constructor;
+ this.promise = new Constructor(noop);
+
+ if (!this.promise[PROMISE_ID]) {
+ makePromise(this.promise);
+ }
+
+ if (isArray(input)) {
+ this.length = input.length;
+ this._remaining = input.length;
+
+ this._result = new Array(this.length);
+
+ if (this.length === 0) {
+ fulfill(this.promise, this._result);
+ } else {
+ this.length = this.length || 0;
+ this._enumerate(input);
+ if (this._remaining === 0) {
+ fulfill(this.promise, this._result);
+ }
+ }
+ } else {
+ reject(this.promise, validationError());
+ }
+}
+
+function validationError() {
+ return new Error('Array Methods must be provided an Array');
+}
+
+Enumerator$1.prototype._enumerate = function (input) {
+ for (var i = 0; this._state === PENDING && i < input.length; i++) {
+ this._eachEntry(input[i], i);
+ }
+};
+
+Enumerator$1.prototype._eachEntry = function (entry, i) {
+ var c = this._instanceConstructor;
+ var resolve$$1 = c.resolve;
+
+ if (resolve$$1 === resolve$1) {
+ var _then = getThen(entry);
+
+ if (_then === then && entry._state !== PENDING) {
+ this._settledAt(entry._state, i, entry._result);
+ } else if (typeof _then !== 'function') {
+ this._remaining--;
+ this._result[i] = entry;
+ } else if (c === Promise$2) {
+ var promise = new c(noop);
+ handleMaybeThenable(promise, entry, _then);
+ this._willSettleAt(promise, i);
+ } else {
+ this._willSettleAt(new c(function (resolve$$1) {
+ return resolve$$1(entry);
+ }), i);
+ }
+ } else {
+ this._willSettleAt(resolve$$1(entry), i);
+ }
+};
+
+Enumerator$1.prototype._settledAt = function (state, i, value) {
+ var promise = this.promise;
+
+ if (promise._state === PENDING) {
+ this._remaining--;
+
+ if (state === REJECTED) {
+ reject(promise, value);
+ } else {
+ this._result[i] = value;
+ }
+ }
+
+ if (this._remaining === 0) {
+ fulfill(promise, this._result);
+ }
+};
+
+Enumerator$1.prototype._willSettleAt = function (promise, i) {
+ var enumerator = this;
+
+ subscribe(promise, undefined, function (value) {
+ return enumerator._settledAt(FULFILLED, i, value);
+ }, function (reason) {
+ return enumerator._settledAt(REJECTED, i, reason);
+ });
+};
+
+/**
+ `Promise.all` accepts an array of promises, and returns a new promise which
+ is fulfilled with an array of fulfillment values for the passed promises, or
+ rejected with the reason of the first passed promise to be rejected. It casts all
+ elements of the passed iterable to promises as it runs this algorithm.
+
+ Example:
+
+ ```javascript
+ let promise1 = resolve(1);
+ let promise2 = resolve(2);
+ let promise3 = resolve(3);
+ let promises = [ promise1, promise2, promise3 ];
+
+ Promise.all(promises).then(function(array){
+ // The array here would be [ 1, 2, 3 ];
+ });
+ ```
+
+ If any of the `promises` given to `all` are rejected, the first promise
+ that is rejected will be given as an argument to the returned promises's
+ rejection handler. For example:
+
+ Example:
+
+ ```javascript
+ let promise1 = resolve(1);
+ let promise2 = reject(new Error("2"));
+ let promise3 = reject(new Error("3"));
+ let promises = [ promise1, promise2, promise3 ];
+
+ Promise.all(promises).then(function(array){
+ // Code here never runs because there are rejected promises!
+ }, function(error) {
+ // error.message === "2"
+ });
+ ```
+
+ @method all
+ @static
+ @param {Array} entries array of promises
+ @param {String} label optional string for labeling the promise.
+ Useful for tooling.
+ @return {Promise} promise that is fulfilled when all `promises` have been
+ fulfilled, or rejected if any of them become rejected.
+ @static
+*/
+function all$1(entries) {
+ return new Enumerator$1(this, entries).promise;
+}
+
+/**
+ `Promise.race` returns a new promise which is settled in the same way as the
+ first passed promise to settle.
+
+ Example:
+
+ ```javascript
+ let promise1 = new Promise(function(resolve, reject){
+ setTimeout(function(){
+ resolve('promise 1');
+ }, 200);
+ });
+
+ let promise2 = new Promise(function(resolve, reject){
+ setTimeout(function(){
+ resolve('promise 2');
+ }, 100);
+ });
+
+ Promise.race([promise1, promise2]).then(function(result){
+ // result === 'promise 2' because it was resolved before promise1
+ // was resolved.
+ });
+ ```
+
+ `Promise.race` is deterministic in that only the state of the first
+ settled promise matters. For example, even if other promises given to the
+ `promises` array argument are resolved, but the first settled promise has
+ become rejected before the other promises became fulfilled, the returned
+ promise will become rejected:
+
+ ```javascript
+ let promise1 = new Promise(function(resolve, reject){
+ setTimeout(function(){
+ resolve('promise 1');
+ }, 200);
+ });
+
+ let promise2 = new Promise(function(resolve, reject){
+ setTimeout(function(){
+ reject(new Error('promise 2'));
+ }, 100);
+ });
+
+ Promise.race([promise1, promise2]).then(function(result){
+ // Code here never runs
+ }, function(reason){
+ // reason.message === 'promise 2' because promise 2 became rejected before
+ // promise 1 became fulfilled
+ });
+ ```
+
+ An example real-world use case is implementing timeouts:
+
+ ```javascript
+ Promise.race([ajax('foo.json'), timeout(5000)])
+ ```
+
+ @method race
+ @static
+ @param {Array} promises array of promises to observe
+ Useful for tooling.
+ @return {Promise} a promise which settles in the same way as the first passed
+ promise to settle.
+*/
+function race$1(entries) {
+ /*jshint validthis:true */
+ var Constructor = this;
+
+ if (!isArray(entries)) {
+ return new Constructor(function (_, reject) {
+ return reject(new TypeError('You must pass an array to race.'));
+ });
+ } else {
+ return new Constructor(function (resolve, reject) {
+ var length = entries.length;
+ for (var i = 0; i < length; i++) {
+ Constructor.resolve(entries[i]).then(resolve, reject);
+ }
+ });
+ }
+}
+
+/**
+ `Promise.reject` returns a promise rejected with the passed `reason`.
+ It is shorthand for the following:
+
+ ```javascript
+ let promise = new Promise(function(resolve, reject){
+ reject(new Error('WHOOPS'));
+ });
+
+ promise.then(function(value){
+ // Code here doesn't run because the promise is rejected!
+ }, function(reason){
+ // reason.message === 'WHOOPS'
+ });
+ ```
+
+ Instead of writing the above, your code now simply becomes the following:
+
+ ```javascript
+ let promise = Promise.reject(new Error('WHOOPS'));
+
+ promise.then(function(value){
+ // Code here doesn't run because the promise is rejected!
+ }, function(reason){
+ // reason.message === 'WHOOPS'
+ });
+ ```
+
+ @method reject
+ @static
+ @param {Any} reason value that the returned promise will be rejected with.
+ Useful for tooling.
+ @return {Promise} a promise rejected with the given `reason`.
+*/
+function reject$1(reason) {
+ /*jshint validthis:true */
+ var Constructor = this;
+ var promise = new Constructor(noop);
+ reject(promise, reason);
+ return promise;
+}
+
+function needsResolver() {
+ throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
+}
+
+function needsNew() {
+ throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
+}
+
+/**
+ Promise objects represent the eventual result of an asynchronous operation. The
+ primary way of interacting with a promise is through its `then` method, which
+ registers callbacks to receive either a promise's eventual value or the reason
+ why the promise cannot be fulfilled.
+
+ Terminology
+ -----------
+
+ - `promise` is an object or function with a `then` method whose behavior conforms to this specification.
+ - `thenable` is an object or function that defines a `then` method.
+ - `value` is any legal JavaScript value (including undefined, a thenable, or a promise).
+ - `exception` is a value that is thrown using the throw statement.
+ - `reason` is a value that indicates why a promise was rejected.
+ - `settled` the final resting state of a promise, fulfilled or rejected.
+
+ A promise can be in one of three states: pending, fulfilled, or rejected.
+
+ Promises that are fulfilled have a fulfillment value and are in the fulfilled
+ state. Promises that are rejected have a rejection reason and are in the
+ rejected state. A fulfillment value is never a thenable.
+
+ Promises can also be said to *resolve* a value. If this value is also a
+ promise, then the original promise's settled state will match the value's
+ settled state. So a promise that *resolves* a promise that rejects will
+ itself reject, and a promise that *resolves* a promise that fulfills will
+ itself fulfill.
+
+
+ Basic Usage:
+ ------------
+
+ ```js
+ let promise = new Promise(function(resolve, reject) {
+ // on success
+ resolve(value);
+
+ // on failure
+ reject(reason);
+ });
+
+ promise.then(function(value) {
+ // on fulfillment
+ }, function(reason) {
+ // on rejection
+ });
+ ```
+
+ Advanced Usage:
+ ---------------
+
+ Promises shine when abstracting away asynchronous interactions such as
+ `XMLHttpRequest`s.
+
+ ```js
+ function getJSON(url) {
+ return new Promise(function(resolve, reject){
+ let xhr = new XMLHttpRequest();
+
+ xhr.open('GET', url);
+ xhr.onreadystatechange = handler;
+ xhr.responseType = 'json';
+ xhr.setRequestHeader('Accept', 'application/json');
+ xhr.send();
+
+ function handler() {
+ if (this.readyState === this.DONE) {
+ if (this.status === 200) {
+ resolve(this.response);
+ } else {
+ reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
+ }
+ }
+ };
+ });
+ }
+
+ getJSON('/posts.json').then(function(json) {
+ // on fulfillment
+ }, function(reason) {
+ // on rejection
+ });
+ ```
+
+ Unlike callbacks, promises are great composable primitives.
+
+ ```js
+ Promise.all([
+ getJSON('/posts'),
+ getJSON('/comments')
+ ]).then(function(values){
+ values[0] // => postsJSON
+ values[1] // => commentsJSON
+
+ return values;
+ });
+ ```
+
+ @class Promise
+ @param {function} resolver
+ Useful for tooling.
+ @constructor
+*/
+function Promise$2(resolver) {
+ this[PROMISE_ID] = nextId();
+ this._result = this._state = undefined;
+ this._subscribers = [];
+
+ if (noop !== resolver) {
+ typeof resolver !== 'function' && needsResolver();
+ this instanceof Promise$2 ? initializePromise(this, resolver) : needsNew();
+ }
+}
+
+Promise$2.all = all$1;
+Promise$2.race = race$1;
+Promise$2.resolve = resolve$1;
+Promise$2.reject = reject$1;
+Promise$2._setScheduler = setScheduler;
+Promise$2._setAsap = setAsap;
+Promise$2._asap = asap;
+
+Promise$2.prototype = {
+ constructor: Promise$2,
+
+ /**
+ The primary way of interacting with a promise is through its `then` method,
+ which registers callbacks to receive either a promise's eventual value or the
+ reason why the promise cannot be fulfilled.
+
+ ```js
+ findUser().then(function(user){
+ // user is available
+ }, function(reason){
+ // user is unavailable, and you are given the reason why
+ });
+ ```
+
+ Chaining
+ --------
+
+ The return value of `then` is itself a promise. This second, 'downstream'
+ promise is resolved with the return value of the first promise's fulfillment
+ or rejection handler, or rejected if the handler throws an exception.
+
+ ```js
+ findUser().then(function (user) {
+ return user.name;
+ }, function (reason) {
+ return 'default name';
+ }).then(function (userName) {
+ // If `findUser` fulfilled, `userName` will be the user's name, otherwise it
+ // will be `'default name'`
+ });
+
+ findUser().then(function (user) {
+ throw new Error('Found user, but still unhappy');
+ }, function (reason) {
+ throw new Error('`findUser` rejected and we're unhappy');
+ }).then(function (value) {
+ // never reached
+ }, function (reason) {
+ // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.
+ // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.
+ });
+ ```
+ If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.
+
+ ```js
+ findUser().then(function (user) {
+ throw new PedagogicalException('Upstream error');
+ }).then(function (value) {
+ // never reached
+ }).then(function (value) {
+ // never reached
+ }, function (reason) {
+ // The `PedgagocialException` is propagated all the way down to here
+ });
+ ```
+
+ Assimilation
+ ------------
+
+ Sometimes the value you want to propagate to a downstream promise can only be
+ retrieved asynchronously. This can be achieved by returning a promise in the
+ fulfillment or rejection handler. The downstream promise will then be pending
+ until the returned promise is settled. This is called *assimilation*.
+
+ ```js
+ findUser().then(function (user) {
+ return findCommentsByAuthor(user);
+ }).then(function (comments) {
+ // The user's comments are now available
+ });
+ ```
+
+ If the assimliated promise rejects, then the downstream promise will also reject.
+
+ ```js
+ findUser().then(function (user) {
+ return findCommentsByAuthor(user);
+ }).then(function (comments) {
+ // If `findCommentsByAuthor` fulfills, we'll have the value here
+ }, function (reason) {
+ // If `findCommentsByAuthor` rejects, we'll have the reason here
+ });
+ ```
+
+ Simple Example
+ --------------
+
+ Synchronous Example
+
+ ```javascript
+ let result;
+
+ try {
+ result = findResult();
+ // success
+ } catch(reason) {
+ // failure
+ }
+ ```
+
+ Errback Example
+
+ ```js
+ findResult(function(result, err){
+ if (err) {
+ // failure
+ } else {
+ // success
+ }
+ });
+ ```
+
+ Promise Example;
+
+ ```javascript
+ findResult().then(function(result){
+ // success
+ }, function(reason){
+ // failure
+ });
+ ```
+
+ Advanced Example
+ --------------
+
+ Synchronous Example
+
+ ```javascript
+ let author, books;
+
+ try {
+ author = findAuthor();
+ books = findBooksByAuthor(author);
+ // success
+ } catch(reason) {
+ // failure
+ }
+ ```
+
+ Errback Example
+
+ ```js
+
+ function foundBooks(books) {
+
+ }
+
+ function failure(reason) {
+
+ }
+
+ findAuthor(function(author, err){
+ if (err) {
+ failure(err);
+ // failure
+ } else {
+ try {
+ findBoooksByAuthor(author, function(books, err) {
+ if (err) {
+ failure(err);
+ } else {
+ try {
+ foundBooks(books);
+ } catch(reason) {
+ failure(reason);
+ }
+ }
+ });
+ } catch(error) {
+ failure(err);
+ }
+ // success
+ }
+ });
+ ```
+
+ Promise Example;
+
+ ```javascript
+ findAuthor().
+ then(findBooksByAuthor).
+ then(function(books){
+ // found books
+ }).catch(function(reason){
+ // something went wrong
+ });
+ ```
+
+ @method then
+ @param {Function} onFulfilled
+ @param {Function} onRejected
+ Useful for tooling.
+ @return {Promise}
+ */
+ then: then,
+
+ /**
+ `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same
+ as the catch block of a try/catch statement.
+
+ ```js
+ function findAuthor(){
+ throw new Error('couldn't find that author');
+ }
+
+ // synchronous
+ try {
+ findAuthor();
+ } catch(reason) {
+ // something went wrong
+ }
+
+ // async with promises
+ findAuthor().catch(function(reason){
+ // something went wrong
+ });
+ ```
+
+ @method catch
+ @param {Function} onRejection
+ Useful for tooling.
+ @return {Promise}
+ */
+ 'catch': function _catch(onRejection) {
+ return this.then(null, onRejection);
+ }
+};
+
+/*global self*/
+function polyfill$1() {
+ var local = undefined;
+
+ if (typeof global !== 'undefined') {
+ local = global;
+ } else if (typeof self !== 'undefined') {
+ local = self;
+ } else {
+ try {
+ local = Function('return this')();
+ } catch (e) {
+ throw new Error('polyfill failed because global object is unavailable in this environment');
+ }
+ }
+
+ var P = local.Promise;
+
+ if (P) {
+ var promiseToString = null;
+ try {
+ promiseToString = Object.prototype.toString.call(P.resolve());
+ } catch (e) {
+ // silently ignored
+ }
+
+ if (promiseToString === '[object Promise]' && !P.cast) {
+ return;
+ }
+ }
+
+ local.Promise = Promise$2;
+}
+
+// Strange compat..
+Promise$2.polyfill = polyfill$1;
+Promise$2.Promise = Promise$2;
+
+return Promise$2;
+
+})));
+
+
+
+}).call(this,require(12),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"12":12}],4:[function(require,module,exports){
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+function EventEmitter() {
+ this._events = this._events || {};
+ this._maxListeners = this._maxListeners || undefined;
+}
+module.exports = EventEmitter;
+
+// Backwards-compat with node 0.10.x
+EventEmitter.EventEmitter = EventEmitter;
+
+EventEmitter.prototype._events = undefined;
+EventEmitter.prototype._maxListeners = undefined;
+
+// By default EventEmitters will print a warning if more than 10 listeners are
+// added to it. This is a useful default which helps finding memory leaks.
+EventEmitter.defaultMaxListeners = 10;
+
+// Obviously not all Emitters should be limited to 10. This function allows
+// that to be increased. Set to zero for unlimited.
+EventEmitter.prototype.setMaxListeners = function(n) {
+ if (!isNumber(n) || n < 0 || isNaN(n))
+ throw TypeError('n must be a positive number');
+ this._maxListeners = n;
+ return this;
+};
+
+EventEmitter.prototype.emit = function(type) {
+ var er, handler, len, args, i, listeners;
+
+ if (!this._events)
+ this._events = {};
+
+ // If there is no 'error' event listener then throw.
+ if (type === 'error') {
+ if (!this._events.error ||
+ (isObject(this._events.error) && !this._events.error.length)) {
+ er = arguments[1];
+ if (er instanceof Error) {
+ throw er; // Unhandled 'error' event
+ } else {
+ // At least give some kind of context to the user
+ var err = new Error('Uncaught, unspecified "error" event. (' + er + ')');
+ err.context = er;
+ throw err;
+ }
+ }
+ }
+
+ handler = this._events[type];
+
+ if (isUndefined(handler))
+ return false;
+
+ if (isFunction(handler)) {
+ switch (arguments.length) {
+ // fast cases
+ case 1:
+ handler.call(this);
+ break;
+ case 2:
+ handler.call(this, arguments[1]);
+ break;
+ case 3:
+ handler.call(this, arguments[1], arguments[2]);
+ break;
+ // slower
+ default:
+ args = Array.prototype.slice.call(arguments, 1);
+ handler.apply(this, args);
+ }
+ } else if (isObject(handler)) {
+ args = Array.prototype.slice.call(arguments, 1);
+ listeners = handler.slice();
+ len = listeners.length;
+ for (i = 0; i < len; i++)
+ listeners[i].apply(this, args);
+ }
+
+ return true;
+};
+
+EventEmitter.prototype.addListener = function(type, listener) {
+ var m;
+
+ if (!isFunction(listener))
+ throw TypeError('listener must be a function');
+
+ if (!this._events)
+ this._events = {};
+
+ // To avoid recursion in the case that type === "newListener"! Before
+ // adding it to the listeners, first emit "newListener".
+ if (this._events.newListener)
+ this.emit('newListener', type,
+ isFunction(listener.listener) ?
+ listener.listener : listener);
+
+ if (!this._events[type])
+ // Optimize the case of one listener. Don't need the extra array object.
+ this._events[type] = listener;
+ else if (isObject(this._events[type]))
+ // If we've already got an array, just append.
+ this._events[type].push(listener);
+ else
+ // Adding the second element, need to change to array.
+ this._events[type] = [this._events[type], listener];
+
+ // Check for listener leak
+ if (isObject(this._events[type]) && !this._events[type].warned) {
+ if (!isUndefined(this._maxListeners)) {
+ m = this._maxListeners;
+ } else {
+ m = EventEmitter.defaultMaxListeners;
+ }
+
+ if (m && m > 0 && this._events[type].length > m) {
+ this._events[type].warned = true;
+ console.error('(node) warning: possible EventEmitter memory ' +
+ 'leak detected. %d listeners added. ' +
+ 'Use emitter.setMaxListeners() to increase limit.',
+ this._events[type].length);
+ if (typeof console.trace === 'function') {
+ // not supported in IE 10
+ console.trace();
+ }
+ }
+ }
+
+ return this;
+};
+
+EventEmitter.prototype.on = EventEmitter.prototype.addListener;
+
+EventEmitter.prototype.once = function(type, listener) {
+ if (!isFunction(listener))
+ throw TypeError('listener must be a function');
+
+ var fired = false;
+
+ function g() {
+ this.removeListener(type, g);
+
+ if (!fired) {
+ fired = true;
+ listener.apply(this, arguments);
+ }
+ }
+
+ g.listener = listener;
+ this.on(type, g);
+
+ return this;
+};
+
+// emits a 'removeListener' event iff the listener was removed
+EventEmitter.prototype.removeListener = function(type, listener) {
+ var list, position, length, i;
+
+ if (!isFunction(listener))
+ throw TypeError('listener must be a function');
+
+ if (!this._events || !this._events[type])
+ return this;
+
+ list = this._events[type];
+ length = list.length;
+ position = -1;
+
+ if (list === listener ||
+ (isFunction(list.listener) && list.listener === listener)) {
+ delete this._events[type];
+ if (this._events.removeListener)
+ this.emit('removeListener', type, listener);
+
+ } else if (isObject(list)) {
+ for (i = length; i-- > 0;) {
+ if (list[i] === listener ||
+ (list[i].listener && list[i].listener === listener)) {
+ position = i;
+ break;
+ }
+ }
+
+ if (position < 0)
+ return this;
+
+ if (list.length === 1) {
+ list.length = 0;
+ delete this._events[type];
+ } else {
+ list.splice(position, 1);
+ }
+
+ if (this._events.removeListener)
+ this.emit('removeListener', type, listener);
+ }
+
+ return this;
+};
+
+EventEmitter.prototype.removeAllListeners = function(type) {
+ var key, listeners;
+
+ if (!this._events)
+ return this;
+
+ // not listening for removeListener, no need to emit
+ if (!this._events.removeListener) {
+ if (arguments.length === 0)
+ this._events = {};
+ else if (this._events[type])
+ delete this._events[type];
+ return this;
+ }
+
+ // emit removeListener for all listeners on all events
+ if (arguments.length === 0) {
+ for (key in this._events) {
+ if (key === 'removeListener') continue;
+ this.removeAllListeners(key);
+ }
+ this.removeAllListeners('removeListener');
+ this._events = {};
+ return this;
+ }
+
+ listeners = this._events[type];
+
+ if (isFunction(listeners)) {
+ this.removeListener(type, listeners);
+ } else if (listeners) {
+ // LIFO order
+ while (listeners.length)
+ this.removeListener(type, listeners[listeners.length - 1]);
+ }
+ delete this._events[type];
+
+ return this;
+};
+
+EventEmitter.prototype.listeners = function(type) {
+ var ret;
+ if (!this._events || !this._events[type])
+ ret = [];
+ else if (isFunction(this._events[type]))
+ ret = [this._events[type]];
+ else
+ ret = this._events[type].slice();
+ return ret;
+};
+
+EventEmitter.prototype.listenerCount = function(type) {
+ if (this._events) {
+ var evlistener = this._events[type];
+
+ if (isFunction(evlistener))
+ return 1;
+ else if (evlistener)
+ return evlistener.length;
+ }
+ return 0;
+};
+
+EventEmitter.listenerCount = function(emitter, type) {
+ return emitter.listenerCount(type);
+};
+
+function isFunction(arg) {
+ return typeof arg === 'function';
+}
+
+function isNumber(arg) {
+ return typeof arg === 'number';
+}
+
+function isObject(arg) {
+ return typeof arg === 'object' && arg !== null;
+}
+
+function isUndefined(arg) {
+ return arg === void 0;
+}
+
+},{}],5:[function(require,module,exports){
+
+var hasOwn = Object.prototype.hasOwnProperty;
+var toString = Object.prototype.toString;
+
+module.exports = function forEach (obj, fn, ctx) {
+ if (toString.call(fn) !== '[object Function]') {
+ throw new TypeError('iterator must be a function');
+ }
+ var l = obj.length;
+ if (l === +l) {
+ for (var i = 0; i < l; i++) {
+ fn.call(ctx, obj[i], i, obj);
+ }
+ } else {
+ for (var k in obj) {
+ if (hasOwn.call(obj, k)) {
+ fn.call(ctx, obj[k], k, obj);
+ }
+ }
+ }
+};
+
+
+},{}],6:[function(require,module,exports){
+(function (global){
+var win;
+
+if (typeof window !== "undefined") {
+ win = window;
+} else if (typeof global !== "undefined") {
+ win = global;
+} else if (typeof self !== "undefined"){
+ win = self;
+} else {
+ win = {};
+}
+
+module.exports = win;
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{}],7:[function(require,module,exports){
+if (typeof Object.create === 'function') {
+ // implementation from standard node.js 'util' module
+ module.exports = function inherits(ctor, superCtor) {
+ ctor.super_ = superCtor
+ ctor.prototype = Object.create(superCtor.prototype, {
+ constructor: {
+ value: ctor,
+ enumerable: false,
+ writable: true,
+ configurable: true
+ }
+ });
+ };
+} else {
+ // old school shim for old browsers
+ module.exports = function inherits(ctor, superCtor) {
+ ctor.super_ = superCtor
+ var TempCtor = function () {}
+ TempCtor.prototype = superCtor.prototype
+ ctor.prototype = new TempCtor()
+ ctor.prototype.constructor = ctor
+ }
+}
+
+},{}],8:[function(require,module,exports){
+var toString = {}.toString;
+
+module.exports = Array.isArray || function (arr) {
+ return toString.call(arr) == '[object Array]';
+};
+
+},{}],9:[function(require,module,exports){
+/**
+ * Helpers.
+ */
+
+var s = 1000;
+var m = s * 60;
+var h = m * 60;
+var d = h * 24;
+var y = d * 365.25;
+
+/**
+ * Parse or format the given `val`.
+ *
+ * Options:
+ *
+ * - `long` verbose formatting [false]
+ *
+ * @param {String|Number} val
+ * @param {Object} [options]
+ * @throws {Error} throw an error if val is not a non-empty string or a number
+ * @return {String|Number}
+ * @api public
+ */
+
+module.exports = function(val, options) {
+ options = options || {};
+ var type = typeof val;
+ if (type === 'string' && val.length > 0) {
+ return parse(val);
+ } else if (type === 'number' && isNaN(val) === false) {
+ return options.long ? fmtLong(val) : fmtShort(val);
+ }
+ throw new Error(
+ 'val is not a non-empty string or a valid number. val=' +
+ JSON.stringify(val)
+ );
+};
+
+/**
+ * Parse the given `str` and return milliseconds.
+ *
+ * @param {String} str
+ * @return {Number}
+ * @api private
+ */
+
+function parse(str) {
+ str = String(str);
+ if (str.length > 100) {
+ return;
+ }
+ var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(
+ str
+ );
+ if (!match) {
+ return;
+ }
+ var n = parseFloat(match[1]);
+ var type = (match[2] || 'ms').toLowerCase();
+ switch (type) {
+ case 'years':
+ case 'year':
+ case 'yrs':
+ case 'yr':
+ case 'y':
+ return n * y;
+ case 'days':
+ case 'day':
+ case 'd':
+ return n * d;
+ case 'hours':
+ case 'hour':
+ case 'hrs':
+ case 'hr':
+ case 'h':
+ return n * h;
+ case 'minutes':
+ case 'minute':
+ case 'mins':
+ case 'min':
+ case 'm':
+ return n * m;
+ case 'seconds':
+ case 'second':
+ case 'secs':
+ case 'sec':
+ case 's':
+ return n * s;
+ case 'milliseconds':
+ case 'millisecond':
+ case 'msecs':
+ case 'msec':
+ case 'ms':
+ return n;
+ default:
+ return undefined;
+ }
+}
+
+/**
+ * Short format for `ms`.
+ *
+ * @param {Number} ms
+ * @return {String}
+ * @api private
+ */
+
+function fmtShort(ms) {
+ if (ms >= d) {
+ return Math.round(ms / d) + 'd';
+ }
+ if (ms >= h) {
+ return Math.round(ms / h) + 'h';
+ }
+ if (ms >= m) {
+ return Math.round(ms / m) + 'm';
+ }
+ if (ms >= s) {
+ return Math.round(ms / s) + 's';
+ }
+ return ms + 'ms';
+}
+
+/**
+ * Long format for `ms`.
+ *
+ * @param {Number} ms
+ * @return {String}
+ * @api private
+ */
+
+function fmtLong(ms) {
+ return plural(ms, d, 'day') ||
+ plural(ms, h, 'hour') ||
+ plural(ms, m, 'minute') ||
+ plural(ms, s, 'second') ||
+ ms + ' ms';
+}
+
+/**
+ * Pluralization helper.
+ */
+
+function plural(ms, n, name) {
+ if (ms < n) {
+ return;
+ }
+ if (ms < n * 1.5) {
+ return Math.floor(ms / n) + ' ' + name;
+ }
+ return Math.ceil(ms / n) + ' ' + name + 's';
+}
+
+},{}],10:[function(require,module,exports){
+'use strict';
+
+// modified from https://github.com/es-shims/es5-shim
+var has = Object.prototype.hasOwnProperty;
+var toStr = Object.prototype.toString;
+var slice = Array.prototype.slice;
+var isArgs = require(11);
+var isEnumerable = Object.prototype.propertyIsEnumerable;
+var hasDontEnumBug = !isEnumerable.call({ toString: null }, 'toString');
+var hasProtoEnumBug = isEnumerable.call(function () {}, 'prototype');
+var dontEnums = [
+ 'toString',
+ 'toLocaleString',
+ 'valueOf',
+ 'hasOwnProperty',
+ 'isPrototypeOf',
+ 'propertyIsEnumerable',
+ 'constructor'
+];
+var equalsConstructorPrototype = function (o) {
+ var ctor = o.constructor;
+ return ctor && ctor.prototype === o;
+};
+var excludedKeys = {
+ $console: true,
+ $external: true,
+ $frame: true,
+ $frameElement: true,
+ $frames: true,
+ $innerHeight: true,
+ $innerWidth: true,
+ $outerHeight: true,
+ $outerWidth: true,
+ $pageXOffset: true,
+ $pageYOffset: true,
+ $parent: true,
+ $scrollLeft: true,
+ $scrollTop: true,
+ $scrollX: true,
+ $scrollY: true,
+ $self: true,
+ $webkitIndexedDB: true,
+ $webkitStorageInfo: true,
+ $window: true
+};
+var hasAutomationEqualityBug = (function () {
+ /* global window */
+ if (typeof window === 'undefined') { return false; }
+ for (var k in window) {
+ try {
+ if (!excludedKeys['$' + k] && has.call(window, k) && window[k] !== null && typeof window[k] === 'object') {
+ try {
+ equalsConstructorPrototype(window[k]);
+ } catch (e) {
+ return true;
+ }
+ }
+ } catch (e) {
+ return true;
+ }
+ }
+ return false;
+}());
+var equalsConstructorPrototypeIfNotBuggy = function (o) {
+ /* global window */
+ if (typeof window === 'undefined' || !hasAutomationEqualityBug) {
+ return equalsConstructorPrototype(o);
+ }
+ try {
+ return equalsConstructorPrototype(o);
+ } catch (e) {
+ return false;
+ }
+};
+
+var keysShim = function keys(object) {
+ var isObject = object !== null && typeof object === 'object';
+ var isFunction = toStr.call(object) === '[object Function]';
+ var isArguments = isArgs(object);
+ var isString = isObject && toStr.call(object) === '[object String]';
+ var theKeys = [];
+
+ if (!isObject && !isFunction && !isArguments) {
+ throw new TypeError('Object.keys called on a non-object');
+ }
+
+ var skipProto = hasProtoEnumBug && isFunction;
+ if (isString && object.length > 0 && !has.call(object, 0)) {
+ for (var i = 0; i < object.length; ++i) {
+ theKeys.push(String(i));
+ }
+ }
+
+ if (isArguments && object.length > 0) {
+ for (var j = 0; j < object.length; ++j) {
+ theKeys.push(String(j));
+ }
+ } else {
+ for (var name in object) {
+ if (!(skipProto && name === 'prototype') && has.call(object, name)) {
+ theKeys.push(String(name));
+ }
+ }
+ }
+
+ if (hasDontEnumBug) {
+ var skipConstructor = equalsConstructorPrototypeIfNotBuggy(object);
+
+ for (var k = 0; k < dontEnums.length; ++k) {
+ if (!(skipConstructor && dontEnums[k] === 'constructor') && has.call(object, dontEnums[k])) {
+ theKeys.push(dontEnums[k]);
+ }
+ }
+ }
+ return theKeys;
+};
+
+keysShim.shim = function shimObjectKeys() {
+ if (Object.keys) {
+ var keysWorksWithArguments = (function () {
+ // Safari 5.0 bug
+ return (Object.keys(arguments) || '').length === 2;
+ }(1, 2));
+ if (!keysWorksWithArguments) {
+ var originalKeys = Object.keys;
+ Object.keys = function keys(object) {
+ if (isArgs(object)) {
+ return originalKeys(slice.call(object));
+ } else {
+ return originalKeys(object);
+ }
+ };
+ }
+ } else {
+ Object.keys = keysShim;
+ }
+ return Object.keys || keysShim;
+};
+
+module.exports = keysShim;
+
+},{"11":11}],11:[function(require,module,exports){
+'use strict';
+
+var toStr = Object.prototype.toString;
+
+module.exports = function isArguments(value) {
+ var str = toStr.call(value);
+ var isArgs = str === '[object Arguments]';
+ if (!isArgs) {
+ isArgs = str !== '[object Array]' &&
+ value !== null &&
+ typeof value === 'object' &&
+ typeof value.length === 'number' &&
+ value.length >= 0 &&
+ toStr.call(value.callee) === '[object Function]';
+ }
+ return isArgs;
+};
+
+},{}],12:[function(require,module,exports){
+// shim for using process in browser
+var process = module.exports = {};
+
+// cached from whatever global is present so that test runners that stub it
+// don't break things. But we need to wrap it in a try catch in case it is
+// wrapped in strict mode code which doesn't define any globals. It's inside a
+// function because try/catches deoptimize in certain engines.
+
+var cachedSetTimeout;
+var cachedClearTimeout;
+
+function defaultSetTimout() {
+ throw new Error('setTimeout has not been defined');
+}
+function defaultClearTimeout () {
+ throw new Error('clearTimeout has not been defined');
+}
+(function () {
+ try {
+ if (typeof setTimeout === 'function') {
+ cachedSetTimeout = setTimeout;
+ } else {
+ cachedSetTimeout = defaultSetTimout;
+ }
+ } catch (e) {
+ cachedSetTimeout = defaultSetTimout;
+ }
+ try {
+ if (typeof clearTimeout === 'function') {
+ cachedClearTimeout = clearTimeout;
+ } else {
+ cachedClearTimeout = defaultClearTimeout;
+ }
+ } catch (e) {
+ cachedClearTimeout = defaultClearTimeout;
+ }
+} ())
+function runTimeout(fun) {
+ if (cachedSetTimeout === setTimeout) {
+ //normal enviroments in sane situations
+ return setTimeout(fun, 0);
+ }
+ // if setTimeout wasn't available but was latter defined
+ if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
+ cachedSetTimeout = setTimeout;
+ return setTimeout(fun, 0);
+ }
+ try {
+ // when when somebody has screwed with setTimeout but no I.E. maddness
+ return cachedSetTimeout(fun, 0);
+ } catch(e){
+ try {
+ // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
+ return cachedSetTimeout.call(null, fun, 0);
+ } catch(e){
+ // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
+ return cachedSetTimeout.call(this, fun, 0);
+ }
+ }
+
+
+}
+function runClearTimeout(marker) {
+ if (cachedClearTimeout === clearTimeout) {
+ //normal enviroments in sane situations
+ return clearTimeout(marker);
+ }
+ // if clearTimeout wasn't available but was latter defined
+ if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
+ cachedClearTimeout = clearTimeout;
+ return clearTimeout(marker);
+ }
+ try {
+ // when when somebody has screwed with setTimeout but no I.E. maddness
+ return cachedClearTimeout(marker);
+ } catch (e){
+ try {
+ // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
+ return cachedClearTimeout.call(null, marker);
+ } catch (e){
+ // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
+ // Some versions of I.E. have different rules for clearTimeout vs setTimeout
+ return cachedClearTimeout.call(this, marker);
+ }
+ }
+
+
+
+}
+var queue = [];
+var draining = false;
+var currentQueue;
+var queueIndex = -1;
+
+function cleanUpNextTick() {
+ if (!draining || !currentQueue) {
+ return;
+ }
+ draining = false;
+ if (currentQueue.length) {
+ queue = currentQueue.concat(queue);
+ } else {
+ queueIndex = -1;
+ }
+ if (queue.length) {
+ drainQueue();
+ }
+}
+
+function drainQueue() {
+ if (draining) {
+ return;
+ }
+ var timeout = runTimeout(cleanUpNextTick);
+ draining = true;
+
+ var len = queue.length;
+ while(len) {
+ currentQueue = queue;
+ queue = [];
+ while (++queueIndex < len) {
+ if (currentQueue) {
+ currentQueue[queueIndex].run();
+ }
+ }
+ queueIndex = -1;
+ len = queue.length;
+ }
+ currentQueue = null;
+ draining = false;
+ runClearTimeout(timeout);
+}
+
+process.nextTick = function (fun) {
+ var args = new Array(arguments.length - 1);
+ if (arguments.length > 1) {
+ for (var i = 1; i < arguments.length; i++) {
+ args[i - 1] = arguments[i];
+ }
+ }
+ queue.push(new Item(fun, args));
+ if (queue.length === 1 && !draining) {
+ runTimeout(drainQueue);
+ }
+};
+
+// v8 likes predictible objects
+function Item(fun, array) {
+ this.fun = fun;
+ this.array = array;
+}
+Item.prototype.run = function () {
+ this.fun.apply(null, this.array);
+};
+process.title = 'browser';
+process.browser = true;
+process.env = {};
+process.argv = [];
+process.version = ''; // empty string to avoid regexp issues
+process.versions = {};
+
+function noop() {}
+
+process.on = noop;
+process.addListener = noop;
+process.once = noop;
+process.off = noop;
+process.removeListener = noop;
+process.removeAllListeners = noop;
+process.emit = noop;
+
+process.binding = function (name) {
+ throw new Error('process.binding is not supported');
+};
+
+process.cwd = function () { return '/' };
+process.chdir = function (dir) {
+ throw new Error('process.chdir is not supported');
+};
+process.umask = function() { return 0; };
+
+},{}],13:[function(require,module,exports){
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+'use strict';
+
+// If obj.hasOwnProperty has been overridden, then calling
+// obj.hasOwnProperty(prop) will break.
+// See: https://github.com/joyent/node/issues/1707
+function hasOwnProperty(obj, prop) {
+ return Object.prototype.hasOwnProperty.call(obj, prop);
+}
+
+module.exports = function(qs, sep, eq, options) {
+ sep = sep || '&';
+ eq = eq || '=';
+ var obj = {};
+
+ if (typeof qs !== 'string' || qs.length === 0) {
+ return obj;
+ }
+
+ var regexp = /\+/g;
+ qs = qs.split(sep);
+
+ var maxKeys = 1000;
+ if (options && typeof options.maxKeys === 'number') {
+ maxKeys = options.maxKeys;
+ }
+
+ var len = qs.length;
+ // maxKeys <= 0 means that we should not limit keys count
+ if (maxKeys > 0 && len > maxKeys) {
+ len = maxKeys;
+ }
+
+ for (var i = 0; i < len; ++i) {
+ var x = qs[i].replace(regexp, '%20'),
+ idx = x.indexOf(eq),
+ kstr, vstr, k, v;
+
+ if (idx >= 0) {
+ kstr = x.substr(0, idx);
+ vstr = x.substr(idx + 1);
+ } else {
+ kstr = x;
+ vstr = '';
+ }
+
+ k = decodeURIComponent(kstr);
+ v = decodeURIComponent(vstr);
+
+ if (!hasOwnProperty(obj, k)) {
+ obj[k] = v;
+ } else if (isArray(obj[k])) {
+ obj[k].push(v);
+ } else {
+ obj[k] = [obj[k], v];
+ }
+ }
+
+ return obj;
+};
+
+var isArray = Array.isArray || function (xs) {
+ return Object.prototype.toString.call(xs) === '[object Array]';
+};
+
+},{}],14:[function(require,module,exports){
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+'use strict';
+
+var stringifyPrimitive = function(v) {
+ switch (typeof v) {
+ case 'string':
+ return v;
+
+ case 'boolean':
+ return v ? 'true' : 'false';
+
+ case 'number':
+ return isFinite(v) ? v : '';
+
+ default:
+ return '';
+ }
+};
+
+module.exports = function(obj, sep, eq, name) {
+ sep = sep || '&';
+ eq = eq || '=';
+ if (obj === null) {
+ obj = undefined;
+ }
+
+ if (typeof obj === 'object') {
+ return map(objectKeys(obj), function(k) {
+ var ks = encodeURIComponent(stringifyPrimitive(k)) + eq;
+ if (isArray(obj[k])) {
+ return map(obj[k], function(v) {
+ return ks + encodeURIComponent(stringifyPrimitive(v));
+ }).join(sep);
+ } else {
+ return ks + encodeURIComponent(stringifyPrimitive(obj[k]));
+ }
+ }).join(sep);
+
+ }
+
+ if (!name) return '';
+ return encodeURIComponent(stringifyPrimitive(name)) + eq +
+ encodeURIComponent(stringifyPrimitive(obj));
+};
+
+var isArray = Array.isArray || function (xs) {
+ return Object.prototype.toString.call(xs) === '[object Array]';
+};
+
+function map (xs, f) {
+ if (xs.map) return xs.map(f);
+ var res = [];
+ for (var i = 0; i < xs.length; i++) {
+ res.push(f(xs[i], i));
+ }
+ return res;
+}
+
+var objectKeys = Object.keys || function (obj) {
+ var res = [];
+ for (var key in obj) {
+ if (Object.prototype.hasOwnProperty.call(obj, key)) res.push(key);
+ }
+ return res;
+};
+
+},{}],15:[function(require,module,exports){
+'use strict';
+
+exports.decode = exports.parse = require(13);
+exports.encode = exports.stringify = require(14);
+
+},{"13":13,"14":14}],16:[function(require,module,exports){
+module.exports = AlgoliaSearch;
+
+var Index = require(18);
+var deprecate = require(28);
+var deprecatedMessage = require(29);
+var AlgoliaSearchCore = require(17);
+var inherits = require(7);
+var errors = require(30);
+
+function AlgoliaSearch() {
+ AlgoliaSearchCore.apply(this, arguments);
+}
+
+inherits(AlgoliaSearch, AlgoliaSearchCore);
+
+/*
+ * Delete an index
+ *
+ * @param indexName the name of index to delete
+ * @param callback the result callback called with two arguments
+ * error: null or Error('message')
+ * content: the server answer that contains the task ID
+ */
+AlgoliaSearch.prototype.deleteIndex = function(indexName, callback) {
+ return this._jsonRequest({
+ method: 'DELETE',
+ url: '/1/indexes/' + encodeURIComponent(indexName),
+ hostType: 'write',
+ callback: callback
+ });
+};
+
+/**
+ * Move an existing index.
+ * @param srcIndexName the name of index to copy.
+ * @param dstIndexName the new index name that will contains a copy of
+ * srcIndexName (destination will be overriten if it already exist).
+ * @param callback the result callback called with two arguments
+ * error: null or Error('message')
+ * content: the server answer that contains the task ID
+ */
+AlgoliaSearch.prototype.moveIndex = function(srcIndexName, dstIndexName, callback) {
+ var postObj = {
+ operation: 'move', destination: dstIndexName
+ };
+ return this._jsonRequest({
+ method: 'POST',
+ url: '/1/indexes/' + encodeURIComponent(srcIndexName) + '/operation',
+ body: postObj,
+ hostType: 'write',
+ callback: callback
+ });
+};
+
+/**
+ * Copy an existing index.
+ * @param srcIndexName the name of index to copy.
+ * @param dstIndexName the new index name that will contains a copy
+ * of srcIndexName (destination will be overriten if it already exist).
+ * @param scope an array of scopes to copy: ['settings', 'synonyms', 'rules']
+ * @param callback the result callback called with two arguments
+ * error: null or Error('message')
+ * content: the server answer that contains the task ID
+ */
+AlgoliaSearch.prototype.copyIndex = function(srcIndexName, dstIndexName, scopeOrCallback, _callback) {
+ var postObj = {
+ operation: 'copy',
+ destination: dstIndexName
+ };
+ var callback = _callback;
+ if (typeof scopeOrCallback === 'function') {
+ // oops, old behaviour of third argument being a function
+ callback = scopeOrCallback;
+ } else if (Array.isArray(scopeOrCallback) && scopeOrCallback.length > 0) {
+ postObj.scope = scopeOrCallback;
+ } else if (typeof scopeOrCallback !== 'undefined') {
+ throw new Error('the scope given to `copyIndex` was not an array with settings, synonyms or rules');
+ }
+ return this._jsonRequest({
+ method: 'POST',
+ url: '/1/indexes/' + encodeURIComponent(srcIndexName) + '/operation',
+ body: postObj,
+ hostType: 'write',
+ callback: callback
+ });
+};
+
+/**
+ * Return last log entries.
+ * @param offset Specify the first entry to retrieve (0-based, 0 is the most recent log entry).
+ * @param length Specify the maximum number of entries to retrieve starting
+ * at offset. Maximum allowed value: 1000.
+ * @param type Specify the maximum number of entries to retrieve starting
+ * at offset. Maximum allowed value: 1000.
+ * @param callback the result callback called with two arguments
+ * error: null or Error('message')
+ * content: the server answer that contains the task ID
+ */
+AlgoliaSearch.prototype.getLogs = function(offset, length, callback) {
+ var clone = require(26);
+ var params = {};
+ if (typeof offset === 'object') {
+ // getLogs(params)
+ params = clone(offset);
+ callback = length;
+ } else if (arguments.length === 0 || typeof offset === 'function') {
+ // getLogs([cb])
+ callback = offset;
+ } else if (arguments.length === 1 || typeof length === 'function') {
+ // getLogs(1, [cb)]
+ callback = length;
+ params.offset = offset;
+ } else {
+ // getLogs(1, 2, [cb])
+ params.offset = offset;
+ params.length = length;
+ }
+
+ if (params.offset === undefined) params.offset = 0;
+ if (params.length === undefined) params.length = 10;
+
+ return this._jsonRequest({
+ method: 'GET',
+ url: '/1/logs?' + this._getSearchParams(params, ''),
+ hostType: 'read',
+ callback: callback
+ });
+};
+
+/*
+ * List all existing indexes (paginated)
+ *
+ * @param page The page to retrieve, starting at 0.
+ * @param callback the result callback called with two arguments
+ * error: null or Error('message')
+ * content: the server answer with index list
+ */
+AlgoliaSearch.prototype.listIndexes = function(page, callback) {
+ var params = '';
+
+ if (page === undefined || typeof page === 'function') {
+ callback = page;
+ } else {
+ params = '?page=' + page;
+ }
+
+ return this._jsonRequest({
+ method: 'GET',
+ url: '/1/indexes' + params,
+ hostType: 'read',
+ callback: callback
+ });
+};
+
+/*
+ * Get the index object initialized
+ *
+ * @param indexName the name of index
+ * @param callback the result callback with one argument (the Index instance)
+ */
+AlgoliaSearch.prototype.initIndex = function(indexName) {
+ return new Index(this, indexName);
+};
+
+AlgoliaSearch.prototype.initAnalytics = function(opts) {
+ // the actual require must be inside the function, when put outside then you have a cyclic dependency
+ // not well resolved that ends up making the main "./index.js" (main module, the agloliasearch function)
+ // export an object instead of a function
+ // Other workarounds:
+ // - rewrite the lib in ES6, cyclic dependencies may be better supported
+ // - move initAnalytics to a property on the main module (algoliasearch.initAnalytics),
+ // same as places.
+ // The current API was made mostly to mimic the one made in PHP
+ var createAnalyticsClient = require(27);
+ return createAnalyticsClient(this.applicationID, this.apiKey, opts);
+};
+
+/*
+ * @deprecated use client.listApiKeys
+ */
+AlgoliaSearch.prototype.listUserKeys = deprecate(function(callback) {
+ return this.listApiKeys(callback);
+}, deprecatedMessage('client.listUserKeys()', 'client.listApiKeys()'));
+
+/*
+ * List all existing api keys with their associated ACLs
+ *
+ * @param callback the result callback called with two arguments
+ * error: null or Error('message')
+ * content: the server answer with api keys list
+ */
+AlgoliaSearch.prototype.listApiKeys = function(callback) {
+ return this._jsonRequest({
+ method: 'GET',
+ url: '/1/keys',
+ hostType: 'read',
+ callback: callback
+ });
+};
+
+/*
+ * @deprecated see client.getApiKey
+ */
+AlgoliaSearch.prototype.getUserKeyACL = deprecate(function(key, callback) {
+ return this.getApiKey(key, callback);
+}, deprecatedMessage('client.getUserKeyACL()', 'client.getApiKey()'));
+
+/*
+ * Get an API key
+ *
+ * @param key
+ * @param callback the result callback called with two arguments
+ * error: null or Error('message')
+ * content: the server answer with the right API key
+ */
+AlgoliaSearch.prototype.getApiKey = function(key, callback) {
+ return this._jsonRequest({
+ method: 'GET',
+ url: '/1/keys/' + key,
+ hostType: 'read',
+ callback: callback
+ });
+};
+
+/*
+ * @deprecated see client.deleteApiKey
+ */
+AlgoliaSearch.prototype.deleteUserKey = deprecate(function(key, callback) {
+ return this.deleteApiKey(key, callback);
+}, deprecatedMessage('client.deleteUserKey()', 'client.deleteApiKey()'));
+
+/*
+ * Delete an existing API key
+ * @param key
+ * @param callback the result callback called with two arguments
+ * error: null or Error('message')
+ * content: the server answer with the date of deletion
+ */
+AlgoliaSearch.prototype.deleteApiKey = function(key, callback) {
+ return this._jsonRequest({
+ method: 'DELETE',
+ url: '/1/keys/' + key,
+ hostType: 'write',
+ callback: callback
+ });
+};
+
+/**
+ * Restore a deleted API key
+ *
+ * @param {String} key - The key to restore
+ * @param {Function} callback - The result callback called with two arguments
+ * error: null or Error('message')
+ * content: the server answer with the restored API key
+ * @return {Promise|undefined} Returns a promise if no callback given
+ * @example
+ * client.restoreApiKey('APIKEY')
+ * @see {@link https://www.algolia.com/doc/rest-api/search/#restore-api-key|Algolia REST API Documentation}
+ */
+AlgoliaSearch.prototype.restoreApiKey = function(key, callback) {
+ return this._jsonRequest({
+ method: 'POST',
+ url: '/1/keys/' + key + '/restore',
+ hostType: 'write',
+ callback: callback
+ });
+};
+
+/*
+ @deprecated see client.addApiKey
+ */
+AlgoliaSearch.prototype.addUserKey = deprecate(function(acls, params, callback) {
+ return this.addApiKey(acls, params, callback);
+}, deprecatedMessage('client.addUserKey()', 'client.addApiKey()'));
+
+/*
+ * Add a new global API key
+ *
+ * @param {string[]} acls - The list of ACL for this key. Defined by an array of strings that
+ * can contains the following values:
+ * - search: allow to search (https and http)
+ * - addObject: allows to add/update an object in the index (https only)
+ * - deleteObject : allows to delete an existing object (https only)
+ * - deleteIndex : allows to delete index content (https only)
+ * - settings : allows to get index settings (https only)
+ * - editSettings : allows to change index settings (https only)
+ * @param {Object} [params] - Optionnal parameters to set for the key
+ * @param {number} params.validity - Number of seconds after which the key will be automatically removed (0 means no time limit for this key)
+ * @param {number} params.maxQueriesPerIPPerHour - Number of API calls allowed from an IP address per hour
+ * @param {number} params.maxHitsPerQuery - Number of hits this API key can retrieve in one call
+ * @param {string[]} params.indexes - Allowed targeted indexes for this key
+ * @param {string} params.description - A description for your key
+ * @param {string[]} params.referers - A list of authorized referers
+ * @param {Object} params.queryParameters - Force the key to use specific query parameters
+ * @param {Function} callback - The result callback called with two arguments
+ * error: null or Error('message')
+ * content: the server answer with the added API key
+ * @return {Promise|undefined} Returns a promise if no callback given
+ * @example
+ * client.addApiKey(['search'], {
+ * validity: 300,
+ * maxQueriesPerIPPerHour: 2000,
+ * maxHitsPerQuery: 3,
+ * indexes: ['fruits'],
+ * description: 'Eat three fruits',
+ * referers: ['*.algolia.com'],
+ * queryParameters: {
+ * tagFilters: ['public'],
+ * }
+ * })
+ * @see {@link https://www.algolia.com/doc/rest_api#AddKey|Algolia REST API Documentation}
+ */
+AlgoliaSearch.prototype.addApiKey = function(acls, params, callback) {
+ var isArray = require(8);
+ var usage = 'Usage: client.addApiKey(arrayOfAcls[, params, callback])';
+
+ if (!isArray(acls)) {
+ throw new Error(usage);
+ }
+
+ if (arguments.length === 1 || typeof params === 'function') {
+ callback = params;
+ params = null;
+ }
+
+ var postObj = {
+ acl: acls
+ };
+
+ if (params) {
+ postObj.validity = params.validity;
+ postObj.maxQueriesPerIPPerHour = params.maxQueriesPerIPPerHour;
+ postObj.maxHitsPerQuery = params.maxHitsPerQuery;
+ postObj.indexes = params.indexes;
+ postObj.description = params.description;
+
+ if (params.queryParameters) {
+ postObj.queryParameters = this._getSearchParams(params.queryParameters, '');
+ }
+
+ postObj.referers = params.referers;
+ }
+
+ return this._jsonRequest({
+ method: 'POST',
+ url: '/1/keys',
+ body: postObj,
+ hostType: 'write',
+ callback: callback
+ });
+};
+
+/**
+ * @deprecated Please use client.addApiKey()
+ */
+AlgoliaSearch.prototype.addUserKeyWithValidity = deprecate(function(acls, params, callback) {
+ return this.addApiKey(acls, params, callback);
+}, deprecatedMessage('client.addUserKeyWithValidity()', 'client.addApiKey()'));
+
+/**
+ * @deprecated Please use client.updateApiKey()
+ */
+AlgoliaSearch.prototype.updateUserKey = deprecate(function(key, acls, params, callback) {
+ return this.updateApiKey(key, acls, params, callback);
+}, deprecatedMessage('client.updateUserKey()', 'client.updateApiKey()'));
+
+/**
+ * Update an existing API key
+ * @param {string} key - The key to update
+ * @param {string[]} acls - The list of ACL for this key. Defined by an array of strings that
+ * can contains the following values:
+ * - search: allow to search (https and http)
+ * - addObject: allows to add/update an object in the index (https only)
+ * - deleteObject : allows to delete an existing object (https only)
+ * - deleteIndex : allows to delete index content (https only)
+ * - settings : allows to get index settings (https only)
+ * - editSettings : allows to change index settings (https only)
+ * @param {Object} [params] - Optionnal parameters to set for the key
+ * @param {number} params.validity - Number of seconds after which the key will be automatically removed (0 means no time limit for this key)
+ * @param {number} params.maxQueriesPerIPPerHour - Number of API calls allowed from an IP address per hour
+ * @param {number} params.maxHitsPerQuery - Number of hits this API key can retrieve in one call
+ * @param {string[]} params.indexes - Allowed targeted indexes for this key
+ * @param {string} params.description - A description for your key
+ * @param {string[]} params.referers - A list of authorized referers
+ * @param {Object} params.queryParameters - Force the key to use specific query parameters
+ * @param {Function} callback - The result callback called with two arguments
+ * error: null or Error('message')
+ * content: the server answer with the modified API key
+ * @return {Promise|undefined} Returns a promise if no callback given
+ * @example
+ * client.updateApiKey('APIKEY', ['search'], {
+ * validity: 300,
+ * maxQueriesPerIPPerHour: 2000,
+ * maxHitsPerQuery: 3,
+ * indexes: ['fruits'],
+ * description: 'Eat three fruits',
+ * referers: ['*.algolia.com'],
+ * queryParameters: {
+ * tagFilters: ['public'],
+ * }
+ * })
+ * @see {@link https://www.algolia.com/doc/rest_api#UpdateIndexKey|Algolia REST API Documentation}
+ */
+AlgoliaSearch.prototype.updateApiKey = function(key, acls, params, callback) {
+ var isArray = require(8);
+ var usage = 'Usage: client.updateApiKey(key, arrayOfAcls[, params, callback])';
+
+ if (!isArray(acls)) {
+ throw new Error(usage);
+ }
+
+ if (arguments.length === 2 || typeof params === 'function') {
+ callback = params;
+ params = null;
+ }
+
+ var putObj = {
+ acl: acls
+ };
+
+ if (params) {
+ putObj.validity = params.validity;
+ putObj.maxQueriesPerIPPerHour = params.maxQueriesPerIPPerHour;
+ putObj.maxHitsPerQuery = params.maxHitsPerQuery;
+ putObj.indexes = params.indexes;
+ putObj.description = params.description;
+
+ if (params.queryParameters) {
+ putObj.queryParameters = this._getSearchParams(params.queryParameters, '');
+ }
+
+ putObj.referers = params.referers;
+ }
+
+ return this._jsonRequest({
+ method: 'PUT',
+ url: '/1/keys/' + key,
+ body: putObj,
+ hostType: 'write',
+ callback: callback
+ });
+};
+
+/**
+ * Initialize a new batch of search queries
+ * @deprecated use client.search()
+ */
+AlgoliaSearch.prototype.startQueriesBatch = deprecate(function startQueriesBatchDeprecated() {
+ this._batch = [];
+}, deprecatedMessage('client.startQueriesBatch()', 'client.search()'));
+
+/**
+ * Add a search query in the batch
+ * @deprecated use client.search()
+ */
+AlgoliaSearch.prototype.addQueryInBatch = deprecate(function addQueryInBatchDeprecated(indexName, query, args) {
+ this._batch.push({
+ indexName: indexName,
+ query: query,
+ params: args
+ });
+}, deprecatedMessage('client.addQueryInBatch()', 'client.search()'));
+
+/**
+ * Launch the batch of queries using XMLHttpRequest.
+ * @deprecated use client.search()
+ */
+AlgoliaSearch.prototype.sendQueriesBatch = deprecate(function sendQueriesBatchDeprecated(callback) {
+ return this.search(this._batch, callback);
+}, deprecatedMessage('client.sendQueriesBatch()', 'client.search()'));
+
+/**
+ * Perform write operations across multiple indexes.
+ *
+ * To reduce the amount of time spent on network round trips,
+ * you can create, update, or delete several objects in one call,
+ * using the batch endpoint (all operations are done in the given order).
+ *
+ * Available actions:
+ * - addObject
+ * - updateObject
+ * - partialUpdateObject
+ * - partialUpdateObjectNoCreate
+ * - deleteObject
+ *
+ * https://www.algolia.com/doc/rest_api#Indexes
+ * @param {Object[]} operations An array of operations to perform
+ * @return {Promise|undefined} Returns a promise if no callback given
+ * @example
+ * client.batch([{
+ * action: 'addObject',
+ * indexName: 'clients',
+ * body: {
+ * name: 'Bill'
+ * }
+ * }, {
+ * action: 'udpateObject',
+ * indexName: 'fruits',
+ * body: {
+ * objectID: '29138',
+ * name: 'banana'
+ * }
+ * }], cb)
+ */
+AlgoliaSearch.prototype.batch = function(operations, callback) {
+ var isArray = require(8);
+ var usage = 'Usage: client.batch(operations[, callback])';
+
+ if (!isArray(operations)) {
+ throw new Error(usage);
+ }
+
+ return this._jsonRequest({
+ method: 'POST',
+ url: '/1/indexes/*/batch',
+ body: {
+ requests: operations
+ },
+ hostType: 'write',
+ callback: callback
+ });
+};
+
+/**
+ * Assign or Move a userID to a cluster
+ *
+ * @param {string} data.userID The userID to assign to a new cluster
+ * @param {string} data.cluster The cluster to assign the user to
+ * @return {Promise|undefined} Returns a promise if no callback given
+ * @example
+ * client.assignUserID({ cluster: 'c1-test', userID: 'some-user' });
+ */
+AlgoliaSearch.prototype.assignUserID = function(data, callback) {
+ if (!data.userID || !data.cluster) {
+ throw new errors.AlgoliaSearchError('You have to provide both a userID and cluster', data);
+ }
+ return this._jsonRequest({
+ method: 'POST',
+ url: '/1/clusters/mapping',
+ hostType: 'write',
+ body: {cluster: data.cluster},
+ callback: callback,
+ headers: {
+ 'x-algolia-user-id': data.userID
+ }
+ });
+};
+
+/**
+ * Assign a array of userIDs to a cluster.
+ *
+ * @param {Array} data.userIDs The array of userIDs to assign to a new cluster
+ * @param {string} data.cluster The cluster to assign the user to
+ * @return {Promise|undefined} Returns a promise if no callback given
+ * @example
+ * client.assignUserIDs({ cluster: 'c1-test', userIDs: ['some-user-1', 'some-user-2'] });
+ */
+AlgoliaSearch.prototype.assignUserIDs = function(data, callback) {
+ if (!data.userIDs || !data.cluster) {
+ throw new errors.AlgoliaSearchError('You have to provide both an array of userIDs and cluster', data);
+ }
+ return this._jsonRequest({
+ method: 'POST',
+ url: '/1/clusters/mapping/batch',
+ hostType: 'write',
+ body: {
+ cluster: data.cluster,
+ users: data.userIDs
+ },
+ callback: callback
+ });
+};
+
+/**
+ * Get the top userIDs
+ *
+ * (the callback is the second argument)
+ *
+ * @return {Promise|undefined} Returns a promise if no callback given
+ * @example
+ * client.getTopUserID();
+ */
+AlgoliaSearch.prototype.getTopUserID = function(callback) {
+ return this._jsonRequest({
+ method: 'GET',
+ url: '/1/clusters/mapping/top',
+ hostType: 'read',
+ callback: callback
+ });
+};
+
+/**
+ * Get userID
+ *
+ * @param {string} data.userID The userID to get info about
+ * @return {Promise|undefined} Returns a promise if no callback given
+ * @example
+ * client.getUserID({ userID: 'some-user' });
+ */
+AlgoliaSearch.prototype.getUserID = function(data, callback) {
+ if (!data.userID) {
+ throw new errors.AlgoliaSearchError('You have to provide a userID', {debugData: data});
+ }
+ return this._jsonRequest({
+ method: 'GET',
+ url: '/1/clusters/mapping/' + data.userID,
+ hostType: 'read',
+ callback: callback
+ });
+};
+
+/**
+ * List all the clusters
+ *
+ * (the callback is the second argument)
+ *
+ * @return {Promise|undefined} Returns a promise if no callback given
+ * @example
+ * client.listClusters();
+ */
+AlgoliaSearch.prototype.listClusters = function(callback) {
+ return this._jsonRequest({
+ method: 'GET',
+ url: '/1/clusters',
+ hostType: 'read',
+ callback: callback
+ });
+};
+
+/**
+ * List the userIDs
+ *
+ * (the callback is the second argument)
+ *
+ * @param {string} data.hitsPerPage How many hits on every page
+ * @param {string} data.page The page to retrieve
+ * @return {Promise|undefined} Returns a promise if no callback given
+ * @example
+ * client.listClusters();
+ * client.listClusters({ page: 3, hitsPerPage: 30});
+ */
+AlgoliaSearch.prototype.listUserIDs = function(data, callback) {
+ return this._jsonRequest({
+ method: 'GET',
+ url: '/1/clusters/mapping',
+ body: data,
+ hostType: 'read',
+ callback: callback
+ });
+};
+
+/**
+ * Remove an userID
+ *
+ * @param {string} data.userID The userID to assign to a new cluster
+ * @return {Promise|undefined} Returns a promise if no callback given
+ * @example
+ * client.removeUserID({ userID: 'some-user' });
+ */
+AlgoliaSearch.prototype.removeUserID = function(data, callback) {
+ if (!data.userID) {
+ throw new errors.AlgoliaSearchError('You have to provide a userID', {debugData: data});
+ }
+ return this._jsonRequest({
+ method: 'DELETE',
+ url: '/1/clusters/mapping',
+ hostType: 'write',
+ callback: callback,
+ headers: {
+ 'x-algolia-user-id': data.userID
+ }
+ });
+};
+
+/**
+ * Search for userIDs
+ *
+ * @param {string} data.cluster The cluster to target
+ * @param {string} data.query The query to execute
+ * @param {string} data.hitsPerPage How many hits on every page
+ * @param {string} data.page The page to retrieve
+ * @return {Promise|undefined} Returns a promise if no callback given
+ * @example
+ * client.searchUserIDs({ cluster: 'c1-test', query: 'some-user' });
+ * client.searchUserIDs({
+ * cluster: "c1-test",
+ * query: "some-user",
+ * page: 3,
+ * hitsPerPage: 2
+ * });
+ */
+AlgoliaSearch.prototype.searchUserIDs = function(data, callback) {
+ return this._jsonRequest({
+ method: 'POST',
+ url: '/1/clusters/mapping/search',
+ body: data,
+ hostType: 'read',
+ callback: callback
+ });
+};
+
+/**
+ * Set strategy for personalization
+ *
+ * @param {Object} data
+ * @param {Object} data.eventsScoring Associate a score to an event
+ * @param {Object} data.eventsScoring. The name of the event
+ * @param {Number} data.eventsScoring..score The score to associate to
+ * @param {String} data.eventsScoring..type Either "click", "conversion" or "view"
+ * @param {Object} data.facetsScoring Associate a score to a facet
+ * @param {Object} data.facetsScoring. The name of the facet
+ * @param {Number} data.facetsScoring..score The score to associate to
+ * @return {Promise|undefined} Returns a promise if no callback given
+ * @example
+ * client.setPersonalizationStrategy({
+ * eventsScoring: {
+ * "Add to cart": { score: 50, type: "conversion" },
+ * Purchase: { score: 100, type: "conversion" }
+ * },
+ * facetsScoring: {
+ * brand: { score: 100 },
+ * categories: { score: 10 }
+ * }
+ * });
+ */
+AlgoliaSearch.prototype.setPersonalizationStrategy = function(data, callback) {
+ return this._jsonRequest({
+ method: 'POST',
+ url: '/1/recommendation/personalization/strategy',
+ body: data,
+ hostType: 'write',
+ callback: callback
+ });
+};
+
+/**
+ * Get strategy for personalization
+ *
+ * @return {Promise|undefined} Returns a promise if no callback given
+ * @example
+ * client.getPersonalizationStrategy();
+ */
+
+AlgoliaSearch.prototype.getPersonalizationStrategy = function(callback) {
+ return this._jsonRequest({
+ method: 'GET',
+ url: '/1/recommendation/personalization/strategy',
+ hostType: 'read',
+ callback: callback
+ });
+};
+
+// environment specific methods
+AlgoliaSearch.prototype.destroy = notImplemented;
+AlgoliaSearch.prototype.enableRateLimitForward = notImplemented;
+AlgoliaSearch.prototype.disableRateLimitForward = notImplemented;
+AlgoliaSearch.prototype.useSecuredAPIKey = notImplemented;
+AlgoliaSearch.prototype.disableSecuredAPIKey = notImplemented;
+AlgoliaSearch.prototype.generateSecuredApiKey = notImplemented;
+AlgoliaSearch.prototype.getSecuredApiKeyRemainingValidity = notImplemented;
+
+function notImplemented() {
+ var message = 'Not implemented in this environment.\n' +
+ 'If you feel this is a mistake, write to support@algolia.com';
+
+ throw new errors.AlgoliaSearchError(message);
+}
+
+},{"17":17,"18":18,"26":26,"27":27,"28":28,"29":29,"30":30,"7":7,"8":8}],17:[function(require,module,exports){
+(function (process){
+module.exports = AlgoliaSearchCore;
+
+var errors = require(30);
+var exitPromise = require(31);
+var IndexCore = require(20);
+var store = require(36);
+
+// We will always put the API KEY in the JSON body in case of too long API KEY,
+// to avoid query string being too long and failing in various conditions (our server limit, browser limit,
+// proxies limit)
+var MAX_API_KEY_LENGTH = 500;
+var RESET_APP_DATA_TIMER =
+ process.env.RESET_APP_DATA_TIMER && parseInt(process.env.RESET_APP_DATA_TIMER, 10) ||
+ 60 * 2 * 1000; // after 2 minutes reset to first host
+
+/*
+ * Algolia Search library initialization
+ * https://www.algolia.com/
+ *
+ * @param {string} applicationID - Your applicationID, found in your dashboard
+ * @param {string} apiKey - Your API key, found in your dashboard
+ * @param {Object} [opts]
+ * @param {number} [opts.timeout=2000] - The request timeout set in milliseconds,
+ * another request will be issued after this timeout
+ * @param {string} [opts.protocol='https:'] - The protocol used to query Algolia Search API.
+ * Set to 'http:' to force using http.
+ * @param {Object|Array} [opts.hosts={
+ * read: [this.applicationID + '-dsn.algolia.net'].concat([
+ * this.applicationID + '-1.algolianet.com',
+ * this.applicationID + '-2.algolianet.com',
+ * this.applicationID + '-3.algolianet.com']
+ * ]),
+ * write: [this.applicationID + '.algolia.net'].concat([
+ * this.applicationID + '-1.algolianet.com',
+ * this.applicationID + '-2.algolianet.com',
+ * this.applicationID + '-3.algolianet.com']
+ * ]) - The hosts to use for Algolia Search API.
+ * If you provide them, you will less benefit from our HA implementation
+ */
+function AlgoliaSearchCore(applicationID, apiKey, opts) {
+ var debug = require(1)('algoliasearch');
+
+ var clone = require(26);
+ var isArray = require(8);
+ var map = require(32);
+
+ var usage = 'Usage: algoliasearch(applicationID, apiKey, opts)';
+
+ if (opts._allowEmptyCredentials !== true && !applicationID) {
+ throw new errors.AlgoliaSearchError('Please provide an application ID. ' + usage);
+ }
+
+ if (opts._allowEmptyCredentials !== true && !apiKey) {
+ throw new errors.AlgoliaSearchError('Please provide an API key. ' + usage);
+ }
+
+ this.applicationID = applicationID;
+ this.apiKey = apiKey;
+
+ this.hosts = {
+ read: [],
+ write: []
+ };
+
+ opts = opts || {};
+
+ this._timeouts = opts.timeouts || {
+ connect: 1 * 1000, // 500ms connect is GPRS latency
+ read: 2 * 1000,
+ write: 30 * 1000
+ };
+
+ // backward compat, if opts.timeout is passed, we use it to configure all timeouts like before
+ if (opts.timeout) {
+ this._timeouts.connect = this._timeouts.read = this._timeouts.write = opts.timeout;
+ }
+
+ var protocol = opts.protocol || 'https:';
+ // while we advocate for colon-at-the-end values: 'http:' for `opts.protocol`
+ // we also accept `http` and `https`. It's a common error.
+ if (!/:$/.test(protocol)) {
+ protocol = protocol + ':';
+ }
+
+ if (protocol !== 'http:' && protocol !== 'https:') {
+ throw new errors.AlgoliaSearchError('protocol must be `http:` or `https:` (was `' + opts.protocol + '`)');
+ }
+
+ this._checkAppIdData();
+
+ if (!opts.hosts) {
+ var defaultHosts = map(this._shuffleResult, function(hostNumber) {
+ return applicationID + '-' + hostNumber + '.algolianet.com';
+ });
+
+ // no hosts given, compute defaults
+ var mainSuffix = (opts.dsn === false ? '' : '-dsn') + '.algolia.net';
+ this.hosts.read = [this.applicationID + mainSuffix].concat(defaultHosts);
+ this.hosts.write = [this.applicationID + '.algolia.net'].concat(defaultHosts);
+ } else if (isArray(opts.hosts)) {
+ // when passing custom hosts, we need to have a different host index if the number
+ // of write/read hosts are different.
+ this.hosts.read = clone(opts.hosts);
+ this.hosts.write = clone(opts.hosts);
+ } else {
+ this.hosts.read = clone(opts.hosts.read);
+ this.hosts.write = clone(opts.hosts.write);
+ }
+
+ // add protocol and lowercase hosts
+ this.hosts.read = map(this.hosts.read, prepareHost(protocol));
+ this.hosts.write = map(this.hosts.write, prepareHost(protocol));
+
+ this.extraHeaders = {};
+
+ // In some situations you might want to warm the cache
+ this.cache = opts._cache || {};
+
+ this._ua = opts._ua;
+ this._useCache = opts._useCache === undefined || opts._cache ? true : opts._useCache;
+ this._useRequestCache = this._useCache && opts._useRequestCache;
+ this._useFallback = opts.useFallback === undefined ? true : opts.useFallback;
+
+ this._setTimeout = opts._setTimeout;
+
+ debug('init done, %j', this);
+}
+
+/*
+ * Get the index object initialized
+ *
+ * @param indexName the name of index
+ * @param callback the result callback with one argument (the Index instance)
+ */
+AlgoliaSearchCore.prototype.initIndex = function(indexName) {
+ return new IndexCore(this, indexName);
+};
+
+/**
+* Add an extra field to the HTTP request
+*
+* @param name the header field name
+* @param value the header field value
+*/
+AlgoliaSearchCore.prototype.setExtraHeader = function(name, value) {
+ this.extraHeaders[name.toLowerCase()] = value;
+};
+
+/**
+* Get the value of an extra HTTP header
+*
+* @param name the header field name
+*/
+AlgoliaSearchCore.prototype.getExtraHeader = function(name) {
+ return this.extraHeaders[name.toLowerCase()];
+};
+
+/**
+* Remove an extra field from the HTTP request
+*
+* @param name the header field name
+*/
+AlgoliaSearchCore.prototype.unsetExtraHeader = function(name) {
+ delete this.extraHeaders[name.toLowerCase()];
+};
+
+/**
+* Augment sent x-algolia-agent with more data, each agent part
+* is automatically separated from the others by a semicolon;
+*
+* @param algoliaAgent the agent to add
+*/
+AlgoliaSearchCore.prototype.addAlgoliaAgent = function(algoliaAgent) {
+ var algoliaAgentWithDelimiter = '; ' + algoliaAgent;
+
+ if (this._ua.indexOf(algoliaAgentWithDelimiter) === -1) {
+ this._ua += algoliaAgentWithDelimiter;
+ }
+};
+
+/*
+ * Wrapper that try all hosts to maximize the quality of service
+ */
+AlgoliaSearchCore.prototype._jsonRequest = function(initialOpts) {
+ this._checkAppIdData();
+
+ var requestDebug = require(1)('algoliasearch:' + initialOpts.url);
+
+
+ var body;
+ var cacheID;
+ var additionalUA = initialOpts.additionalUA || '';
+ var cache = initialOpts.cache;
+ var client = this;
+ var tries = 0;
+ var usingFallback = false;
+ var hasFallback = client._useFallback && client._request.fallback && initialOpts.fallback;
+ var headers;
+
+ if (
+ this.apiKey.length > MAX_API_KEY_LENGTH &&
+ initialOpts.body !== undefined &&
+ (initialOpts.body.params !== undefined || // index.search()
+ initialOpts.body.requests !== undefined) // client.search()
+ ) {
+ initialOpts.body.apiKey = this.apiKey;
+ headers = this._computeRequestHeaders({
+ additionalUA: additionalUA,
+ withApiKey: false,
+ headers: initialOpts.headers
+ });
+ } else {
+ headers = this._computeRequestHeaders({
+ additionalUA: additionalUA,
+ headers: initialOpts.headers
+ });
+ }
+
+ if (initialOpts.body !== undefined) {
+ body = safeJSONStringify(initialOpts.body);
+ }
+
+ requestDebug('request start');
+ var debugData = [];
+
+
+ function doRequest(requester, reqOpts) {
+ client._checkAppIdData();
+
+ var startTime = new Date();
+
+ if (client._useCache && !client._useRequestCache) {
+ cacheID = initialOpts.url;
+ }
+
+ // as we sometime use POST requests to pass parameters (like query='aa'),
+ // the cacheID must also include the body to be different between calls
+ if (client._useCache && !client._useRequestCache && body) {
+ cacheID += '_body_' + reqOpts.body;
+ }
+
+ // handle cache existence
+ if (isCacheValidWithCurrentID(!client._useRequestCache, cache, cacheID)) {
+ requestDebug('serving response from cache');
+
+ var responseText = cache[cacheID];
+
+ // Cache response must match the type of the original one
+ return client._promise.resolve({
+ body: JSON.parse(responseText),
+ responseText: responseText
+ });
+ }
+
+ // if we reached max tries
+ if (tries >= client.hosts[initialOpts.hostType].length) {
+ if (!hasFallback || usingFallback) {
+ requestDebug('could not get any response');
+ // then stop
+ return client._promise.reject(new errors.AlgoliaSearchError(
+ 'Cannot connect to the AlgoliaSearch API.' +
+ ' Send an email to support@algolia.com to report and resolve the issue.' +
+ ' Application id was: ' + client.applicationID, {debugData: debugData}
+ ));
+ }
+
+ requestDebug('switching to fallback');
+
+ // let's try the fallback starting from here
+ tries = 0;
+
+ // method, url and body are fallback dependent
+ reqOpts.method = initialOpts.fallback.method;
+ reqOpts.url = initialOpts.fallback.url;
+ reqOpts.jsonBody = initialOpts.fallback.body;
+ if (reqOpts.jsonBody) {
+ reqOpts.body = safeJSONStringify(reqOpts.jsonBody);
+ }
+ // re-compute headers, they could be omitting the API KEY
+ headers = client._computeRequestHeaders({
+ additionalUA: additionalUA,
+ headers: initialOpts.headers
+ });
+
+ reqOpts.timeouts = client._getTimeoutsForRequest(initialOpts.hostType);
+ client._setHostIndexByType(0, initialOpts.hostType);
+ usingFallback = true; // the current request is now using fallback
+ return doRequest(client._request.fallback, reqOpts);
+ }
+
+ var currentHost = client._getHostByType(initialOpts.hostType);
+
+ var url = currentHost + reqOpts.url;
+ var options = {
+ body: reqOpts.body,
+ jsonBody: reqOpts.jsonBody,
+ method: reqOpts.method,
+ headers: headers,
+ timeouts: reqOpts.timeouts,
+ debug: requestDebug,
+ forceAuthHeaders: reqOpts.forceAuthHeaders
+ };
+
+ requestDebug('method: %s, url: %s, headers: %j, timeouts: %d',
+ options.method, url, options.headers, options.timeouts);
+
+ if (requester === client._request.fallback) {
+ requestDebug('using fallback');
+ }
+
+ // `requester` is any of this._request or this._request.fallback
+ // thus it needs to be called using the client as context
+ return requester.call(client, url, options).then(success, tryFallback);
+
+ function success(httpResponse) {
+ // compute the status of the response,
+ //
+ // When in browser mode, using XDR or JSONP, we have no statusCode available
+ // So we rely on our API response `status` property.
+ // But `waitTask` can set a `status` property which is not the statusCode (it's the task status)
+ // So we check if there's a `message` along `status` and it means it's an error
+ //
+ // That's the only case where we have a response.status that's not the http statusCode
+ var status = httpResponse && httpResponse.body && httpResponse.body.message && httpResponse.body.status ||
+
+ // this is important to check the request statusCode AFTER the body eventual
+ // statusCode because some implementations (jQuery XDomainRequest transport) may
+ // send statusCode 200 while we had an error
+ httpResponse.statusCode ||
+
+ // When in browser mode, using XDR or JSONP
+ // we default to success when no error (no response.status && response.message)
+ // If there was a JSON.parse() error then body is null and it fails
+ httpResponse && httpResponse.body && 200;
+
+ requestDebug('received response: statusCode: %s, computed statusCode: %d, headers: %j',
+ httpResponse.statusCode, status, httpResponse.headers);
+
+ var httpResponseOk = Math.floor(status / 100) === 2;
+
+ var endTime = new Date();
+ debugData.push({
+ currentHost: currentHost,
+ headers: removeCredentials(headers),
+ content: body || null,
+ contentLength: body !== undefined ? body.length : null,
+ method: reqOpts.method,
+ timeouts: reqOpts.timeouts,
+ url: reqOpts.url,
+ startTime: startTime,
+ endTime: endTime,
+ duration: endTime - startTime,
+ statusCode: status
+ });
+
+ if (httpResponseOk) {
+ if (client._useCache && !client._useRequestCache && cache) {
+ cache[cacheID] = httpResponse.responseText;
+ }
+
+ return {
+ responseText: httpResponse.responseText,
+ body: httpResponse.body
+ };
+ }
+
+ var shouldRetry = Math.floor(status / 100) !== 4;
+
+ if (shouldRetry) {
+ tries += 1;
+ return retryRequest();
+ }
+
+ requestDebug('unrecoverable error');
+
+ // no success and no retry => fail
+ var unrecoverableError = new errors.AlgoliaSearchError(
+ httpResponse.body && httpResponse.body.message, {debugData: debugData, statusCode: status}
+ );
+
+ return client._promise.reject(unrecoverableError);
+ }
+
+ function tryFallback(err) {
+ // error cases:
+ // While not in fallback mode:
+ // - CORS not supported
+ // - network error
+ // While in fallback mode:
+ // - timeout
+ // - network error
+ // - badly formatted JSONP (script loaded, did not call our callback)
+ // In both cases:
+ // - uncaught exception occurs (TypeError)
+ requestDebug('error: %s, stack: %s', err.message, err.stack);
+
+ var endTime = new Date();
+ debugData.push({
+ currentHost: currentHost,
+ headers: removeCredentials(headers),
+ content: body || null,
+ contentLength: body !== undefined ? body.length : null,
+ method: reqOpts.method,
+ timeouts: reqOpts.timeouts,
+ url: reqOpts.url,
+ startTime: startTime,
+ endTime: endTime,
+ duration: endTime - startTime
+ });
+
+ if (!(err instanceof errors.AlgoliaSearchError)) {
+ err = new errors.Unknown(err && err.message, err);
+ }
+
+ tries += 1;
+
+ // stop the request implementation when:
+ if (
+ // we did not generate this error,
+ // it comes from a throw in some other piece of code
+ err instanceof errors.Unknown ||
+
+ // server sent unparsable JSON
+ err instanceof errors.UnparsableJSON ||
+
+ // max tries and already using fallback or no fallback
+ tries >= client.hosts[initialOpts.hostType].length &&
+ (usingFallback || !hasFallback)) {
+ // stop request implementation for this command
+ err.debugData = debugData;
+ return client._promise.reject(err);
+ }
+
+ // When a timeout occurred, retry by raising timeout
+ if (err instanceof errors.RequestTimeout) {
+ return retryRequestWithHigherTimeout();
+ }
+
+ return retryRequest();
+ }
+
+ function retryRequest() {
+ requestDebug('retrying request');
+ client._incrementHostIndex(initialOpts.hostType);
+ return doRequest(requester, reqOpts);
+ }
+
+ function retryRequestWithHigherTimeout() {
+ requestDebug('retrying request with higher timeout');
+ client._incrementHostIndex(initialOpts.hostType);
+ client._incrementTimeoutMultipler();
+ reqOpts.timeouts = client._getTimeoutsForRequest(initialOpts.hostType);
+ return doRequest(requester, reqOpts);
+ }
+ }
+
+ function isCacheValidWithCurrentID(
+ useRequestCache,
+ currentCache,
+ currentCacheID
+ ) {
+ return (
+ client._useCache &&
+ useRequestCache &&
+ currentCache &&
+ currentCache[currentCacheID] !== undefined
+ );
+ }
+
+
+ function interopCallbackReturn(request, callback) {
+ if (isCacheValidWithCurrentID(client._useRequestCache, cache, cacheID)) {
+ request.catch(function() {
+ // Release the cache on error
+ delete cache[cacheID];
+ });
+ }
+
+ if (typeof initialOpts.callback === 'function') {
+ // either we have a callback
+ request.then(function okCb(content) {
+ exitPromise(function() {
+ initialOpts.callback(null, callback(content));
+ }, client._setTimeout || setTimeout);
+ }, function nookCb(err) {
+ exitPromise(function() {
+ initialOpts.callback(err);
+ }, client._setTimeout || setTimeout);
+ });
+ } else {
+ // either we are using promises
+ return request.then(callback);
+ }
+ }
+
+ if (client._useCache && client._useRequestCache) {
+ cacheID = initialOpts.url;
+ }
+
+ // as we sometime use POST requests to pass parameters (like query='aa'),
+ // the cacheID must also include the body to be different between calls
+ if (client._useCache && client._useRequestCache && body) {
+ cacheID += '_body_' + body;
+ }
+
+ if (isCacheValidWithCurrentID(client._useRequestCache, cache, cacheID)) {
+ requestDebug('serving request from cache');
+
+ var maybePromiseForCache = cache[cacheID];
+
+ // In case the cache is warmup with value that is not a promise
+ var promiseForCache = typeof maybePromiseForCache.then !== 'function'
+ ? client._promise.resolve({responseText: maybePromiseForCache})
+ : maybePromiseForCache;
+
+ return interopCallbackReturn(promiseForCache, function(content) {
+ // In case of the cache request, return the original value
+ return JSON.parse(content.responseText);
+ });
+ }
+
+ var request = doRequest(
+ client._request, {
+ url: initialOpts.url,
+ method: initialOpts.method,
+ body: body,
+ jsonBody: initialOpts.body,
+ timeouts: client._getTimeoutsForRequest(initialOpts.hostType),
+ forceAuthHeaders: initialOpts.forceAuthHeaders
+ }
+ );
+
+ if (client._useCache && client._useRequestCache && cache) {
+ cache[cacheID] = request;
+ }
+
+ return interopCallbackReturn(request, function(content) {
+ // In case of the first request, return the JSON value
+ return content.body;
+ });
+};
+
+/*
+* Transform search param object in query string
+* @param {object} args arguments to add to the current query string
+* @param {string} params current query string
+* @return {string} the final query string
+*/
+AlgoliaSearchCore.prototype._getSearchParams = function(args, params) {
+ if (args === undefined || args === null) {
+ return params;
+ }
+ for (var key in args) {
+ if (key !== null && args[key] !== undefined && args.hasOwnProperty(key)) {
+ params += params === '' ? '' : '&';
+ params += key + '=' + encodeURIComponent(Object.prototype.toString.call(args[key]) === '[object Array]' ? safeJSONStringify(args[key]) : args[key]);
+ }
+ }
+ return params;
+};
+
+/**
+ * Compute the headers for a request
+ *
+ * @param [string] options.additionalUA semi-colon separated string with other user agents to add
+ * @param [boolean=true] options.withApiKey Send the api key as a header
+ * @param [Object] options.headers Extra headers to send
+ */
+AlgoliaSearchCore.prototype._computeRequestHeaders = function(options) {
+ var forEach = require(5);
+
+ var ua = options.additionalUA ?
+ this._ua + '; ' + options.additionalUA :
+ this._ua;
+
+ var requestHeaders = {
+ 'x-algolia-agent': ua,
+ 'x-algolia-application-id': this.applicationID
+ };
+
+ // browser will inline headers in the url, node.js will use http headers
+ // but in some situations, the API KEY will be too long (big secured API keys)
+ // so if the request is a POST and the KEY is very long, we will be asked to not put
+ // it into headers but in the JSON body
+ if (options.withApiKey !== false) {
+ requestHeaders['x-algolia-api-key'] = this.apiKey;
+ }
+
+ if (this.userToken) {
+ requestHeaders['x-algolia-usertoken'] = this.userToken;
+ }
+
+ if (this.securityTags) {
+ requestHeaders['x-algolia-tagfilters'] = this.securityTags;
+ }
+
+ forEach(this.extraHeaders, function addToRequestHeaders(value, key) {
+ requestHeaders[key] = value;
+ });
+
+ if (options.headers) {
+ forEach(options.headers, function addToRequestHeaders(value, key) {
+ requestHeaders[key] = value;
+ });
+ }
+
+ return requestHeaders;
+};
+
+/**
+ * Search through multiple indices at the same time
+ * @param {Object[]} queries An array of queries you want to run.
+ * @param {string} queries[].indexName The index name you want to target
+ * @param {string} [queries[].query] The query to issue on this index. Can also be passed into `params`
+ * @param {Object} queries[].params Any search param like hitsPerPage, ..
+ * @param {Function} callback Callback to be called
+ * @return {Promise|undefined} Returns a promise if no callback given
+ */
+AlgoliaSearchCore.prototype.search = function(queries, opts, callback) {
+ var isArray = require(8);
+ var map = require(32);
+
+ var usage = 'Usage: client.search(arrayOfQueries[, callback])';
+
+ if (!isArray(queries)) {
+ throw new Error(usage);
+ }
+
+ if (typeof opts === 'function') {
+ callback = opts;
+ opts = {};
+ } else if (opts === undefined) {
+ opts = {};
+ }
+
+ var client = this;
+
+ var postObj = {
+ requests: map(queries, function prepareRequest(query) {
+ var params = '';
+
+ // allow query.query
+ // so we are mimicing the index.search(query, params) method
+ // {indexName:, query:, params:}
+ if (query.query !== undefined) {
+ params += 'query=' + encodeURIComponent(query.query);
+ }
+
+ return {
+ indexName: query.indexName,
+ params: client._getSearchParams(query.params, params)
+ };
+ })
+ };
+
+ var JSONPParams = map(postObj.requests, function prepareJSONPParams(request, requestId) {
+ return requestId + '=' +
+ encodeURIComponent(
+ '/1/indexes/' + encodeURIComponent(request.indexName) + '?' +
+ request.params
+ );
+ }).join('&');
+
+ var url = '/1/indexes/*/queries';
+
+ if (opts.strategy !== undefined) {
+ postObj.strategy = opts.strategy;
+ }
+
+ return this._jsonRequest({
+ cache: this.cache,
+ method: 'POST',
+ url: url,
+ body: postObj,
+ hostType: 'read',
+ fallback: {
+ method: 'GET',
+ url: '/1/indexes/*',
+ body: {
+ params: JSONPParams
+ }
+ },
+ callback: callback
+ });
+};
+
+/**
+* Search for facet values
+* https://www.algolia.com/doc/rest-api/search#search-for-facet-values
+* This is the top-level API for SFFV.
+*
+* @param {object[]} queries An array of queries to run.
+* @param {string} queries[].indexName Index name, name of the index to search.
+* @param {object} queries[].params Query parameters.
+* @param {string} queries[].params.facetName Facet name, name of the attribute to search for values in.
+* Must be declared as a facet
+* @param {string} queries[].params.facetQuery Query for the facet search
+* @param {string} [queries[].params.*] Any search parameter of Algolia,
+* see https://www.algolia.com/doc/api-client/javascript/search#search-parameters
+* Pagination is not supported. The page and hitsPerPage parameters will be ignored.
+*/
+AlgoliaSearchCore.prototype.searchForFacetValues = function(queries) {
+ var isArray = require(8);
+ var map = require(32);
+
+ var usage = 'Usage: client.searchForFacetValues([{indexName, params: {facetName, facetQuery, ...params}}, ...queries])'; // eslint-disable-line max-len
+
+ if (!isArray(queries)) {
+ throw new Error(usage);
+ }
+
+ var client = this;
+
+ return client._promise.all(map(queries, function performQuery(query) {
+ if (
+ !query ||
+ query.indexName === undefined ||
+ query.params.facetName === undefined ||
+ query.params.facetQuery === undefined
+ ) {
+ throw new Error(usage);
+ }
+
+ var clone = require(26);
+ var omit = require(34);
+
+ var indexName = query.indexName;
+ var params = query.params;
+
+ var facetName = params.facetName;
+ var filteredParams = omit(clone(params), function(keyName) {
+ return keyName === 'facetName';
+ });
+ var searchParameters = client._getSearchParams(filteredParams, '');
+
+ return client._jsonRequest({
+ cache: client.cache,
+ method: 'POST',
+ url:
+ '/1/indexes/' +
+ encodeURIComponent(indexName) +
+ '/facets/' +
+ encodeURIComponent(facetName) +
+ '/query',
+ hostType: 'read',
+ body: {params: searchParameters}
+ });
+ }));
+};
+
+/**
+ * Set the extra security tagFilters header
+ * @param {string|array} tags The list of tags defining the current security filters
+ */
+AlgoliaSearchCore.prototype.setSecurityTags = function(tags) {
+ if (Object.prototype.toString.call(tags) === '[object Array]') {
+ var strTags = [];
+ for (var i = 0; i < tags.length; ++i) {
+ if (Object.prototype.toString.call(tags[i]) === '[object Array]') {
+ var oredTags = [];
+ for (var j = 0; j < tags[i].length; ++j) {
+ oredTags.push(tags[i][j]);
+ }
+ strTags.push('(' + oredTags.join(',') + ')');
+ } else {
+ strTags.push(tags[i]);
+ }
+ }
+ tags = strTags.join(',');
+ }
+
+ this.securityTags = tags;
+};
+
+/**
+ * Set the extra user token header
+ * @param {string} userToken The token identifying a uniq user (used to apply rate limits)
+ */
+AlgoliaSearchCore.prototype.setUserToken = function(userToken) {
+ this.userToken = userToken;
+};
+
+/**
+ * Clear all queries in client's cache
+ * @return undefined
+ */
+AlgoliaSearchCore.prototype.clearCache = function() {
+ this.cache = {};
+};
+
+/**
+* Set the number of milliseconds a request can take before automatically being terminated.
+* @deprecated
+* @param {Number} milliseconds
+*/
+AlgoliaSearchCore.prototype.setRequestTimeout = function(milliseconds) {
+ if (milliseconds) {
+ this._timeouts.connect = this._timeouts.read = this._timeouts.write = milliseconds;
+ }
+};
+
+/**
+* Set the three different (connect, read, write) timeouts to be used when requesting
+* @param {Object} timeouts
+*/
+AlgoliaSearchCore.prototype.setTimeouts = function(timeouts) {
+ this._timeouts = timeouts;
+};
+
+/**
+* Get the three different (connect, read, write) timeouts to be used when requesting
+* @param {Object} timeouts
+*/
+AlgoliaSearchCore.prototype.getTimeouts = function() {
+ return this._timeouts;
+};
+
+AlgoliaSearchCore.prototype._getAppIdData = function() {
+ var data = store.get(this.applicationID);
+ if (data !== null) this._cacheAppIdData(data);
+ return data;
+};
+
+AlgoliaSearchCore.prototype._setAppIdData = function(data) {
+ data.lastChange = (new Date()).getTime();
+ this._cacheAppIdData(data);
+ return store.set(this.applicationID, data);
+};
+
+AlgoliaSearchCore.prototype._checkAppIdData = function() {
+ var data = this._getAppIdData();
+ var now = (new Date()).getTime();
+ if (data === null || now - data.lastChange > RESET_APP_DATA_TIMER) {
+ return this._resetInitialAppIdData(data);
+ }
+
+ return data;
+};
+
+AlgoliaSearchCore.prototype._resetInitialAppIdData = function(data) {
+ var newData = data || {};
+ newData.hostIndexes = {read: 0, write: 0};
+ newData.timeoutMultiplier = 1;
+ newData.shuffleResult = newData.shuffleResult || shuffle([1, 2, 3]);
+ return this._setAppIdData(newData);
+};
+
+AlgoliaSearchCore.prototype._cacheAppIdData = function(data) {
+ this._hostIndexes = data.hostIndexes;
+ this._timeoutMultiplier = data.timeoutMultiplier;
+ this._shuffleResult = data.shuffleResult;
+};
+
+AlgoliaSearchCore.prototype._partialAppIdDataUpdate = function(newData) {
+ var foreach = require(5);
+ var currentData = this._getAppIdData();
+ foreach(newData, function(value, key) {
+ currentData[key] = value;
+ });
+
+ return this._setAppIdData(currentData);
+};
+
+AlgoliaSearchCore.prototype._getHostByType = function(hostType) {
+ return this.hosts[hostType][this._getHostIndexByType(hostType)];
+};
+
+AlgoliaSearchCore.prototype._getTimeoutMultiplier = function() {
+ return this._timeoutMultiplier;
+};
+
+AlgoliaSearchCore.prototype._getHostIndexByType = function(hostType) {
+ return this._hostIndexes[hostType];
+};
+
+AlgoliaSearchCore.prototype._setHostIndexByType = function(hostIndex, hostType) {
+ var clone = require(26);
+ var newHostIndexes = clone(this._hostIndexes);
+ newHostIndexes[hostType] = hostIndex;
+ this._partialAppIdDataUpdate({hostIndexes: newHostIndexes});
+ return hostIndex;
+};
+
+AlgoliaSearchCore.prototype._incrementHostIndex = function(hostType) {
+ return this._setHostIndexByType(
+ (this._getHostIndexByType(hostType) + 1) % this.hosts[hostType].length, hostType
+ );
+};
+
+AlgoliaSearchCore.prototype._incrementTimeoutMultipler = function() {
+ var timeoutMultiplier = Math.max(this._timeoutMultiplier + 1, 4);
+ return this._partialAppIdDataUpdate({timeoutMultiplier: timeoutMultiplier});
+};
+
+AlgoliaSearchCore.prototype._getTimeoutsForRequest = function(hostType) {
+ return {
+ connect: this._timeouts.connect * this._timeoutMultiplier,
+ complete: this._timeouts[hostType] * this._timeoutMultiplier
+ };
+};
+
+function prepareHost(protocol) {
+ return function prepare(host) {
+ return protocol + '//' + host.toLowerCase();
+ };
+}
+
+// Prototype.js < 1.7, a widely used library, defines a weird
+// Array.prototype.toJSON function that will fail to stringify our content
+// appropriately
+// refs:
+// - https://groups.google.com/forum/#!topic/prototype-core/E-SAVvV_V9Q
+// - https://github.com/sstephenson/prototype/commit/038a2985a70593c1a86c230fadbdfe2e4898a48c
+// - http://stackoverflow.com/a/3148441/147079
+function safeJSONStringify(obj) {
+ /* eslint no-extend-native:0 */
+
+ if (Array.prototype.toJSON === undefined) {
+ return JSON.stringify(obj);
+ }
+
+ var toJSON = Array.prototype.toJSON;
+ delete Array.prototype.toJSON;
+ var out = JSON.stringify(obj);
+ Array.prototype.toJSON = toJSON;
+
+ return out;
+}
+
+function shuffle(array) {
+ var currentIndex = array.length;
+ var temporaryValue;
+ var randomIndex;
+
+ // While there remain elements to shuffle...
+ while (currentIndex !== 0) {
+ // Pick a remaining element...
+ randomIndex = Math.floor(Math.random() * currentIndex);
+ currentIndex -= 1;
+
+ // And swap it with the current element.
+ temporaryValue = array[currentIndex];
+ array[currentIndex] = array[randomIndex];
+ array[randomIndex] = temporaryValue;
+ }
+
+ return array;
+}
+
+function removeCredentials(headers) {
+ var newHeaders = {};
+
+ for (var headerName in headers) {
+ if (Object.prototype.hasOwnProperty.call(headers, headerName)) {
+ var value;
+
+ if (headerName === 'x-algolia-api-key' || headerName === 'x-algolia-application-id') {
+ value = '**hidden for security purposes**';
+ } else {
+ value = headers[headerName];
+ }
+
+ newHeaders[headerName] = value;
+ }
+ }
+
+ return newHeaders;
+}
+
+}).call(this,require(12))
+},{"1":1,"12":12,"20":20,"26":26,"30":30,"31":31,"32":32,"34":34,"36":36,"5":5,"8":8}],18:[function(require,module,exports){
+var inherits = require(7);
+var IndexCore = require(20);
+var deprecate = require(28);
+var deprecatedMessage = require(29);
+var exitPromise = require(31);
+var errors = require(30);
+
+var deprecateForwardToSlaves = deprecate(
+ function() {},
+ deprecatedMessage('forwardToSlaves', 'forwardToReplicas')
+);
+
+module.exports = Index;
+
+function Index() {
+ IndexCore.apply(this, arguments);
+}
+
+inherits(Index, IndexCore);
+
+/*
+* Add an object in this index
+*
+* @param content contains the javascript object to add inside the index
+* @param objectID (optional) an objectID you want to attribute to this object
+* (if the attribute already exist the old object will be overwrite)
+* @param callback (optional) the result callback called with two arguments:
+* error: null or Error('message')
+* content: the server answer that contains 3 elements: createAt, taskId and objectID
+*/
+Index.prototype.addObject = function(content, objectID, callback) {
+ var indexObj = this;
+
+ if (arguments.length === 1 || typeof objectID === 'function') {
+ callback = objectID;
+ objectID = undefined;
+ }
+
+ return this.as._jsonRequest({
+ method: objectID !== undefined ?
+ 'PUT' : // update or create
+ 'POST', // create (API generates an objectID)
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + // create
+ (objectID !== undefined ? '/' + encodeURIComponent(objectID) : ''), // update or create
+ body: content,
+ hostType: 'write',
+ callback: callback
+ });
+};
+
+/*
+* Add several objects
+*
+* @param objects contains an array of objects to add
+* @param callback (optional) the result callback called with two arguments:
+* error: null or Error('message')
+* content: the server answer that updateAt and taskID
+*/
+Index.prototype.addObjects = function(objects, callback) {
+ var isArray = require(8);
+ var usage = 'Usage: index.addObjects(arrayOfObjects[, callback])';
+
+ if (!isArray(objects)) {
+ throw new Error(usage);
+ }
+
+ var indexObj = this;
+ var postObj = {
+ requests: []
+ };
+ for (var i = 0; i < objects.length; ++i) {
+ var request = {
+ action: 'addObject',
+ body: objects[i]
+ };
+ postObj.requests.push(request);
+ }
+ return this.as._jsonRequest({
+ method: 'POST',
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
+ body: postObj,
+ hostType: 'write',
+ callback: callback
+ });
+};
+
+/*
+* Update partially an object (only update attributes passed in argument)
+*
+* @param partialObject contains the javascript attributes to override, the
+* object must contains an objectID attribute
+* @param createIfNotExists (optional) if false, avoid an automatic creation of the object
+* @param callback (optional) the result callback called with two arguments:
+* error: null or Error('message')
+* content: the server answer that contains 3 elements: createAt, taskId and objectID
+*/
+Index.prototype.partialUpdateObject = function(partialObject, createIfNotExists, callback) {
+ if (arguments.length === 1 || typeof createIfNotExists === 'function') {
+ callback = createIfNotExists;
+ createIfNotExists = undefined;
+ }
+
+ var indexObj = this;
+ var url = '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(partialObject.objectID) + '/partial';
+ if (createIfNotExists === false) {
+ url += '?createIfNotExists=false';
+ }
+
+ return this.as._jsonRequest({
+ method: 'POST',
+ url: url,
+ body: partialObject,
+ hostType: 'write',
+ callback: callback
+ });
+};
+
+/*
+* Partially Override the content of several objects
+*
+* @param objects contains an array of objects to update (each object must contains a objectID attribute)
+* @param callback (optional) the result callback called with two arguments:
+* error: null or Error('message')
+* content: the server answer that updateAt and taskID
+*/
+Index.prototype.partialUpdateObjects = function(objects, createIfNotExists, callback) {
+ if (arguments.length === 1 || typeof createIfNotExists === 'function') {
+ callback = createIfNotExists;
+ createIfNotExists = true;
+ }
+
+ var isArray = require(8);
+ var usage = 'Usage: index.partialUpdateObjects(arrayOfObjects[, callback])';
+
+ if (!isArray(objects)) {
+ throw new Error(usage);
+ }
+
+ var indexObj = this;
+ var postObj = {
+ requests: []
+ };
+ for (var i = 0; i < objects.length; ++i) {
+ var request = {
+ action: createIfNotExists === true ? 'partialUpdateObject' : 'partialUpdateObjectNoCreate',
+ objectID: objects[i].objectID,
+ body: objects[i]
+ };
+ postObj.requests.push(request);
+ }
+ return this.as._jsonRequest({
+ method: 'POST',
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
+ body: postObj,
+ hostType: 'write',
+ callback: callback
+ });
+};
+
+/*
+* Override the content of object
+*
+* @param object contains the javascript object to save, the object must contains an objectID attribute
+* @param callback (optional) the result callback called with two arguments:
+* error: null or Error('message')
+* content: the server answer that updateAt and taskID
+*/
+Index.prototype.saveObject = function(object, callback) {
+ var indexObj = this;
+ return this.as._jsonRequest({
+ method: 'PUT',
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(object.objectID),
+ body: object,
+ hostType: 'write',
+ callback: callback
+ });
+};
+
+/*
+* Override the content of several objects
+*
+* @param objects contains an array of objects to update (each object must contains a objectID attribute)
+* @param callback (optional) the result callback called with two arguments:
+* error: null or Error('message')
+* content: the server answer that updateAt and taskID
+*/
+Index.prototype.saveObjects = function(objects, callback) {
+ var isArray = require(8);
+ var usage = 'Usage: index.saveObjects(arrayOfObjects[, callback])';
+
+ if (!isArray(objects)) {
+ throw new Error(usage);
+ }
+
+ var indexObj = this;
+ var postObj = {
+ requests: []
+ };
+ for (var i = 0; i < objects.length; ++i) {
+ var request = {
+ action: 'updateObject',
+ objectID: objects[i].objectID,
+ body: objects[i]
+ };
+ postObj.requests.push(request);
+ }
+ return this.as._jsonRequest({
+ method: 'POST',
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
+ body: postObj,
+ hostType: 'write',
+ callback: callback
+ });
+};
+
+/*
+* Delete an object from the index
+*
+* @param objectID the unique identifier of object to delete
+* @param callback (optional) the result callback called with two arguments:
+* error: null or Error('message')
+* content: the server answer that contains 3 elements: createAt, taskId and objectID
+*/
+Index.prototype.deleteObject = function(objectID, callback) {
+ if (typeof objectID === 'function' || typeof objectID !== 'string' && typeof objectID !== 'number') {
+ var err = new errors.AlgoliaSearchError(
+ objectID && typeof objectID !== 'function'
+ ? 'ObjectID must be a string'
+ : 'Cannot delete an object without an objectID'
+ );
+ callback = objectID;
+ if (typeof callback === 'function') {
+ return callback(err);
+ }
+
+ return this.as._promise.reject(err);
+ }
+
+ var indexObj = this;
+ return this.as._jsonRequest({
+ method: 'DELETE',
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(objectID),
+ hostType: 'write',
+ callback: callback
+ });
+};
+
+/*
+* Delete several objects from an index
+*
+* @param objectIDs contains an array of objectID to delete
+* @param callback (optional) the result callback called with two arguments:
+* error: null or Error('message')
+* content: the server answer that contains 3 elements: createAt, taskId and objectID
+*/
+Index.prototype.deleteObjects = function(objectIDs, callback) {
+ var isArray = require(8);
+ var map = require(32);
+
+ var usage = 'Usage: index.deleteObjects(arrayOfObjectIDs[, callback])';
+
+ if (!isArray(objectIDs)) {
+ throw new Error(usage);
+ }
+
+ var indexObj = this;
+ var postObj = {
+ requests: map(objectIDs, function prepareRequest(objectID) {
+ return {
+ action: 'deleteObject',
+ objectID: objectID,
+ body: {
+ objectID: objectID
+ }
+ };
+ })
+ };
+
+ return this.as._jsonRequest({
+ method: 'POST',
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/batch',
+ body: postObj,
+ hostType: 'write',
+ callback: callback
+ });
+};
+
+/*
+* Delete all objects matching a query
+*
+* @param query the query string
+* @param params the optional query parameters
+* @param callback (optional) the result callback called with one argument
+* error: null or Error('message')
+* @deprecated see index.deleteBy
+*/
+Index.prototype.deleteByQuery = deprecate(function(query, params, callback) {
+ var clone = require(26);
+ var map = require(32);
+
+ var indexObj = this;
+ var client = indexObj.as;
+
+ if (arguments.length === 1 || typeof params === 'function') {
+ callback = params;
+ params = {};
+ } else {
+ params = clone(params);
+ }
+
+ params.attributesToRetrieve = 'objectID';
+ params.hitsPerPage = 1000;
+ params.distinct = false;
+
+ // when deleting, we should never use cache to get the
+ // search results
+ this.clearCache();
+
+ // there's a problem in how we use the promise chain,
+ // see how waitTask is done
+ var promise = this
+ .search(query, params)
+ .then(stopOrDelete);
+
+ function stopOrDelete(searchContent) {
+ // stop here
+ if (searchContent.nbHits === 0) {
+ // return indexObj.as._request.resolve();
+ return searchContent;
+ }
+
+ // continue and do a recursive call
+ var objectIDs = map(searchContent.hits, function getObjectID(object) {
+ return object.objectID;
+ });
+
+ return indexObj
+ .deleteObjects(objectIDs)
+ .then(waitTask)
+ .then(doDeleteByQuery);
+ }
+
+ function waitTask(deleteObjectsContent) {
+ return indexObj.waitTask(deleteObjectsContent.taskID);
+ }
+
+ function doDeleteByQuery() {
+ return indexObj.deleteByQuery(query, params);
+ }
+
+ if (!callback) {
+ return promise;
+ }
+
+ promise.then(success, failure);
+
+ function success() {
+ exitPromise(function exit() {
+ callback(null);
+ }, client._setTimeout || setTimeout);
+ }
+
+ function failure(err) {
+ exitPromise(function exit() {
+ callback(err);
+ }, client._setTimeout || setTimeout);
+ }
+}, deprecatedMessage('index.deleteByQuery()', 'index.deleteBy()'));
+
+/**
+* Delete all objects matching a query
+*
+* the query parameters that can be used are:
+* - filters (numeric, facet, tag)
+* - geo
+*
+* you can not send an empty query or filters
+*
+* @param params the optional query parameters
+* @param callback (optional) the result callback called with one argument
+* error: null or Error('message')
+*/
+Index.prototype.deleteBy = function(params, callback) {
+ var indexObj = this;
+ return this.as._jsonRequest({
+ method: 'POST',
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/deleteByQuery',
+ body: {params: indexObj.as._getSearchParams(params, '')},
+ hostType: 'write',
+ callback: callback
+ });
+};
+
+/*
+* Browse all content from an index using events. Basically this will do
+* .browse() -> .browseFrom -> .browseFrom -> .. until all the results are returned
+*
+* @param {string} query - The full text query
+* @param {Object} [queryParameters] - Any search query parameter
+* @return {EventEmitter}
+* @example
+* var browser = index.browseAll('cool songs', {
+* tagFilters: 'public,comments',
+* hitsPerPage: 500
+* });
+*
+* browser.on('result', function resultCallback(content) {
+* console.log(content.hits);
+* });
+*
+* // if any error occurs, you get it
+* browser.on('error', function(err) {
+* throw err;
+* });
+*
+* // when you have browsed the whole index, you get this event
+* browser.on('end', function() {
+* console.log('finished');
+* });
+*
+* // at any point if you want to stop the browsing process, you can stop it manually
+* // otherwise it will go on and on
+* browser.stop();
+*
+* @see {@link https://www.algolia.com/doc/rest_api#Browse|Algolia REST API Documentation}
+*/
+Index.prototype.browseAll = function(query, queryParameters) {
+ if (typeof query === 'object') {
+ queryParameters = query;
+ query = undefined;
+ }
+
+ var merge = require(33);
+
+ var IndexBrowser = require(19);
+
+ var browser = new IndexBrowser();
+ var client = this.as;
+ var index = this;
+ var params = client._getSearchParams(
+ merge({}, queryParameters || {}, {
+ query: query
+ }), ''
+ );
+
+ // start browsing
+ browseLoop();
+
+ function browseLoop(cursor) {
+ if (browser._stopped) {
+ return;
+ }
+
+ var body;
+
+ if (cursor !== undefined) {
+ body = {
+ cursor: cursor
+ };
+ } else {
+ body = {
+ params: params
+ };
+ }
+
+ client._jsonRequest({
+ method: 'POST',
+ url: '/1/indexes/' + encodeURIComponent(index.indexName) + '/browse',
+ hostType: 'read',
+ body: body,
+ callback: browseCallback
+ });
+ }
+
+ function browseCallback(err, content) {
+ if (browser._stopped) {
+ return;
+ }
+
+ if (err) {
+ browser._error(err);
+ return;
+ }
+
+ browser._result(content);
+
+ // no cursor means we are finished browsing
+ if (content.cursor === undefined) {
+ browser._end();
+ return;
+ }
+
+ browseLoop(content.cursor);
+ }
+
+ return browser;
+};
+
+/*
+* Get a Typeahead.js adapter
+* @param searchParams contains an object with query parameters (see search for details)
+*/
+Index.prototype.ttAdapter = deprecate(function(params) {
+ var self = this;
+ return function ttAdapter(query, syncCb, asyncCb) {
+ var cb;
+
+ if (typeof asyncCb === 'function') {
+ // typeahead 0.11
+ cb = asyncCb;
+ } else {
+ // pre typeahead 0.11
+ cb = syncCb;
+ }
+
+ self.search(query, params, function searchDone(err, content) {
+ if (err) {
+ cb(err);
+ return;
+ }
+
+ cb(content.hits);
+ });
+ };
+},
+'ttAdapter is not necessary anymore and will be removed in the next version,\n' +
+'have a look at autocomplete.js (https://github.com/algolia/autocomplete.js)');
+
+/*
+* Wait the publication of a task on the server.
+* All server task are asynchronous and you can check with this method that the task is published.
+*
+* @param taskID the id of the task returned by server
+* @param callback the result callback with with two arguments:
+* error: null or Error('message')
+* content: the server answer that contains the list of results
+*/
+Index.prototype.waitTask = function(taskID, callback) {
+ // wait minimum 100ms before retrying
+ var baseDelay = 100;
+ // wait maximum 5s before retrying
+ var maxDelay = 5000;
+ var loop = 0;
+
+ // waitTask() must be handled differently from other methods,
+ // it's a recursive method using a timeout
+ var indexObj = this;
+ var client = indexObj.as;
+
+ var promise = retryLoop();
+
+ function retryLoop() {
+ return client._jsonRequest({
+ method: 'GET',
+ hostType: 'read',
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/task/' + taskID
+ }).then(function success(content) {
+ loop++;
+ var delay = baseDelay * loop * loop;
+ if (delay > maxDelay) {
+ delay = maxDelay;
+ }
+
+ if (content.status !== 'published') {
+ return client._promise.delay(delay).then(retryLoop);
+ }
+
+ return content;
+ });
+ }
+
+ if (!callback) {
+ return promise;
+ }
+
+ promise.then(successCb, failureCb);
+
+ function successCb(content) {
+ exitPromise(function exit() {
+ callback(null, content);
+ }, client._setTimeout || setTimeout);
+ }
+
+ function failureCb(err) {
+ exitPromise(function exit() {
+ callback(err);
+ }, client._setTimeout || setTimeout);
+ }
+};
+
+/*
+* This function deletes the index content. Settings and index specific API keys are kept untouched.
+*
+* @param callback (optional) the result callback called with two arguments
+* error: null or Error('message')
+* content: the settings object or the error message if a failure occurred
+*/
+Index.prototype.clearIndex = function(callback) {
+ var indexObj = this;
+ return this.as._jsonRequest({
+ method: 'POST',
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/clear',
+ hostType: 'write',
+ callback: callback
+ });
+};
+
+/*
+* Get settings of this index
+*
+* @param opts an object of options to add
+* @param opts.advanced get more settings like nbShards (useful for Enterprise)
+* @param callback (optional) the result callback called with two arguments
+* error: null or Error('message')
+* content: the settings object or the error message if a failure occurred
+*/
+Index.prototype.getSettings = function(opts, callback) {
+ if (arguments.length === 1 && typeof opts === 'function') {
+ callback = opts;
+ opts = {};
+ }
+ opts = opts || {};
+
+ var indexName = encodeURIComponent(this.indexName);
+ return this.as._jsonRequest({
+ method: 'GET',
+ url:
+ '/1/indexes/' +
+ indexName +
+ '/settings?getVersion=2' +
+ (opts.advanced ? '&advanced=' + opts.advanced : ''),
+ hostType: 'read',
+ callback: callback
+ });
+};
+
+Index.prototype.searchSynonyms = function(params, callback) {
+ if (typeof params === 'function') {
+ callback = params;
+ params = {};
+ } else if (params === undefined) {
+ params = {};
+ }
+
+ return this.as._jsonRequest({
+ method: 'POST',
+ url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/synonyms/search',
+ body: params,
+ hostType: 'read',
+ callback: callback
+ });
+};
+
+function exportData(method, _hitsPerPage, callback) {
+ function search(page, _previous) {
+ var options = {
+ page: page || 0,
+ hitsPerPage: _hitsPerPage || 100
+ };
+ var previous = _previous || [];
+
+ return method(options).then(function(result) {
+ var hits = result.hits;
+ var nbHits = result.nbHits;
+ var current = hits.map(function(s) {
+ delete s._highlightResult;
+ return s;
+ });
+ var synonyms = previous.concat(current);
+ if (synonyms.length < nbHits) {
+ return search(options.page + 1, synonyms);
+ }
+ return synonyms;
+ });
+ }
+ return search().then(function(data) {
+ if (typeof callback === 'function') {
+ callback(data);
+ return undefined;
+ }
+ return data;
+ });
+}
+
+/**
+ * Retrieve all the synonyms in an index
+ * @param [number=100] hitsPerPage The amount of synonyms to retrieve per batch
+ * @param [function] callback will be called after all synonyms are retrieved
+ */
+Index.prototype.exportSynonyms = function(hitsPerPage, callback) {
+ return exportData(this.searchSynonyms.bind(this), hitsPerPage, callback);
+};
+
+Index.prototype.saveSynonym = function(synonym, opts, callback) {
+ if (typeof opts === 'function') {
+ callback = opts;
+ opts = {};
+ } else if (opts === undefined) {
+ opts = {};
+ }
+
+ if (opts.forwardToSlaves !== undefined) deprecateForwardToSlaves();
+ var forwardToReplicas = (opts.forwardToSlaves || opts.forwardToReplicas) ? 'true' : 'false';
+
+ return this.as._jsonRequest({
+ method: 'PUT',
+ url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/synonyms/' + encodeURIComponent(synonym.objectID) +
+ '?forwardToReplicas=' + forwardToReplicas,
+ body: synonym,
+ hostType: 'write',
+ callback: callback
+ });
+};
+
+Index.prototype.getSynonym = function(objectID, callback) {
+ return this.as._jsonRequest({
+ method: 'GET',
+ url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/synonyms/' + encodeURIComponent(objectID),
+ hostType: 'read',
+ callback: callback
+ });
+};
+
+Index.prototype.deleteSynonym = function(objectID, opts, callback) {
+ if (typeof opts === 'function') {
+ callback = opts;
+ opts = {};
+ } else if (opts === undefined) {
+ opts = {};
+ }
+
+ if (opts.forwardToSlaves !== undefined) deprecateForwardToSlaves();
+ var forwardToReplicas = (opts.forwardToSlaves || opts.forwardToReplicas) ? 'true' : 'false';
+
+ return this.as._jsonRequest({
+ method: 'DELETE',
+ url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/synonyms/' + encodeURIComponent(objectID) +
+ '?forwardToReplicas=' + forwardToReplicas,
+ hostType: 'write',
+ callback: callback
+ });
+};
+
+Index.prototype.clearSynonyms = function(opts, callback) {
+ if (typeof opts === 'function') {
+ callback = opts;
+ opts = {};
+ } else if (opts === undefined) {
+ opts = {};
+ }
+
+ if (opts.forwardToSlaves !== undefined) deprecateForwardToSlaves();
+ var forwardToReplicas = (opts.forwardToSlaves || opts.forwardToReplicas) ? 'true' : 'false';
+
+ return this.as._jsonRequest({
+ method: 'POST',
+ url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/synonyms/clear' +
+ '?forwardToReplicas=' + forwardToReplicas,
+ hostType: 'write',
+ callback: callback
+ });
+};
+
+Index.prototype.batchSynonyms = function(synonyms, opts, callback) {
+ if (typeof opts === 'function') {
+ callback = opts;
+ opts = {};
+ } else if (opts === undefined) {
+ opts = {};
+ }
+
+ if (opts.forwardToSlaves !== undefined) deprecateForwardToSlaves();
+ var forwardToReplicas = (opts.forwardToSlaves || opts.forwardToReplicas) ? 'true' : 'false';
+
+ return this.as._jsonRequest({
+ method: 'POST',
+ url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/synonyms/batch' +
+ '?forwardToReplicas=' + forwardToReplicas +
+ '&replaceExistingSynonyms=' + (opts.replaceExistingSynonyms ? 'true' : 'false'),
+ hostType: 'write',
+ body: synonyms,
+ callback: callback
+ });
+};
+
+Index.prototype.searchRules = function(params, callback) {
+ if (typeof params === 'function') {
+ callback = params;
+ params = {};
+ } else if (params === undefined) {
+ params = {};
+ }
+
+ return this.as._jsonRequest({
+ method: 'POST',
+ url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/rules/search',
+ body: params,
+ hostType: 'read',
+ callback: callback
+ });
+};
+/**
+ * Retrieve all the query rules in an index
+ * @param [number=100] hitsPerPage The amount of query rules to retrieve per batch
+ * @param [function] callback will be called after all query rules are retrieved
+ * error: null or Error('message')
+ */
+Index.prototype.exportRules = function(hitsPerPage, callback) {
+ return exportData(this.searchRules.bind(this), hitsPerPage, callback);
+};
+
+Index.prototype.saveRule = function(rule, opts, callback) {
+ if (typeof opts === 'function') {
+ callback = opts;
+ opts = {};
+ } else if (opts === undefined) {
+ opts = {};
+ }
+
+ if (!rule.objectID) {
+ throw new errors.AlgoliaSearchError('Missing or empty objectID field for rule');
+ }
+
+ var forwardToReplicas = opts.forwardToReplicas === true ? 'true' : 'false';
+
+ return this.as._jsonRequest({
+ method: 'PUT',
+ url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/rules/' + encodeURIComponent(rule.objectID) +
+ '?forwardToReplicas=' + forwardToReplicas,
+ body: rule,
+ hostType: 'write',
+ callback: callback
+ });
+};
+
+Index.prototype.getRule = function(objectID, callback) {
+ return this.as._jsonRequest({
+ method: 'GET',
+ url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/rules/' + encodeURIComponent(objectID),
+ hostType: 'read',
+ callback: callback
+ });
+};
+
+Index.prototype.deleteRule = function(objectID, opts, callback) {
+ if (typeof opts === 'function') {
+ callback = opts;
+ opts = {};
+ } else if (opts === undefined) {
+ opts = {};
+ }
+
+ var forwardToReplicas = opts.forwardToReplicas === true ? 'true' : 'false';
+
+ return this.as._jsonRequest({
+ method: 'DELETE',
+ url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/rules/' + encodeURIComponent(objectID) +
+ '?forwardToReplicas=' + forwardToReplicas,
+ hostType: 'write',
+ callback: callback
+ });
+};
+
+Index.prototype.clearRules = function(opts, callback) {
+ if (typeof opts === 'function') {
+ callback = opts;
+ opts = {};
+ } else if (opts === undefined) {
+ opts = {};
+ }
+
+ var forwardToReplicas = opts.forwardToReplicas === true ? 'true' : 'false';
+
+ return this.as._jsonRequest({
+ method: 'POST',
+ url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/rules/clear' +
+ '?forwardToReplicas=' + forwardToReplicas,
+ hostType: 'write',
+ callback: callback
+ });
+};
+
+Index.prototype.batchRules = function(rules, opts, callback) {
+ if (typeof opts === 'function') {
+ callback = opts;
+ opts = {};
+ } else if (opts === undefined) {
+ opts = {};
+ }
+
+ var forwardToReplicas = opts.forwardToReplicas === true ? 'true' : 'false';
+
+ return this.as._jsonRequest({
+ method: 'POST',
+ url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/rules/batch' +
+ '?forwardToReplicas=' + forwardToReplicas +
+ '&clearExistingRules=' + (opts.clearExistingRules === true ? 'true' : 'false'),
+ hostType: 'write',
+ body: rules,
+ callback: callback
+ });
+};
+
+Index.prototype.exists = function(callback) {
+ var result = this.getSettings().then(function() {
+ return true;
+ }).catch(function(err) {
+ if (err instanceof errors.AlgoliaSearchError && err.statusCode === 404) {
+ return false;
+ }
+
+ throw err;
+ });
+
+ if (typeof callback !== 'function') {
+ return result;
+ }
+
+ result.then(function(res) {
+ callback(null, res);
+ }).catch(function(err) {
+ callback(err);
+ });
+};
+
+Index.prototype.findObject = function(findCallback, requestOptions, callback) {
+ requestOptions = requestOptions === undefined ? {} : requestOptions;
+ var paginate = requestOptions.paginate !== undefined ? requestOptions.paginate : true;
+ var query = requestOptions.query !== undefined ? requestOptions.query : '';
+
+ var that = this;
+ var page = 0;
+
+ var paginateLoop = function() {
+ requestOptions.page = page;
+
+ return that.search(query, requestOptions).then(function(result) {
+ var hits = result.hits;
+
+ for (var position = 0; position < hits.length; position++) {
+ var hit = hits[position];
+ if (findCallback(hit)) {
+ return {
+ object: hit,
+ position: position,
+ page: page
+ };
+ }
+ }
+
+ page += 1;
+
+ // paginate if option was set and has next page
+ if (!paginate || page >= result.nbPages) {
+ throw new errors.ObjectNotFound('Object not found');
+ }
+
+ return paginateLoop();
+ });
+ };
+
+ var promise = paginateLoop(page);
+
+ if (callback === undefined) {
+ return promise;
+ }
+
+ promise
+ .then(function(res) {
+ callback(null, res);
+ })
+ .catch(function(err) {
+ callback(err);
+ });
+};
+
+Index.prototype.getObjectPosition = function(result, objectID) {
+ var hits = result.hits;
+
+ for (var position = 0; position < hits.length; position++) {
+ if (hits[position].objectID === objectID) {
+ return position;
+ }
+ }
+
+ return -1;
+};
+
+/*
+* Set settings for this index
+*
+* @param settings the settings object that can contains :
+* - minWordSizefor1Typo: (integer) the minimum number of characters to accept one typo (default = 3).
+* - minWordSizefor2Typos: (integer) the minimum number of characters to accept two typos (default = 7).
+* - hitsPerPage: (integer) the number of hits per page (default = 10).
+* - attributesToRetrieve: (array of strings) default list of attributes to retrieve in objects.
+* If set to null, all attributes are retrieved.
+* - attributesToHighlight: (array of strings) default list of attributes to highlight.
+* If set to null, all indexed attributes are highlighted.
+* - attributesToSnippet**: (array of strings) default list of attributes to snippet alongside the number
+* of words to return (syntax is attributeName:nbWords).
+* By default no snippet is computed. If set to null, no snippet is computed.
+* - attributesToIndex: (array of strings) the list of fields you want to index.
+* If set to null, all textual and numerical attributes of your objects are indexed,
+* but you should update it to get optimal results.
+* This parameter has two important uses:
+* - Limit the attributes to index: For example if you store a binary image in base64,
+* you want to store it and be able to
+* retrieve it but you don't want to search in the base64 string.
+* - Control part of the ranking*: (see the ranking parameter for full explanation)
+* Matches in attributes at the beginning of
+* the list will be considered more important than matches in attributes further down the list.
+* In one attribute, matching text at the beginning of the attribute will be
+* considered more important than text after, you can disable
+* this behavior if you add your attribute inside `unordered(AttributeName)`,
+* for example attributesToIndex: ["title", "unordered(text)"].
+* - attributesForFaceting: (array of strings) The list of fields you want to use for faceting.
+* All strings in the attribute selected for faceting are extracted and added as a facet.
+* If set to null, no attribute is used for faceting.
+* - attributeForDistinct: (string) The attribute name used for the Distinct feature.
+* This feature is similar to the SQL "distinct" keyword: when enabled
+* in query with the distinct=1 parameter, all hits containing a duplicate
+* value for this attribute are removed from results.
+* For example, if the chosen attribute is show_name and several hits have
+* the same value for show_name, then only the best one is kept and others are removed.
+* - ranking: (array of strings) controls the way results are sorted.
+* We have six available criteria:
+* - typo: sort according to number of typos,
+* - geo: sort according to decreassing distance when performing a geo-location based search,
+* - proximity: sort according to the proximity of query words in hits,
+* - attribute: sort according to the order of attributes defined by attributesToIndex,
+* - exact:
+* - if the user query contains one word: sort objects having an attribute
+* that is exactly the query word before others.
+* For example if you search for the "V" TV show, you want to find it
+* with the "V" query and avoid to have all popular TV
+* show starting by the v letter before it.
+* - if the user query contains multiple words: sort according to the
+* number of words that matched exactly (and not as a prefix).
+* - custom: sort according to a user defined formula set in **customRanking** attribute.
+* The standard order is ["typo", "geo", "proximity", "attribute", "exact", "custom"]
+* - customRanking: (array of strings) lets you specify part of the ranking.
+* The syntax of this condition is an array of strings containing attributes
+* prefixed by asc (ascending order) or desc (descending order) operator.
+* For example `"customRanking" => ["desc(population)", "asc(name)"]`
+* - queryType: Select how the query words are interpreted, it can be one of the following value:
+* - prefixAll: all query words are interpreted as prefixes,
+* - prefixLast: only the last word is interpreted as a prefix (default behavior),
+* - prefixNone: no query word is interpreted as a prefix. This option is not recommended.
+* - highlightPreTag: (string) Specify the string that is inserted before
+* the highlighted parts in the query result (default to "").
+* - highlightPostTag: (string) Specify the string that is inserted after
+* the highlighted parts in the query result (default to " ").
+* - optionalWords: (array of strings) Specify a list of words that should
+* be considered as optional when found in the query.
+* @param callback (optional) the result callback called with two arguments
+* error: null or Error('message')
+* content: the server answer or the error message if a failure occurred
+*/
+Index.prototype.setSettings = function(settings, opts, callback) {
+ if (arguments.length === 1 || typeof opts === 'function') {
+ callback = opts;
+ opts = {};
+ }
+
+ if (opts.forwardToSlaves !== undefined) deprecateForwardToSlaves();
+ var forwardToReplicas = (opts.forwardToSlaves || opts.forwardToReplicas) ? 'true' : 'false';
+
+ var indexObj = this;
+ return this.as._jsonRequest({
+ method: 'PUT',
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/settings?forwardToReplicas='
+ + forwardToReplicas,
+ hostType: 'write',
+ body: settings,
+ callback: callback
+ });
+};
+
+/*
+* @deprecated see client.listApiKeys()
+*/
+Index.prototype.listUserKeys = deprecate(function(callback) {
+ return this.listApiKeys(callback);
+}, deprecatedMessage('index.listUserKeys()', 'client.listApiKeys()'));
+
+/*
+* List all existing API keys to this index
+*
+* @param callback the result callback called with two arguments
+* error: null or Error('message')
+* content: the server answer with API keys belonging to the index
+*
+* @deprecated see client.listApiKeys()
+*/
+Index.prototype.listApiKeys = deprecate(function(callback) {
+ var indexObj = this;
+ return this.as._jsonRequest({
+ method: 'GET',
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys',
+ hostType: 'read',
+ callback: callback
+ });
+}, deprecatedMessage('index.listApiKeys()', 'client.listApiKeys()'));
+
+/*
+* @deprecated see client.getApiKey()
+*/
+Index.prototype.getUserKeyACL = deprecate(function(key, callback) {
+ return this.getApiKey(key, callback);
+}, deprecatedMessage('index.getUserKeyACL()', 'client.getApiKey()'));
+
+
+/*
+* Get an API key from this index
+*
+* @param key
+* @param callback the result callback called with two arguments
+* error: null or Error('message')
+* content: the server answer with the right API key
+*
+* @deprecated see client.getApiKey()
+*/
+Index.prototype.getApiKey = deprecate(function(key, callback) {
+ var indexObj = this;
+ return this.as._jsonRequest({
+ method: 'GET',
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys/' + key,
+ hostType: 'read',
+ callback: callback
+ });
+}, deprecatedMessage('index.getApiKey()', 'client.getApiKey()'));
+
+/*
+* @deprecated see client.deleteApiKey()
+*/
+Index.prototype.deleteUserKey = deprecate(function(key, callback) {
+ return this.deleteApiKey(key, callback);
+}, deprecatedMessage('index.deleteUserKey()', 'client.deleteApiKey()'));
+
+/*
+* Delete an existing API key associated to this index
+*
+* @param key
+* @param callback the result callback called with two arguments
+* error: null or Error('message')
+* content: the server answer with the deletion date
+*
+* @deprecated see client.deleteApiKey()
+*/
+Index.prototype.deleteApiKey = deprecate(function(key, callback) {
+ var indexObj = this;
+ return this.as._jsonRequest({
+ method: 'DELETE',
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/keys/' + key,
+ hostType: 'write',
+ callback: callback
+ });
+}, deprecatedMessage('index.deleteApiKey()', 'client.deleteApiKey()'));
+
+/*
+* @deprecated see client.addApiKey()
+*/
+Index.prototype.addUserKey = deprecate(function(acls, params, callback) {
+ return this.addApiKey(acls, params, callback);
+}, deprecatedMessage('index.addUserKey()', 'client.addApiKey()'));
+
+/*
+* Add a new API key to this index
+*
+* @param {string[]} acls - The list of ACL for this key. Defined by an array of strings that
+* can contains the following values:
+* - search: allow to search (https and http)
+* - addObject: allows to add/update an object in the index (https only)
+* - deleteObject : allows to delete an existing object (https only)
+* - deleteIndex : allows to delete index content (https only)
+* - settings : allows to get index settings (https only)
+* - editSettings : allows to change index settings (https only)
+* @param {Object} [params] - Optionnal parameters to set for the key
+* @param {number} params.validity - Number of seconds after which the key will
+* be automatically removed (0 means no time limit for this key)
+* @param {number} params.maxQueriesPerIPPerHour - Number of API calls allowed from an IP address per hour
+* @param {number} params.maxHitsPerQuery - Number of hits this API key can retrieve in one call
+* @param {string} params.description - A description for your key
+* @param {string[]} params.referers - A list of authorized referers
+* @param {Object} params.queryParameters - Force the key to use specific query parameters
+* @param {Function} callback - The result callback called with two arguments
+* error: null or Error('message')
+* content: the server answer with the added API key
+* @return {Promise|undefined} Returns a promise if no callback given
+* @example
+* index.addUserKey(['search'], {
+* validity: 300,
+* maxQueriesPerIPPerHour: 2000,
+* maxHitsPerQuery: 3,
+* description: 'Eat three fruits',
+* referers: ['*.algolia.com'],
+* queryParameters: {
+* tagFilters: ['public'],
+* }
+* })
+* @see {@link https://www.algolia.com/doc/rest_api#AddIndexKey|Algolia REST API Documentation}
+*
+* @deprecated see client.addApiKey()
+*/
+Index.prototype.addApiKey = deprecate(function(acls, params, callback) {
+ var isArray = require(8);
+ var usage = 'Usage: index.addApiKey(arrayOfAcls[, params, callback])';
+
+ if (!isArray(acls)) {
+ throw new Error(usage);
+ }
+
+ if (arguments.length === 1 || typeof params === 'function') {
+ callback = params;
+ params = null;
+ }
+
+ var postObj = {
+ acl: acls
+ };
+
+ if (params) {
+ postObj.validity = params.validity;
+ postObj.maxQueriesPerIPPerHour = params.maxQueriesPerIPPerHour;
+ postObj.maxHitsPerQuery = params.maxHitsPerQuery;
+ postObj.description = params.description;
+
+ if (params.queryParameters) {
+ postObj.queryParameters = this.as._getSearchParams(params.queryParameters, '');
+ }
+
+ postObj.referers = params.referers;
+ }
+
+ return this.as._jsonRequest({
+ method: 'POST',
+ url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/keys',
+ body: postObj,
+ hostType: 'write',
+ callback: callback
+ });
+}, deprecatedMessage('index.addApiKey()', 'client.addApiKey()'));
+
+/**
+* @deprecated use client.addApiKey()
+*/
+Index.prototype.addUserKeyWithValidity = deprecate(function deprecatedAddUserKeyWithValidity(acls, params, callback) {
+ return this.addApiKey(acls, params, callback);
+}, deprecatedMessage('index.addUserKeyWithValidity()', 'client.addApiKey()'));
+
+/*
+* @deprecated see client.updateApiKey()
+*/
+Index.prototype.updateUserKey = deprecate(function(key, acls, params, callback) {
+ return this.updateApiKey(key, acls, params, callback);
+}, deprecatedMessage('index.updateUserKey()', 'client.updateApiKey()'));
+
+/**
+* Update an existing API key of this index
+* @param {string} key - The key to update
+* @param {string[]} acls - The list of ACL for this key. Defined by an array of strings that
+* can contains the following values:
+* - search: allow to search (https and http)
+* - addObject: allows to add/update an object in the index (https only)
+* - deleteObject : allows to delete an existing object (https only)
+* - deleteIndex : allows to delete index content (https only)
+* - settings : allows to get index settings (https only)
+* - editSettings : allows to change index settings (https only)
+* @param {Object} [params] - Optionnal parameters to set for the key
+* @param {number} params.validity - Number of seconds after which the key will
+* be automatically removed (0 means no time limit for this key)
+* @param {number} params.maxQueriesPerIPPerHour - Number of API calls allowed from an IP address per hour
+* @param {number} params.maxHitsPerQuery - Number of hits this API key can retrieve in one call
+* @param {string} params.description - A description for your key
+* @param {string[]} params.referers - A list of authorized referers
+* @param {Object} params.queryParameters - Force the key to use specific query parameters
+* @param {Function} callback - The result callback called with two arguments
+* error: null or Error('message')
+* content: the server answer with user keys list
+* @return {Promise|undefined} Returns a promise if no callback given
+* @example
+* index.updateApiKey('APIKEY', ['search'], {
+* validity: 300,
+* maxQueriesPerIPPerHour: 2000,
+* maxHitsPerQuery: 3,
+* description: 'Eat three fruits',
+* referers: ['*.algolia.com'],
+* queryParameters: {
+* tagFilters: ['public'],
+* }
+* })
+* @see {@link https://www.algolia.com/doc/rest_api#UpdateIndexKey|Algolia REST API Documentation}
+*
+* @deprecated see client.updateApiKey()
+*/
+Index.prototype.updateApiKey = deprecate(function(key, acls, params, callback) {
+ var isArray = require(8);
+ var usage = 'Usage: index.updateApiKey(key, arrayOfAcls[, params, callback])';
+
+ if (!isArray(acls)) {
+ throw new Error(usage);
+ }
+
+ if (arguments.length === 2 || typeof params === 'function') {
+ callback = params;
+ params = null;
+ }
+
+ var putObj = {
+ acl: acls
+ };
+
+ if (params) {
+ putObj.validity = params.validity;
+ putObj.maxQueriesPerIPPerHour = params.maxQueriesPerIPPerHour;
+ putObj.maxHitsPerQuery = params.maxHitsPerQuery;
+ putObj.description = params.description;
+
+ if (params.queryParameters) {
+ putObj.queryParameters = this.as._getSearchParams(params.queryParameters, '');
+ }
+
+ putObj.referers = params.referers;
+ }
+
+ return this.as._jsonRequest({
+ method: 'PUT',
+ url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/keys/' + key,
+ body: putObj,
+ hostType: 'write',
+ callback: callback
+ });
+}, deprecatedMessage('index.updateApiKey()', 'client.updateApiKey()'));
+
+},{"19":19,"20":20,"26":26,"28":28,"29":29,"30":30,"31":31,"32":32,"33":33,"7":7,"8":8}],19:[function(require,module,exports){
+'use strict';
+
+// This is the object returned by the `index.browseAll()` method
+
+module.exports = IndexBrowser;
+
+var inherits = require(7);
+var EventEmitter = require(4).EventEmitter;
+
+function IndexBrowser() {
+}
+
+inherits(IndexBrowser, EventEmitter);
+
+IndexBrowser.prototype.stop = function() {
+ this._stopped = true;
+ this._clean();
+};
+
+IndexBrowser.prototype._end = function() {
+ this.emit('end');
+ this._clean();
+};
+
+IndexBrowser.prototype._error = function(err) {
+ this.emit('error', err);
+ this._clean();
+};
+
+IndexBrowser.prototype._result = function(content) {
+ this.emit('result', content);
+};
+
+IndexBrowser.prototype._clean = function() {
+ this.removeAllListeners('stop');
+ this.removeAllListeners('end');
+ this.removeAllListeners('error');
+ this.removeAllListeners('result');
+};
+
+},{"4":4,"7":7}],20:[function(require,module,exports){
+var buildSearchMethod = require(25);
+var deprecate = require(28);
+var deprecatedMessage = require(29);
+
+module.exports = IndexCore;
+
+/*
+* Index class constructor.
+* You should not use this method directly but use initIndex() function
+*/
+function IndexCore(algoliasearch, indexName) {
+ this.indexName = indexName;
+ this.as = algoliasearch;
+ this.typeAheadArgs = null;
+ this.typeAheadValueOption = null;
+
+ // make sure every index instance has it's own cache
+ this.cache = {};
+}
+
+/*
+* Clear all queries in cache
+*/
+IndexCore.prototype.clearCache = function() {
+ this.cache = {};
+};
+
+/*
+* Search inside the index using XMLHttpRequest request (Using a POST query to
+* minimize number of OPTIONS queries: Cross-Origin Resource Sharing).
+*
+* @param {string} [query] the full text query
+* @param {object} [args] (optional) if set, contains an object with query parameters:
+* - page: (integer) Pagination parameter used to select the page to retrieve.
+* Page is zero-based and defaults to 0. Thus,
+* to retrieve the 10th page you need to set page=9
+* - hitsPerPage: (integer) Pagination parameter used to select the number of hits per page. Defaults to 20.
+* - attributesToRetrieve: a string that contains the list of object attributes
+* you want to retrieve (let you minimize the answer size).
+* Attributes are separated with a comma (for example "name,address").
+* You can also use an array (for example ["name","address"]).
+* By default, all attributes are retrieved. You can also use '*' to retrieve all
+* values when an attributesToRetrieve setting is specified for your index.
+* - attributesToHighlight: a string that contains the list of attributes you
+* want to highlight according to the query.
+* Attributes are separated by a comma. You can also use an array (for example ["name","address"]).
+* If an attribute has no match for the query, the raw value is returned.
+* By default all indexed text attributes are highlighted.
+* You can use `*` if you want to highlight all textual attributes.
+* Numerical attributes are not highlighted.
+* A matchLevel is returned for each highlighted attribute and can contain:
+* - full: if all the query terms were found in the attribute,
+* - partial: if only some of the query terms were found,
+* - none: if none of the query terms were found.
+* - attributesToSnippet: a string that contains the list of attributes to snippet alongside
+* the number of words to return (syntax is `attributeName:nbWords`).
+* Attributes are separated by a comma (Example: attributesToSnippet=name:10,content:10).
+* You can also use an array (Example: attributesToSnippet: ['name:10','content:10']).
+* By default no snippet is computed.
+* - minWordSizefor1Typo: the minimum number of characters in a query word to accept one typo in this word.
+* Defaults to 3.
+* - minWordSizefor2Typos: the minimum number of characters in a query word
+* to accept two typos in this word. Defaults to 7.
+* - getRankingInfo: if set to 1, the result hits will contain ranking
+* information in _rankingInfo attribute.
+* - aroundLatLng: search for entries around a given
+* latitude/longitude (specified as two floats separated by a comma).
+* For example aroundLatLng=47.316669,5.016670).
+* You can specify the maximum distance in meters with the aroundRadius parameter (in meters)
+* and the precision for ranking with aroundPrecision
+* (for example if you set aroundPrecision=100, two objects that are distant of
+* less than 100m will be considered as identical for "geo" ranking parameter).
+* At indexing, you should specify geoloc of an object with the _geoloc attribute
+* (in the form {"_geoloc":{"lat":48.853409, "lng":2.348800}})
+* - insideBoundingBox: search entries inside a given area defined by the two extreme points
+* of a rectangle (defined by 4 floats: p1Lat,p1Lng,p2Lat,p2Lng).
+* For example insideBoundingBox=47.3165,4.9665,47.3424,5.0201).
+* At indexing, you should specify geoloc of an object with the _geoloc attribute
+* (in the form {"_geoloc":{"lat":48.853409, "lng":2.348800}})
+* - numericFilters: a string that contains the list of numeric filters you want to
+* apply separated by a comma.
+* The syntax of one filter is `attributeName` followed by `operand` followed by `value`.
+* Supported operands are `<`, `<=`, `=`, `>` and `>=`.
+* You can have multiple conditions on one attribute like for example numericFilters=price>100,price<1000.
+* You can also use an array (for example numericFilters: ["price>100","price<1000"]).
+* - tagFilters: filter the query by a set of tags. You can AND tags by separating them by commas.
+* To OR tags, you must add parentheses. For example, tags=tag1,(tag2,tag3) means tag1 AND (tag2 OR tag3).
+* You can also use an array, for example tagFilters: ["tag1",["tag2","tag3"]]
+* means tag1 AND (tag2 OR tag3).
+* At indexing, tags should be added in the _tags** attribute
+* of objects (for example {"_tags":["tag1","tag2"]}).
+* - facetFilters: filter the query by a list of facets.
+* Facets are separated by commas and each facet is encoded as `attributeName:value`.
+* For example: `facetFilters=category:Book,author:John%20Doe`.
+* You can also use an array (for example `["category:Book","author:John%20Doe"]`).
+* - facets: List of object attributes that you want to use for faceting.
+* Comma separated list: `"category,author"` or array `['category','author']`
+* Only attributes that have been added in **attributesForFaceting** index setting
+* can be used in this parameter.
+* You can also use `*` to perform faceting on all attributes specified in **attributesForFaceting**.
+* - queryType: select how the query words are interpreted, it can be one of the following value:
+* - prefixAll: all query words are interpreted as prefixes,
+* - prefixLast: only the last word is interpreted as a prefix (default behavior),
+* - prefixNone: no query word is interpreted as a prefix. This option is not recommended.
+* - optionalWords: a string that contains the list of words that should
+* be considered as optional when found in the query.
+* Comma separated and array are accepted.
+* - distinct: If set to 1, enable the distinct feature (disabled by default)
+* if the attributeForDistinct index setting is set.
+* This feature is similar to the SQL "distinct" keyword: when enabled
+* in a query with the distinct=1 parameter,
+* all hits containing a duplicate value for the attributeForDistinct attribute are removed from results.
+* For example, if the chosen attribute is show_name and several hits have
+* the same value for show_name, then only the best
+* one is kept and others are removed.
+* - restrictSearchableAttributes: List of attributes you want to use for
+* textual search (must be a subset of the attributesToIndex index setting)
+* either comma separated or as an array
+* @param {function} [callback] the result callback called with two arguments:
+* error: null or Error('message'). If false, the content contains the error.
+* content: the server answer that contains the list of results.
+*/
+IndexCore.prototype.search = buildSearchMethod('query');
+
+/*
+* -- BETA --
+* Search a record similar to the query inside the index using XMLHttpRequest request (Using a POST query to
+* minimize number of OPTIONS queries: Cross-Origin Resource Sharing).
+*
+* @param {string} [query] the similar query
+* @param {object} [args] (optional) if set, contains an object with query parameters.
+* All search parameters are supported (see search function), restrictSearchableAttributes and facetFilters
+* are the two most useful to restrict the similar results and get more relevant content
+*/
+IndexCore.prototype.similarSearch = deprecate(
+ buildSearchMethod('similarQuery'),
+ deprecatedMessage(
+ 'index.similarSearch(query[, callback])',
+ 'index.search({ similarQuery: query }[, callback])'
+ )
+);
+
+/*
+* Browse index content. The response content will have a `cursor` property that you can use
+* to browse subsequent pages for this query. Use `index.browseFrom(cursor)` when you want.
+*
+* @param {string} query - The full text query
+* @param {Object} [queryParameters] - Any search query parameter
+* @param {Function} [callback] - The result callback called with two arguments
+* error: null or Error('message')
+* content: the server answer with the browse result
+* @return {Promise|undefined} Returns a promise if no callback given
+* @example
+* index.browse('cool songs', {
+* tagFilters: 'public,comments',
+* hitsPerPage: 500
+* }, callback);
+* @see {@link https://www.algolia.com/doc/rest_api#Browse|Algolia REST API Documentation}
+*/
+IndexCore.prototype.browse = function(query, queryParameters, callback) {
+ var merge = require(33);
+
+ var indexObj = this;
+
+ var page;
+ var hitsPerPage;
+
+ // we check variadic calls that are not the one defined
+ // .browse()/.browse(fn)
+ // => page = 0
+ if (arguments.length === 0 || arguments.length === 1 && typeof arguments[0] === 'function') {
+ page = 0;
+ callback = arguments[0];
+ query = undefined;
+ } else if (typeof arguments[0] === 'number') {
+ // .browse(2)/.browse(2, 10)/.browse(2, fn)/.browse(2, 10, fn)
+ page = arguments[0];
+ if (typeof arguments[1] === 'number') {
+ hitsPerPage = arguments[1];
+ } else if (typeof arguments[1] === 'function') {
+ callback = arguments[1];
+ hitsPerPage = undefined;
+ }
+ query = undefined;
+ queryParameters = undefined;
+ } else if (typeof arguments[0] === 'object') {
+ // .browse(queryParameters)/.browse(queryParameters, cb)
+ if (typeof arguments[1] === 'function') {
+ callback = arguments[1];
+ }
+ queryParameters = arguments[0];
+ query = undefined;
+ } else if (typeof arguments[0] === 'string' && typeof arguments[1] === 'function') {
+ // .browse(query, cb)
+ callback = arguments[1];
+ queryParameters = undefined;
+ }
+
+ // otherwise it's a .browse(query)/.browse(query, queryParameters)/.browse(query, queryParameters, cb)
+
+ // get search query parameters combining various possible calls
+ // to .browse();
+ queryParameters = merge({}, queryParameters || {}, {
+ page: page,
+ hitsPerPage: hitsPerPage,
+ query: query
+ });
+
+ var params = this.as._getSearchParams(queryParameters, '');
+
+ return this.as._jsonRequest({
+ method: 'POST',
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/browse',
+ body: {params: params},
+ hostType: 'read',
+ callback: callback
+ });
+};
+
+/*
+* Continue browsing from a previous position (cursor), obtained via a call to `.browse()`.
+*
+* @param {string} query - The full text query
+* @param {Object} [queryParameters] - Any search query parameter
+* @param {Function} [callback] - The result callback called with two arguments
+* error: null or Error('message')
+* content: the server answer with the browse result
+* @return {Promise|undefined} Returns a promise if no callback given
+* @example
+* index.browseFrom('14lkfsakl32', callback);
+* @see {@link https://www.algolia.com/doc/rest_api#Browse|Algolia REST API Documentation}
+*/
+IndexCore.prototype.browseFrom = function(cursor, callback) {
+ return this.as._jsonRequest({
+ method: 'POST',
+ url: '/1/indexes/' + encodeURIComponent(this.indexName) + '/browse',
+ body: {cursor: cursor},
+ hostType: 'read',
+ callback: callback
+ });
+};
+
+/*
+* Search for facet values
+* https://www.algolia.com/doc/rest-api/search#search-for-facet-values
+*
+* @param {string} params.facetName Facet name, name of the attribute to search for values in.
+* Must be declared as a facet
+* @param {string} params.facetQuery Query for the facet search
+* @param {string} [params.*] Any search parameter of Algolia,
+* see https://www.algolia.com/doc/api-client/javascript/search#search-parameters
+* Pagination is not supported. The page and hitsPerPage parameters will be ignored.
+* @param callback (optional)
+*/
+IndexCore.prototype.searchForFacetValues = function(params, callback) {
+ var clone = require(26);
+ var omit = require(34);
+ var usage = 'Usage: index.searchForFacetValues({facetName, facetQuery, ...params}[, callback])';
+
+ if (params.facetName === undefined || params.facetQuery === undefined) {
+ throw new Error(usage);
+ }
+
+ var facetName = params.facetName;
+ var filteredParams = omit(clone(params), function(keyName) {
+ return keyName === 'facetName';
+ });
+ var searchParameters = this.as._getSearchParams(filteredParams, '');
+
+ return this.as._jsonRequest({
+ method: 'POST',
+ url: '/1/indexes/' +
+ encodeURIComponent(this.indexName) + '/facets/' + encodeURIComponent(facetName) + '/query',
+ hostType: 'read',
+ body: {params: searchParameters},
+ callback: callback
+ });
+};
+
+IndexCore.prototype.searchFacet = deprecate(function(params, callback) {
+ return this.searchForFacetValues(params, callback);
+}, deprecatedMessage(
+ 'index.searchFacet(params[, callback])',
+ 'index.searchForFacetValues(params[, callback])'
+));
+
+IndexCore.prototype._search = function(params, url, callback, additionalUA) {
+ return this.as._jsonRequest({
+ cache: this.cache,
+ method: 'POST',
+ url: url || '/1/indexes/' + encodeURIComponent(this.indexName) + '/query',
+ body: {params: params},
+ hostType: 'read',
+ fallback: {
+ method: 'GET',
+ url: '/1/indexes/' + encodeURIComponent(this.indexName),
+ body: {params: params}
+ },
+ callback: callback,
+ additionalUA: additionalUA
+ });
+};
+
+/*
+* Get an object from this index
+*
+* @param objectID the unique identifier of the object to retrieve
+* @param attrs (optional) if set, contains the array of attribute names to retrieve
+* @param callback (optional) the result callback called with two arguments
+* error: null or Error('message')
+* content: the object to retrieve or the error message if a failure occurred
+*/
+IndexCore.prototype.getObject = function(objectID, attrs, callback) {
+ var indexObj = this;
+
+ if (arguments.length === 1 || typeof attrs === 'function') {
+ callback = attrs;
+ attrs = undefined;
+ }
+
+ var params = '';
+ if (attrs !== undefined) {
+ params = '?attributes=';
+ for (var i = 0; i < attrs.length; ++i) {
+ if (i !== 0) {
+ params += ',';
+ }
+ params += attrs[i];
+ }
+ }
+
+ return this.as._jsonRequest({
+ method: 'GET',
+ url: '/1/indexes/' + encodeURIComponent(indexObj.indexName) + '/' + encodeURIComponent(objectID) + params,
+ hostType: 'read',
+ callback: callback
+ });
+};
+
+/*
+* Get several objects from this index
+*
+* @param objectIDs the array of unique identifier of objects to retrieve
+*/
+IndexCore.prototype.getObjects = function(objectIDs, attributesToRetrieve, callback) {
+ var isArray = require(8);
+ var map = require(32);
+
+ var usage = 'Usage: index.getObjects(arrayOfObjectIDs[, callback])';
+
+ if (!isArray(objectIDs)) {
+ throw new Error(usage);
+ }
+
+ var indexObj = this;
+
+ if (arguments.length === 1 || typeof attributesToRetrieve === 'function') {
+ callback = attributesToRetrieve;
+ attributesToRetrieve = undefined;
+ }
+
+ var body = {
+ requests: map(objectIDs, function prepareRequest(objectID) {
+ var request = {
+ indexName: indexObj.indexName,
+ objectID: objectID
+ };
+
+ if (attributesToRetrieve) {
+ request.attributesToRetrieve = attributesToRetrieve.join(',');
+ }
+
+ return request;
+ })
+ };
+
+ return this.as._jsonRequest({
+ method: 'POST',
+ url: '/1/indexes/*/objects',
+ hostType: 'read',
+ body: body,
+ callback: callback
+ });
+};
+
+IndexCore.prototype.as = null;
+IndexCore.prototype.indexName = null;
+IndexCore.prototype.typeAheadArgs = null;
+IndexCore.prototype.typeAheadValueOption = null;
+
+},{"25":25,"26":26,"28":28,"29":29,"32":32,"33":33,"34":34,"8":8}],21:[function(require,module,exports){
+'use strict';
+
+var AlgoliaSearch = require(16);
+var createAlgoliasearch = require(22);
+
+module.exports = createAlgoliasearch(AlgoliaSearch, 'Browser');
+
+},{"16":16,"22":22}],22:[function(require,module,exports){
+(function (process){
+'use strict';
+
+var global = require(6);
+var Promise = global.Promise || require(3).Promise;
+
+// This is the standalone browser build entry point
+// Browser implementation of the Algolia Search JavaScript client,
+// using XMLHttpRequest, XDomainRequest and JSONP as fallback
+module.exports = function createAlgoliasearch(AlgoliaSearch, uaSuffix) {
+ var inherits = require(7);
+ var errors = require(30);
+ var inlineHeaders = require(23);
+ var jsonpRequest = require(24);
+ var places = require(35);
+ uaSuffix = uaSuffix || '';
+
+ if (process.env.NODE_ENV === 'debug') {
+ require(1).enable('algoliasearch*');
+ }
+
+ function algoliasearch(applicationID, apiKey, opts) {
+ var cloneDeep = require(26);
+
+ opts = cloneDeep(opts || {});
+
+ opts._ua = opts._ua || algoliasearch.ua;
+
+ return new AlgoliaSearchBrowser(applicationID, apiKey, opts);
+ }
+
+ algoliasearch.version = require(37);
+
+ algoliasearch.ua =
+ 'Algolia for JavaScript (' + algoliasearch.version + '); ' + uaSuffix;
+
+ algoliasearch.initPlaces = places(algoliasearch);
+
+ // we expose into window no matter how we are used, this will allow
+ // us to easily debug any website running algolia
+ global.__algolia = {
+ debug: require(1),
+ algoliasearch: algoliasearch
+ };
+
+ var support = {
+ hasXMLHttpRequest: 'XMLHttpRequest' in global,
+ hasXDomainRequest: 'XDomainRequest' in global
+ };
+
+ if (support.hasXMLHttpRequest) {
+ support.cors = 'withCredentials' in new XMLHttpRequest();
+ }
+
+ function AlgoliaSearchBrowser() {
+ // call AlgoliaSearch constructor
+ AlgoliaSearch.apply(this, arguments);
+ }
+
+ inherits(AlgoliaSearchBrowser, AlgoliaSearch);
+
+ AlgoliaSearchBrowser.prototype._request = function request(url, opts) {
+ return new Promise(function wrapRequest(resolve, reject) {
+ // no cors or XDomainRequest, no request
+ if (!support.cors && !support.hasXDomainRequest) {
+ // very old browser, not supported
+ reject(new errors.Network('CORS not supported'));
+ return;
+ }
+
+ url = inlineHeaders(url, opts.headers);
+
+ var body = opts.body;
+ var req = support.cors ? new XMLHttpRequest() : new XDomainRequest();
+ var reqTimeout;
+ var timedOut;
+ var connected = false;
+
+ reqTimeout = setTimeout(onTimeout, opts.timeouts.connect);
+ // we set an empty onprogress listener
+ // so that XDomainRequest on IE9 is not aborted
+ // refs:
+ // - https://github.com/algolia/algoliasearch-client-js/issues/76
+ // - https://social.msdn.microsoft.com/Forums/ie/en-US/30ef3add-767c-4436-b8a9-f1ca19b4812e/ie9-rtm-xdomainrequest-issued-requests-may-abort-if-all-event-handlers-not-specified?forum=iewebdevelopment
+ req.onprogress = onProgress;
+ if ('onreadystatechange' in req) req.onreadystatechange = onReadyStateChange;
+ req.onload = onLoad;
+ req.onerror = onError;
+
+ // do not rely on default XHR async flag, as some analytics code like hotjar
+ // breaks it and set it to false by default
+ if (req instanceof XMLHttpRequest) {
+ req.open(opts.method, url, true);
+
+ // The Analytics API never accepts Auth headers as query string
+ // this option exists specifically for them.
+ if (opts.forceAuthHeaders) {
+ req.setRequestHeader(
+ 'x-algolia-application-id',
+ opts.headers['x-algolia-application-id']
+ );
+ req.setRequestHeader(
+ 'x-algolia-api-key',
+ opts.headers['x-algolia-api-key']
+ );
+ }
+ } else {
+ req.open(opts.method, url);
+ }
+
+ // headers are meant to be sent after open
+ if (support.cors) {
+ if (body) {
+ if (opts.method === 'POST') {
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Simple_requests
+ req.setRequestHeader('content-type', 'application/x-www-form-urlencoded');
+ } else {
+ req.setRequestHeader('content-type', 'application/json');
+ }
+ }
+ req.setRequestHeader('accept', 'application/json');
+ }
+
+ if (body) {
+ req.send(body);
+ } else {
+ req.send();
+ }
+
+ // event object not received in IE8, at least
+ // but we do not use it, still important to note
+ function onLoad(/* event */) {
+ // When browser does not supports req.timeout, we can
+ // have both a load and timeout event, since handled by a dumb setTimeout
+ if (timedOut) {
+ return;
+ }
+
+ clearTimeout(reqTimeout);
+
+ var out;
+
+ try {
+ out = {
+ body: JSON.parse(req.responseText),
+ responseText: req.responseText,
+ statusCode: req.status,
+ // XDomainRequest does not have any response headers
+ headers: req.getAllResponseHeaders && req.getAllResponseHeaders() || {}
+ };
+ } catch (e) {
+ out = new errors.UnparsableJSON({
+ more: req.responseText
+ });
+ }
+
+ if (out instanceof errors.UnparsableJSON) {
+ reject(out);
+ } else {
+ resolve(out);
+ }
+ }
+
+ function onError(event) {
+ if (timedOut) {
+ return;
+ }
+
+ clearTimeout(reqTimeout);
+
+ // error event is trigerred both with XDR/XHR on:
+ // - DNS error
+ // - unallowed cross domain request
+ reject(
+ new errors.Network({
+ more: event
+ })
+ );
+ }
+
+ function onTimeout() {
+ timedOut = true;
+ req.abort();
+
+ reject(new errors.RequestTimeout());
+ }
+
+ function onConnect() {
+ connected = true;
+ clearTimeout(reqTimeout);
+ reqTimeout = setTimeout(onTimeout, opts.timeouts.complete);
+ }
+
+ function onProgress() {
+ if (!connected) onConnect();
+ }
+
+ function onReadyStateChange() {
+ if (!connected && req.readyState > 1) onConnect();
+ }
+ });
+ };
+
+ AlgoliaSearchBrowser.prototype._request.fallback = function requestFallback(url, opts) {
+ url = inlineHeaders(url, opts.headers);
+
+ return new Promise(function wrapJsonpRequest(resolve, reject) {
+ jsonpRequest(url, opts, function jsonpRequestDone(err, content) {
+ if (err) {
+ reject(err);
+ return;
+ }
+
+ resolve(content);
+ });
+ });
+ };
+
+ AlgoliaSearchBrowser.prototype._promise = {
+ reject: function rejectPromise(val) {
+ return Promise.reject(val);
+ },
+ resolve: function resolvePromise(val) {
+ return Promise.resolve(val);
+ },
+ delay: function delayPromise(ms) {
+ return new Promise(function resolveOnTimeout(resolve/* , reject*/) {
+ setTimeout(resolve, ms);
+ });
+ },
+ all: function all(promises) {
+ return Promise.all(promises);
+ }
+ };
+
+ return algoliasearch;
+};
+
+}).call(this,require(12))
+},{"1":1,"12":12,"23":23,"24":24,"26":26,"3":3,"30":30,"35":35,"37":37,"6":6,"7":7}],23:[function(require,module,exports){
+'use strict';
+
+module.exports = inlineHeaders;
+
+var encode = require(14);
+
+function inlineHeaders(url, headers) {
+ if (/\?/.test(url)) {
+ url += '&';
+ } else {
+ url += '?';
+ }
+
+ return url + encode(headers);
+}
+
+},{"14":14}],24:[function(require,module,exports){
+'use strict';
+
+module.exports = jsonpRequest;
+
+var errors = require(30);
+
+var JSONPCounter = 0;
+
+function jsonpRequest(url, opts, cb) {
+ if (opts.method !== 'GET') {
+ cb(new Error('Method ' + opts.method + ' ' + url + ' is not supported by JSONP.'));
+ return;
+ }
+
+ opts.debug('JSONP: start');
+
+ var cbCalled = false;
+ var timedOut = false;
+
+ JSONPCounter += 1;
+ var head = document.getElementsByTagName('head')[0];
+ var script = document.createElement('script');
+ var cbName = 'algoliaJSONP_' + JSONPCounter;
+ var done = false;
+
+ window[cbName] = function(data) {
+ removeGlobals();
+
+ if (timedOut) {
+ opts.debug('JSONP: Late answer, ignoring');
+ return;
+ }
+
+ cbCalled = true;
+
+ clean();
+
+ cb(null, {
+ body: data,
+ responseText: JSON.stringify(data)/* ,
+ // We do not send the statusCode, there's no statusCode in JSONP, it will be
+ // computed using data.status && data.message like with XDR
+ statusCode*/
+ });
+ };
+
+ // add callback by hand
+ url += '&callback=' + cbName;
+
+ // add body params manually
+ if (opts.jsonBody && opts.jsonBody.params) {
+ url += '&' + opts.jsonBody.params;
+ }
+
+ var ontimeout = setTimeout(timeout, opts.timeouts.complete);
+
+ // script onreadystatechange needed only for
+ // <= IE8
+ // https://github.com/angular/angular.js/issues/4523
+ script.onreadystatechange = readystatechange;
+ script.onload = success;
+ script.onerror = error;
+
+ script.async = true;
+ script.defer = true;
+ script.src = url;
+ head.appendChild(script);
+
+ function success() {
+ opts.debug('JSONP: success');
+
+ if (done || timedOut) {
+ return;
+ }
+
+ done = true;
+
+ // script loaded but did not call the fn => script loading error
+ if (!cbCalled) {
+ opts.debug('JSONP: Fail. Script loaded but did not call the callback');
+ clean();
+ cb(new errors.JSONPScriptFail());
+ }
+ }
+
+ function readystatechange() {
+ if (this.readyState === 'loaded' || this.readyState === 'complete') {
+ success();
+ }
+ }
+
+ function clean() {
+ clearTimeout(ontimeout);
+ script.onload = null;
+ script.onreadystatechange = null;
+ script.onerror = null;
+ head.removeChild(script);
+ }
+
+ function removeGlobals() {
+ try {
+ delete window[cbName];
+ delete window[cbName + '_loaded'];
+ } catch (e) {
+ window[cbName] = window[cbName + '_loaded'] = undefined;
+ }
+ }
+
+ function timeout() {
+ opts.debug('JSONP: Script timeout');
+ timedOut = true;
+ clean();
+ cb(new errors.RequestTimeout());
+ }
+
+ function error() {
+ opts.debug('JSONP: Script error');
+
+ if (done || timedOut) {
+ return;
+ }
+
+ clean();
+ cb(new errors.JSONPScriptError());
+ }
+}
+
+},{"30":30}],25:[function(require,module,exports){
+module.exports = buildSearchMethod;
+
+var errors = require(30);
+
+/**
+ * Creates a search method to be used in clients
+ * @param {string} queryParam the name of the attribute used for the query
+ * @param {string} url the url
+ * @return {function} the search method
+ */
+function buildSearchMethod(queryParam, url) {
+ /**
+ * The search method. Prepares the data and send the query to Algolia.
+ * @param {string} query the string used for query search
+ * @param {object} args additional parameters to send with the search
+ * @param {function} [callback] the callback to be called with the client gets the answer
+ * @return {undefined|Promise} If the callback is not provided then this methods returns a Promise
+ */
+ return function search(query, args, callback) {
+ // warn V2 users on how to search
+ if (typeof query === 'function' && typeof args === 'object' ||
+ typeof callback === 'object') {
+ // .search(query, params, cb)
+ // .search(cb, params)
+ throw new errors.AlgoliaSearchError('index.search usage is index.search(query, params, cb)');
+ }
+
+ // Normalizing the function signature
+ if (arguments.length === 0 || typeof query === 'function') {
+ // Usage : .search(), .search(cb)
+ callback = query;
+ query = '';
+ } else if (arguments.length === 1 || typeof args === 'function') {
+ // Usage : .search(query/args), .search(query, cb)
+ callback = args;
+ args = undefined;
+ }
+ // At this point we have 3 arguments with values
+
+ // Usage : .search(args) // careful: typeof null === 'object'
+ if (typeof query === 'object' && query !== null) {
+ args = query;
+ query = undefined;
+ } else if (query === undefined || query === null) { // .search(undefined/null)
+ query = '';
+ }
+
+ var params = '';
+
+ if (query !== undefined) {
+ params += queryParam + '=' + encodeURIComponent(query);
+ }
+
+ var additionalUA;
+ if (args !== undefined) {
+ if (args.additionalUA) {
+ additionalUA = args.additionalUA;
+ delete args.additionalUA;
+ }
+ // `_getSearchParams` will augment params, do not be fooled by the = versus += from previous if
+ params = this.as._getSearchParams(args, params);
+ }
+
+
+ return this._search(params, url, callback, additionalUA);
+ };
+}
+
+},{"30":30}],26:[function(require,module,exports){
+module.exports = function clone(obj) {
+ return JSON.parse(JSON.stringify(obj));
+};
+
+},{}],27:[function(require,module,exports){
+module.exports = createAnalyticsClient;
+
+var algoliasearch = require(21);
+
+function createAnalyticsClient(appId, apiKey, opts) {
+ var analytics = {};
+
+ opts = opts || {};
+ // there need to be 4 hosts, like on the client, since if requests fail,
+ // the counter goes up by 1, so we need to have the same amount of hosts
+ // 4 because: -dsn, -1, -2, -3
+ // This is done because the APPID used for search will be the same for the analytics client created,
+ // and since the state of available hosts is shared by APPID globally for the module, we had issues
+ // where the hostIndex would be 1 while the array was only one entry (you got an empty host)
+ opts.hosts = opts.hosts || [
+ 'analytics.algolia.com',
+ 'analytics.algolia.com',
+ 'analytics.algolia.com',
+ 'analytics.algolia.com'
+ ];
+ opts.protocol = opts.protocol || 'https:';
+
+ analytics.as = algoliasearch(appId, apiKey, opts);
+
+ analytics.getABTests = function(_params, callback) {
+ var params = params || {};
+ var offset = params.offset || 0;
+ var limit = params.limit || 10;
+
+ return this.as._jsonRequest({
+ method: 'GET',
+ url: '/2/abtests?offset=' + encodeURIComponent(offset) + '&limit=' + encodeURIComponent(limit),
+ hostType: 'read',
+ forceAuthHeaders: true,
+ callback: callback
+ });
+ };
+
+ analytics.getABTest = function(abTestID, callback) {
+ return this.as._jsonRequest({
+ method: 'GET',
+ url: '/2/abtests/' + encodeURIComponent(abTestID),
+ hostType: 'read',
+ forceAuthHeaders: true,
+ callback: callback
+ });
+ };
+
+ analytics.addABTest = function(abTest, callback) {
+ return this.as._jsonRequest({
+ method: 'POST',
+ url: '/2/abtests',
+ body: abTest,
+ hostType: 'read',
+ forceAuthHeaders: true,
+ callback: callback
+ });
+ };
+
+ analytics.stopABTest = function(abTestID, callback) {
+ return this.as._jsonRequest({
+ method: 'POST',
+ url: '/2/abtests/' + encodeURIComponent(abTestID) + '/stop',
+ hostType: 'read',
+ forceAuthHeaders: true,
+ callback: callback
+ });
+ };
+
+ analytics.deleteABTest = function(abTestID, callback) {
+ return this.as._jsonRequest({
+ method: 'DELETE',
+ url: '/2/abtests/' + encodeURIComponent(abTestID),
+ hostType: 'write',
+ forceAuthHeaders: true,
+ callback: callback
+ });
+ };
+
+ analytics.waitTask = function(indexName, taskID, callback) {
+ return this.as.initIndex(indexName).waitTask(taskID, callback);
+ };
+
+ return analytics;
+}
+
+},{"21":21}],28:[function(require,module,exports){
+module.exports = function deprecate(fn, message) {
+ var warned = false;
+
+ function deprecated() {
+ if (!warned) {
+ /* eslint no-console:0 */
+ console.warn(message);
+ warned = true;
+ }
+
+ return fn.apply(this, arguments);
+ }
+
+ return deprecated;
+};
+
+},{}],29:[function(require,module,exports){
+module.exports = function deprecatedMessage(previousUsage, newUsage) {
+ var githubAnchorLink = previousUsage.toLowerCase()
+ .replace(/[\.\(\)]/g, '');
+
+ return 'algoliasearch: `' + previousUsage + '` was replaced by `' + newUsage +
+ '`. Please see https://github.com/algolia/algoliasearch-client-javascript/wiki/Deprecated#' + githubAnchorLink;
+};
+
+},{}],30:[function(require,module,exports){
+'use strict';
+
+// This file hosts our error definitions
+// We use custom error "types" so that we can act on them when we need it
+// e.g.: if error instanceof errors.UnparsableJSON then..
+
+var inherits = require(7);
+
+function AlgoliaSearchError(message, extraProperties) {
+ var forEach = require(5);
+
+ var error = this;
+
+ // try to get a stacktrace
+ if (typeof Error.captureStackTrace === 'function') {
+ Error.captureStackTrace(this, this.constructor);
+ } else {
+ error.stack = (new Error()).stack || 'Cannot get a stacktrace, browser is too old';
+ }
+
+ this.name = 'AlgoliaSearchError';
+ this.message = message || 'Unknown error';
+
+ if (extraProperties) {
+ forEach(extraProperties, function addToErrorObject(value, key) {
+ error[key] = value;
+ });
+ }
+}
+
+inherits(AlgoliaSearchError, Error);
+
+function createCustomError(name, message) {
+ function AlgoliaSearchCustomError() {
+ var args = Array.prototype.slice.call(arguments, 0);
+
+ // custom message not set, use default
+ if (typeof args[0] !== 'string') {
+ args.unshift(message);
+ }
+
+ AlgoliaSearchError.apply(this, args);
+ this.name = 'AlgoliaSearch' + name + 'Error';
+ }
+
+ inherits(AlgoliaSearchCustomError, AlgoliaSearchError);
+
+ return AlgoliaSearchCustomError;
+}
+
+// late exports to let various fn defs and inherits take place
+module.exports = {
+ AlgoliaSearchError: AlgoliaSearchError,
+ UnparsableJSON: createCustomError(
+ 'UnparsableJSON',
+ 'Could not parse the incoming response as JSON, see err.more for details'
+ ),
+ RequestTimeout: createCustomError(
+ 'RequestTimeout',
+ 'Request timed out before getting a response'
+ ),
+ Network: createCustomError(
+ 'Network',
+ 'Network issue, see err.more for details'
+ ),
+ JSONPScriptFail: createCustomError(
+ 'JSONPScriptFail',
+ '"),window.ALGOLIA_SUPPORTS_DOCWRITE===!0?(document.write(''),n("document.write")()):r(o,n("DOMElement"))}catch(s){r(o,n("DOMElement"))}}function n(e){return function(){var t="AlgoliaSearch: loaded V2 script using "+e;window.console&&window.console.log&&window.console.log(t)}}t.exports=o},{1:1}],4:[function(e,t,r){"use strict";function o(){var e="-- AlgoliaSearch V2 => V3 error --\nYou are trying to use a new version of the AlgoliaSearch JavaScript client with an old notation.\nPlease read our migration guide at https://github.com/algolia/algoliasearch-client-js/wiki/Migration-guide-from-2.x.x-to-3.x.x\n-- /AlgoliaSearch V2 => V3 error --";window.AlgoliaSearch=function(){throw new Error(e)},window.AlgoliaSearchHelper=function(){throw new Error(e)},window.AlgoliaExplainResults=function(){throw new Error(e)}}t.exports=o},{}],5:[function(e,t,r){"use strict";function o(t){var r=e(2),o=e(3),n=e(4);r(t)?o(t):n()}o("algoliasearch")},{2:2,3:3,4:4}]},{},[5])(5)}),function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.algoliasearch=e()}}(function(){var e;return function t(e,r,o){function n(s,a){if(!r[s]){if(!e[s]){var c="function"==typeof require&&require;if(!a&&c)return c(s,!0);if(i)return i(s,!0);var u=new Error("Cannot find module '"+s+"'");throw u.code="MODULE_NOT_FOUND",u}var l=r[s]={exports:{}};e[s][0].call(l.exports,function(t){var r=e[s][1][t];return n(r?r:t)},l,l.exports,t,e,r,o)}return r[s].exports}for(var i="function"==typeof require&&require,s=0;s=31||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/))}function i(e){var t=this.useColors;if(e[0]=(t?"%c":"")+this.namespace+(t?" %c":" ")+e[0]+(t?"%c ":" ")+"+"+r.humanize(this.diff),t){var o="color: "+this.color;e.splice(1,0,o,"color: inherit");var n=0,i=0;e[0].replace(/%[a-zA-Z%]/g,function(e){"%%"!==e&&(n++,"%c"===e&&(i=n))}),e.splice(i,0,o)}}function s(){return"object"==typeof console&&console.log&&Function.prototype.apply.call(console.log,console,arguments)}function a(e){try{null==e?r.storage.removeItem("debug"):r.storage.debug=e}catch(t){}}function c(){var e;try{e=r.storage.debug}catch(t){}return!e&&"undefined"!=typeof o&&"env"in o&&(e=o.env.DEBUG),e}function u(){try{return window.localStorage}catch(e){}}r=t.exports=e(2),r.log=s,r.formatArgs=i,r.save=a,r.load=c,r.useColors=n,r.storage="undefined"!=typeof chrome&&"undefined"!=typeof chrome.storage?chrome.storage.local:u(),r.colors=["lightseagreen","forestgreen","goldenrod","dodgerblue","darkorchid","crimson"],r.formatters.j=function(e){try{return JSON.stringify(e)}catch(t){return"[UnexpectedJSONParseError]: "+t.message}},r.enable(c())}).call(this,e(12))},{12:12,2:2}],2:[function(e,t,r){function o(e){var t,o=0;for(t in e)o=(o<<5)-o+e.charCodeAt(t),o|=0;return r.colors[Math.abs(o)%r.colors.length]}function n(e){function t(){if(t.enabled){var e=t,o=+new Date,n=o-(u||o);e.diff=n,e.prev=u,e.curr=o,u=o;for(var i=new Array(arguments.length),s=0;s0&&this._events[e].length>r&&(this._events[e].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[e].length),"function"==typeof console.trace&&console.trace())),this},o.prototype.on=o.prototype.addListener,o.prototype.once=function(e,t){function r(){this.removeListener(e,r),o||(o=!0,t.apply(this,arguments))}if(!n(t))throw TypeError("listener must be a function");var o=!1;return r.listener=t,this.on(e,r),this},o.prototype.removeListener=function(e,t){var r,o,i,a;if(!n(t))throw TypeError("listener must be a function");if(!this._events||!this._events[e])return this;if(r=this._events[e],i=r.length,o=-1,r===t||n(r.listener)&&r.listener===t)delete this._events[e],this._events.removeListener&&this.emit("removeListener",e,t);else if(s(r)){for(a=i;a-- >0;)if(r[a]===t||r[a].listener&&r[a].listener===t){o=a;break}if(o<0)return this;1===r.length?(r.length=0,delete this._events[e]):r.splice(o,1),this._events.removeListener&&this.emit("removeListener",e,t)}return this},o.prototype.removeAllListeners=function(e){var t,r;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length?this._events={}:this._events[e]&&delete this._events[e],this;if(0===arguments.length){for(t in this._events)"removeListener"!==t&&this.removeAllListeners(t);return this.removeAllListeners("removeListener"),this._events={},this}if(r=this._events[e],n(r))this.removeListener(e,r);else if(r)for(;r.length;)this.removeListener(e,r[r.length-1]);return delete this._events[e],this},o.prototype.listeners=function(e){var t;return t=this._events&&this._events[e]?n(this._events[e])?[this._events[e]]:this._events[e].slice():[]},o.prototype.listenerCount=function(e){if(this._events){var t=this._events[e];if(n(t))return 1;if(t)return t.length}return 0},o.listenerCount=function(e,t){return e.listenerCount(t)}},{}],5:[function(e,t,r){var o=Object.prototype.hasOwnProperty,n=Object.prototype.toString;t.exports=function(e,t,r){if("[object Function]"!==n.call(t))throw new TypeError("iterator must be a function");var i=e.length;if(i===+i)for(var s=0;s100)){var t=/^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(e);if(t){var r=parseFloat(t[1]),o=(t[2]||"ms").toLowerCase();switch(o){case"years":case"year":case"yrs":case"yr":case"y":return r*p;case"days":case"day":case"d":return r*l;case"hours":case"hour":case"hrs":case"hr":case"h":return r*u;case"minutes":case"minute":case"mins":case"min":case"m":return r*c;case"seconds":case"second":case"secs":case"sec":case"s":return r*a;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return r;default:return}}}}function n(e){return e>=l?Math.round(e/l)+"d":e>=u?Math.round(e/u)+"h":e>=c?Math.round(e/c)+"m":e>=a?Math.round(e/a)+"s":e+"ms"}function i(e){return s(e,l,"day")||s(e,u,"hour")||s(e,c,"minute")||s(e,a,"second")||e+" ms"}function s(e,t,r){if(!(e0)return o(e);if("number"===r&&isNaN(e)===!1)return t["long"]?i(e):n(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))}},{}],10:[function(e,t,r){"use strict";var o=Object.prototype.hasOwnProperty,n=Object.prototype.toString,i=Array.prototype.slice,s=e(11),a=Object.prototype.propertyIsEnumerable,c=!a.call({toString:null},"toString"),u=a.call(function(){},"prototype"),l=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],p=function(e){var t=e.constructor;return t&&t.prototype===e},d={$console:!0,$external:!0,$frame:!0,$frameElement:!0,$frames:!0,$innerHeight:!0,$innerWidth:!0,$outerHeight:!0,$outerWidth:!0,$pageXOffset:!0,$pageYOffset:!0,$parent:!0,$scrollLeft:!0,$scrollTop:!0,$scrollX:!0,$scrollY:!0,$self:!0,$webkitIndexedDB:!0,$webkitStorageInfo:!0,$window:!0},h=function(){if("undefined"==typeof window)return!1;for(var e in window)try{if(!d["$"+e]&&o.call(window,e)&&null!==window[e]&&"object"==typeof window[e])try{p(window[e])}catch(t){return!0}}catch(t){return!0}return!1}(),f=function(e){if("undefined"==typeof window||!h)return p(e);try{return p(e)}catch(t){return!1}},y=function(e){var t=null!==e&&"object"==typeof e,r="[object Function]"===n.call(e),i=s(e),a=t&&"[object String]"===n.call(e),p=[];if(!t&&!r&&!i)throw new TypeError("Object.keys called on a non-object");var d=u&&r;if(a&&e.length>0&&!o.call(e,0))for(var h=0;h0)for(var y=0;y=0&&"[object Function]"===o.call(e.callee)),r}},{}],12:[function(e,t,r){function o(){throw new Error("setTimeout has not been defined")}function n(){throw new Error("clearTimeout has not been defined")}function i(e){if(p===setTimeout)return setTimeout(e,0);if((p===o||!p)&&setTimeout)return p=setTimeout,setTimeout(e,0);try{return p(e,0)}catch(t){try{return p.call(null,e,0)}catch(t){return p.call(this,e,0)}}}function s(e){if(d===clearTimeout)return clearTimeout(e);if((d===n||!d)&&clearTimeout)return d=clearTimeout,clearTimeout(e);try{return d(e)}catch(t){try{return d.call(null,e)}catch(t){return d.call(this,e)}}}function a(){m&&f&&(m=!1,f.length?y=f.concat(y):v=-1,y.length&&c())}function c(){if(!m){var e=i(a);m=!0;for(var t=y.length;t;){for(f=y,y=[];++v1)for(var r=1;r0&&u>c&&(u=c);for(var l=0;l=0?(p=y.substr(0,m),d=y.substr(m+1)):(p=y,d=""),h=decodeURIComponent(p),f=decodeURIComponent(d),o(s,h)?n(s[h])?s[h].push(f):s[h]=[s[h],f]:s[h]=f}return s};var n=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)}},{}],14:[function(e,t,r){"use strict";function o(e,t){if(e.map)return e.map(t);for(var r=[],o=0;o0)n.scope=r;else if("undefined"!=typeof r)throw new Error("the scope given to `copyIndex` was not an array with settings, synonyms or rules");return this._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(e)+"/operation",body:n,hostType:"write",callback:i})},o.prototype.getLogs=function(t,r,o){var n=e(26),i={};return"object"==typeof t?(i=n(t),o=r):0===arguments.length||"function"==typeof t?o=t:1===arguments.length||"function"==typeof r?(o=r,i.offset=t):(i.offset=t,i.length=r),void 0===i.offset&&(i.offset=0),void 0===i.length&&(i.length=10),this._jsonRequest({method:"GET",url:"/1/logs?"+this._getSearchParams(i,""),hostType:"read",callback:o})},o.prototype.listIndexes=function(e,t){var r="";return void 0===e||"function"==typeof e?t=e:r="?page="+e,this._jsonRequest({method:"GET",url:"/1/indexes"+r,hostType:"read",callback:t})},o.prototype.initIndex=function(e){return new i(this,e)},o.prototype.initAnalytics=function(t){var r=e(27);return r(this.applicationID,this.apiKey,t)},o.prototype.listUserKeys=s(function(e){return this.listApiKeys(e)},a("client.listUserKeys()","client.listApiKeys()")),o.prototype.listApiKeys=function(e){return this._jsonRequest({method:"GET",url:"/1/keys",hostType:"read",callback:e})},o.prototype.getUserKeyACL=s(function(e,t){return this.getApiKey(e,t)},a("client.getUserKeyACL()","client.getApiKey()")),o.prototype.getApiKey=function(e,t){return this._jsonRequest({method:"GET",url:"/1/keys/"+e,hostType:"read",callback:t})},o.prototype.deleteUserKey=s(function(e,t){return this.deleteApiKey(e,t)},a("client.deleteUserKey()","client.deleteApiKey()")),o.prototype.deleteApiKey=function(e,t){return this._jsonRequest({method:"DELETE",url:"/1/keys/"+e,hostType:"write",callback:t})},o.prototype.restoreApiKey=function(e,t){return this._jsonRequest({method:"POST",url:"/1/keys/"+e+"/restore",hostType:"write",callback:t})},o.prototype.addUserKey=s(function(e,t,r){return this.addApiKey(e,t,r)},a("client.addUserKey()","client.addApiKey()")),o.prototype.addApiKey=function(t,r,o){var n=e(8),i="Usage: client.addApiKey(arrayOfAcls[, params, callback])";if(!n(t))throw new Error(i);1!==arguments.length&&"function"!=typeof r||(o=r,r=null);var s={acl:t};return r&&(s.validity=r.validity,s.maxQueriesPerIPPerHour=r.maxQueriesPerIPPerHour,s.maxHitsPerQuery=r.maxHitsPerQuery,s.indexes=r.indexes,s.description=r.description,r.queryParameters&&(s.queryParameters=this._getSearchParams(r.queryParameters,"")),s.referers=r.referers),this._jsonRequest({method:"POST",url:"/1/keys",body:s,hostType:"write",callback:o})},o.prototype.addUserKeyWithValidity=s(function(e,t,r){return this.addApiKey(e,t,r)},a("client.addUserKeyWithValidity()","client.addApiKey()")),o.prototype.updateUserKey=s(function(e,t,r,o){return this.updateApiKey(e,t,r,o)},a("client.updateUserKey()","client.updateApiKey()")),o.prototype.updateApiKey=function(t,r,o,n){var i=e(8),s="Usage: client.updateApiKey(key, arrayOfAcls[, params, callback])";if(!i(r))throw new Error(s);2!==arguments.length&&"function"!=typeof o||(n=o,o=null);var a={acl:r};return o&&(a.validity=o.validity,a.maxQueriesPerIPPerHour=o.maxQueriesPerIPPerHour,a.maxHitsPerQuery=o.maxHitsPerQuery,a.indexes=o.indexes,a.description=o.description,o.queryParameters&&(a.queryParameters=this._getSearchParams(o.queryParameters,"")),a.referers=o.referers),this._jsonRequest({method:"PUT",url:"/1/keys/"+t,body:a,hostType:"write",callback:n})},o.prototype.startQueriesBatch=s(function(){this._batch=[]},a("client.startQueriesBatch()","client.search()")),o.prototype.addQueryInBatch=s(function(e,t,r){this._batch.push({indexName:e,query:t,params:r})},a("client.addQueryInBatch()","client.search()")),o.prototype.sendQueriesBatch=s(function(e){return this.search(this._batch,e)},a("client.sendQueriesBatch()","client.search()")),o.prototype.batch=function(t,r){var o=e(8),n="Usage: client.batch(operations[, callback])";if(!o(t))throw new Error(n);return this._jsonRequest({method:"POST",url:"/1/indexes/*/batch",body:{requests:t},hostType:"write",callback:r})},o.prototype.assignUserID=function(e,t){if(!e.userID||!e.cluster)throw new l.AlgoliaSearchError("You have to provide both a userID and cluster",e);return this._jsonRequest({method:"POST",url:"/1/clusters/mapping",hostType:"write",body:{cluster:e.cluster},callback:t,headers:{"x-algolia-user-id":e.userID}})},o.prototype.assignUserIDs=function(e,t){if(!e.userIDs||!e.cluster)throw new l.AlgoliaSearchError("You have to provide both an array of userIDs and cluster",e);return this._jsonRequest({method:"POST",url:"/1/clusters/mapping/batch",hostType:"write",body:{cluster:e.cluster,users:e.userIDs},callback:t})},o.prototype.getTopUserID=function(e){return this._jsonRequest({method:"GET",url:"/1/clusters/mapping/top",hostType:"read",callback:e})},o.prototype.getUserID=function(e,t){if(!e.userID)throw new l.AlgoliaSearchError("You have to provide a userID",{debugData:e});return this._jsonRequest({method:"GET",url:"/1/clusters/mapping/"+e.userID,hostType:"read",callback:t})},o.prototype.listClusters=function(e){return this._jsonRequest({method:"GET",url:"/1/clusters",hostType:"read",callback:e})},o.prototype.listUserIDs=function(e,t){return this._jsonRequest({method:"GET",url:"/1/clusters/mapping",body:e,hostType:"read",callback:t})},o.prototype.removeUserID=function(e,t){if(!e.userID)throw new l.AlgoliaSearchError("You have to provide a userID",{debugData:e});return this._jsonRequest({method:"DELETE",url:"/1/clusters/mapping",hostType:"write",callback:t,headers:{"x-algolia-user-id":e.userID}})},o.prototype.searchUserIDs=function(e,t){return this._jsonRequest({method:"POST",url:"/1/clusters/mapping/search",body:e,hostType:"read",callback:t})},o.prototype.setPersonalizationStrategy=function(e,t){return this._jsonRequest({method:"POST",url:"/1/recommendation/personalization/strategy",body:e,hostType:"write",callback:t})},o.prototype.getPersonalizationStrategy=function(e){return this._jsonRequest({method:"GET",url:"/1/recommendation/personalization/strategy",hostType:"read",callback:e})},o.prototype.destroy=n,o.prototype.enableRateLimitForward=n,o.prototype.disableRateLimitForward=n,o.prototype.useSecuredAPIKey=n,o.prototype.disableSecuredAPIKey=n,o.prototype.generateSecuredApiKey=n,o.prototype.getSecuredApiKeyRemainingValidity=n},{17:17,18:18,26:26,27:27,28:28,29:29,30:30,7:7,8:8}],17:[function(e,t,r){
+(function(r){function o(t,r,o){var i=e(1)("algoliasearch"),s=e(26),a=e(8),u=e(32),l="Usage: algoliasearch(applicationID, apiKey, opts)";if(o._allowEmptyCredentials!==!0&&!t)throw new c.AlgoliaSearchError("Please provide an application ID. "+l);if(o._allowEmptyCredentials!==!0&&!r)throw new c.AlgoliaSearchError("Please provide an API key. "+l);this.applicationID=t,this.apiKey=r,this.hosts={read:[],write:[]},o=o||{},this._timeouts=o.timeouts||{connect:1e3,read:2e3,write:3e4},o.timeout&&(this._timeouts.connect=this._timeouts.read=this._timeouts.write=o.timeout);var p=o.protocol||"https:";if(/:$/.test(p)||(p+=":"),"http:"!==p&&"https:"!==p)throw new c.AlgoliaSearchError("protocol must be `http:` or `https:` (was `"+o.protocol+"`)");if(this._checkAppIdData(),o.hosts)a(o.hosts)?(this.hosts.read=s(o.hosts),this.hosts.write=s(o.hosts)):(this.hosts.read=s(o.hosts.read),this.hosts.write=s(o.hosts.write));else{var d=u(this._shuffleResult,function(e){return t+"-"+e+".algolianet.com"}),h=(o.dsn===!1?"":"-dsn")+".algolia.net";this.hosts.read=[this.applicationID+h].concat(d),this.hosts.write=[this.applicationID+".algolia.net"].concat(d)}this.hosts.read=u(this.hosts.read,n(p)),this.hosts.write=u(this.hosts.write,n(p)),this.extraHeaders={},this.cache=o._cache||{},this._ua=o._ua,this._useCache=!(void 0!==o._useCache&&!o._cache)||o._useCache,this._useRequestCache=this._useCache&&o._useRequestCache,this._useFallback=void 0===o.useFallback||o.useFallback,this._setTimeout=o._setTimeout,i("init done, %j",this)}function n(e){return function(t){return e+"//"+t.toLowerCase()}}function i(e){if(void 0===Array.prototype.toJSON)return JSON.stringify(e);var t=Array.prototype.toJSON;delete Array.prototype.toJSON;var r=JSON.stringify(e);return Array.prototype.toJSON=t,r}function s(e){for(var t,r,o=e.length;0!==o;)r=Math.floor(Math.random()*o),o-=1,t=e[o],e[o]=e[r],e[r]=t;return e}function a(e){var t={};for(var r in e)if(Object.prototype.hasOwnProperty.call(e,r)){var o;o="x-algolia-api-key"===r||"x-algolia-application-id"===r?"**hidden for security purposes**":e[r],t[r]=o}return t}t.exports=o;var c=e(30),u=e(31),l=e(20),p=e(36),d=500,h=r.env.RESET_APP_DATA_TIMER&&parseInt(r.env.RESET_APP_DATA_TIMER,10)||12e4;o.prototype.initIndex=function(e){return new l(this,e)},o.prototype.setExtraHeader=function(e,t){this.extraHeaders[e.toLowerCase()]=t},o.prototype.getExtraHeader=function(e){return this.extraHeaders[e.toLowerCase()]},o.prototype.unsetExtraHeader=function(e){delete this.extraHeaders[e.toLowerCase()]},o.prototype.addAlgoliaAgent=function(e){var t="; "+e;this._ua.indexOf(t)===-1&&(this._ua+=t)},o.prototype._jsonRequest=function(t){function r(e,n){function u(e){var t=e&&e.body&&e.body.message&&e.body.status||e.statusCode||e&&e.body&&200;h("received response: statusCode: %s, computed statusCode: %d, headers: %j",e.statusCode,t,e.headers);var r=2===Math.floor(t/100),o=new Date;if(w.push({currentHost:A,headers:a(p),content:s||null,contentLength:void 0!==s?s.length:null,method:n.method,timeouts:n.timeouts,url:n.url,startTime:x,endTime:o,duration:o-x,statusCode:t}),r)return m._useCache&&!m._useRequestCache&&y&&(y[l]=e.responseText),{responseText:e.responseText,body:e.body};var i=4!==Math.floor(t/100);if(i)return v+=1,_();h("unrecoverable error");var u=new c.AlgoliaSearchError(e.body&&e.body.message,{debugData:w,statusCode:t});return m._promise.reject(u)}function d(e){h("error: %s, stack: %s",e.message,e.stack);var r=new Date;return w.push({currentHost:A,headers:a(p),content:s||null,contentLength:void 0!==s?s.length:null,method:n.method,timeouts:n.timeouts,url:n.url,startTime:x,endTime:r,duration:r-x}),e instanceof c.AlgoliaSearchError||(e=new c.Unknown(e&&e.message,e)),v+=1,e instanceof c.Unknown||e instanceof c.UnparsableJSON||v>=m.hosts[t.hostType].length&&(g||!b)?(e.debugData=w,m._promise.reject(e)):e instanceof c.RequestTimeout?T():_()}function _(){return h("retrying request"),m._incrementHostIndex(t.hostType),r(e,n)}function T(){return h("retrying request with higher timeout"),m._incrementHostIndex(t.hostType),m._incrementTimeoutMultipler(),n.timeouts=m._getTimeoutsForRequest(t.hostType),r(e,n)}m._checkAppIdData();var x=new Date;if(m._useCache&&!m._useRequestCache&&(l=t.url),m._useCache&&!m._useRequestCache&&s&&(l+="_body_"+n.body),o(!m._useRequestCache,y,l)){h("serving response from cache");var R=y[l];return m._promise.resolve({body:JSON.parse(R),responseText:R})}if(v>=m.hosts[t.hostType].length)return!b||g?(h("could not get any response"),m._promise.reject(new c.AlgoliaSearchError("Cannot connect to the AlgoliaSearch API. Send an email to support@algolia.com to report and resolve the issue. Application id was: "+m.applicationID,{debugData:w}))):(h("switching to fallback"),v=0,n.method=t.fallback.method,n.url=t.fallback.url,n.jsonBody=t.fallback.body,n.jsonBody&&(n.body=i(n.jsonBody)),p=m._computeRequestHeaders({additionalUA:f,headers:t.headers}),n.timeouts=m._getTimeoutsForRequest(t.hostType),m._setHostIndexByType(0,t.hostType),g=!0,r(m._request.fallback,n));var A=m._getHostByType(t.hostType),j=A+n.url,S={body:n.body,jsonBody:n.jsonBody,method:n.method,headers:p,timeouts:n.timeouts,debug:h,forceAuthHeaders:n.forceAuthHeaders};return h("method: %s, url: %s, headers: %j, timeouts: %d",S.method,j,S.headers,S.timeouts),e===m._request.fallback&&h("using fallback"),e.call(m,j,S).then(u,d)}function o(e,t,r){return m._useCache&&e&&t&&void 0!==t[r]}function n(e,r){return o(m._useRequestCache,y,l)&&e["catch"](function(){delete y[l]}),"function"!=typeof t.callback?e.then(r):void e.then(function(e){u(function(){t.callback(null,r(e))},m._setTimeout||setTimeout)},function(e){u(function(){t.callback(e)},m._setTimeout||setTimeout)})}this._checkAppIdData();var s,l,p,h=e(1)("algoliasearch:"+t.url),f=t.additionalUA||"",y=t.cache,m=this,v=0,g=!1,b=m._useFallback&&m._request.fallback&&t.fallback;this.apiKey.length>d&&void 0!==t.body&&(void 0!==t.body.params||void 0!==t.body.requests)?(t.body.apiKey=this.apiKey,p=this._computeRequestHeaders({additionalUA:f,withApiKey:!1,headers:t.headers})):p=this._computeRequestHeaders({additionalUA:f,headers:t.headers}),void 0!==t.body&&(s=i(t.body)),h("request start");var w=[];if(m._useCache&&m._useRequestCache&&(l=t.url),m._useCache&&m._useRequestCache&&s&&(l+="_body_"+s),o(m._useRequestCache,y,l)){h("serving request from cache");var _=y[l],T="function"!=typeof _.then?m._promise.resolve({responseText:_}):_;return n(T,function(e){return JSON.parse(e.responseText)})}var x=r(m._request,{url:t.url,method:t.method,body:s,jsonBody:t.body,timeouts:m._getTimeoutsForRequest(t.hostType),forceAuthHeaders:t.forceAuthHeaders});return m._useCache&&m._useRequestCache&&y&&(y[l]=x),n(x,function(e){return e.body})},o.prototype._getSearchParams=function(e,t){if(void 0===e||null===e)return t;for(var r in e)null!==r&&void 0!==e[r]&&e.hasOwnProperty(r)&&(t+=""===t?"":"&",t+=r+"="+encodeURIComponent("[object Array]"===Object.prototype.toString.call(e[r])?i(e[r]):e[r]));return t},o.prototype._computeRequestHeaders=function(t){var r=e(5),o=t.additionalUA?this._ua+"; "+t.additionalUA:this._ua,n={"x-algolia-agent":o,"x-algolia-application-id":this.applicationID};return t.withApiKey!==!1&&(n["x-algolia-api-key"]=this.apiKey),this.userToken&&(n["x-algolia-usertoken"]=this.userToken),this.securityTags&&(n["x-algolia-tagfilters"]=this.securityTags),r(this.extraHeaders,function(e,t){n[t]=e}),t.headers&&r(t.headers,function(e,t){n[t]=e}),n},o.prototype.search=function(t,r,o){var n=e(8),i=e(32),s="Usage: client.search(arrayOfQueries[, callback])";if(!n(t))throw new Error(s);"function"==typeof r?(o=r,r={}):void 0===r&&(r={});var a=this,c={requests:i(t,function(e){var t="";return void 0!==e.query&&(t+="query="+encodeURIComponent(e.query)),{indexName:e.indexName,params:a._getSearchParams(e.params,t)}})},u=i(c.requests,function(e,t){return t+"="+encodeURIComponent("/1/indexes/"+encodeURIComponent(e.indexName)+"?"+e.params)}).join("&"),l="/1/indexes/*/queries";return void 0!==r.strategy&&(c.strategy=r.strategy),this._jsonRequest({cache:this.cache,method:"POST",url:l,body:c,hostType:"read",fallback:{method:"GET",url:"/1/indexes/*",body:{params:u}},callback:o})},o.prototype.searchForFacetValues=function(t){var r=e(8),o=e(32),n="Usage: client.searchForFacetValues([{indexName, params: {facetName, facetQuery, ...params}}, ...queries])";if(!r(t))throw new Error(n);var i=this;return i._promise.all(o(t,function(t){if(!t||void 0===t.indexName||void 0===t.params.facetName||void 0===t.params.facetQuery)throw new Error(n);var r=e(26),o=e(34),s=t.indexName,a=t.params,c=a.facetName,u=o(r(a),function(e){return"facetName"===e}),l=i._getSearchParams(u,"");return i._jsonRequest({cache:i.cache,method:"POST",url:"/1/indexes/"+encodeURIComponent(s)+"/facets/"+encodeURIComponent(c)+"/query",hostType:"read",body:{params:l}})}))},o.prototype.setSecurityTags=function(e){if("[object Array]"===Object.prototype.toString.call(e)){for(var t=[],r=0;rh?this._resetInitialAppIdData(e):e},o.prototype._resetInitialAppIdData=function(e){var t=e||{};return t.hostIndexes={read:0,write:0},t.timeoutMultiplier=1,t.shuffleResult=t.shuffleResult||s([1,2,3]),this._setAppIdData(t)},o.prototype._cacheAppIdData=function(e){this._hostIndexes=e.hostIndexes,this._timeoutMultiplier=e.timeoutMultiplier,this._shuffleResult=e.shuffleResult},o.prototype._partialAppIdDataUpdate=function(t){var r=e(5),o=this._getAppIdData();return r(t,function(e,t){o[t]=e}),this._setAppIdData(o)},o.prototype._getHostByType=function(e){return this.hosts[e][this._getHostIndexByType(e)]},o.prototype._getTimeoutMultiplier=function(){return this._timeoutMultiplier},o.prototype._getHostIndexByType=function(e){return this._hostIndexes[e]},o.prototype._setHostIndexByType=function(t,r){var o=e(26),n=o(this._hostIndexes);return n[r]=t,this._partialAppIdDataUpdate({hostIndexes:n}),t},o.prototype._incrementHostIndex=function(e){return this._setHostIndexByType((this._getHostIndexByType(e)+1)%this.hosts[e].length,e)},o.prototype._incrementTimeoutMultipler=function(){var e=Math.max(this._timeoutMultiplier+1,4);return this._partialAppIdDataUpdate({timeoutMultiplier:e})},o.prototype._getTimeoutsForRequest=function(e){return{connect:this._timeouts.connect*this._timeoutMultiplier,complete:this._timeouts[e]*this._timeoutMultiplier}}}).call(this,e(12))},{1:1,12:12,20:20,26:26,30:30,31:31,32:32,34:34,36:36,5:5,8:8}],18:[function(e,t,r){function o(){s.apply(this,arguments)}function n(e,t,r){function o(r,n){var i={page:r||0,hitsPerPage:t||100},s=n||[];return e(i).then(function(e){var t=e.hits,r=e.nbHits,n=t.map(function(e){return delete e._highlightResult,e}),a=s.concat(n);return a.lengths&&(t=s),"published"!==e.status?l._promise.delay(t).then(r):e})}function o(e){u(function(){t(null,e)},l._setTimeout||setTimeout)}function n(e){u(function(){t(e)},l._setTimeout||setTimeout)}var i=100,s=5e3,a=0,c=this,l=c.as,p=r();return t?void p.then(o,n):p},o.prototype.clearIndex=function(e){var t=this;return this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(t.indexName)+"/clear",hostType:"write",callback:e})},o.prototype.getSettings=function(e,t){1===arguments.length&&"function"==typeof e&&(t=e,e={}),e=e||{};var r=encodeURIComponent(this.indexName);return this.as._jsonRequest({method:"GET",url:"/1/indexes/"+r+"/settings?getVersion=2"+(e.advanced?"&advanced="+e.advanced:""),hostType:"read",callback:t})},o.prototype.searchSynonyms=function(e,t){return"function"==typeof e?(t=e,e={}):void 0===e&&(e={}),this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/synonyms/search",body:e,hostType:"read",callback:t})},o.prototype.exportSynonyms=function(e,t){return n(this.searchSynonyms.bind(this),e,t)},o.prototype.saveSynonym=function(e,t,r){"function"==typeof t?(r=t,t={}):void 0===t&&(t={}),void 0!==t.forwardToSlaves&&p();var o=t.forwardToSlaves||t.forwardToReplicas?"true":"false";return this.as._jsonRequest({method:"PUT",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/synonyms/"+encodeURIComponent(e.objectID)+"?forwardToReplicas="+o,body:e,hostType:"write",callback:r})},o.prototype.getSynonym=function(e,t){return this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/synonyms/"+encodeURIComponent(e),hostType:"read",callback:t})},o.prototype.deleteSynonym=function(e,t,r){"function"==typeof t?(r=t,t={}):void 0===t&&(t={}),void 0!==t.forwardToSlaves&&p();var o=t.forwardToSlaves||t.forwardToReplicas?"true":"false";return this.as._jsonRequest({method:"DELETE",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/synonyms/"+encodeURIComponent(e)+"?forwardToReplicas="+o,hostType:"write",callback:r})},o.prototype.clearSynonyms=function(e,t){"function"==typeof e?(t=e,e={}):void 0===e&&(e={}),void 0!==e.forwardToSlaves&&p();var r=e.forwardToSlaves||e.forwardToReplicas?"true":"false";return this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/synonyms/clear?forwardToReplicas="+r,hostType:"write",callback:t})},o.prototype.batchSynonyms=function(e,t,r){"function"==typeof t?(r=t,t={}):void 0===t&&(t={}),void 0!==t.forwardToSlaves&&p();var o=t.forwardToSlaves||t.forwardToReplicas?"true":"false";return this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/synonyms/batch?forwardToReplicas="+o+"&replaceExistingSynonyms="+(t.replaceExistingSynonyms?"true":"false"),hostType:"write",body:e,callback:r})},o.prototype.searchRules=function(e,t){return"function"==typeof e?(t=e,e={}):void 0===e&&(e={}),this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/rules/search",body:e,hostType:"read",callback:t})},o.prototype.exportRules=function(e,t){return n(this.searchRules.bind(this),e,t)},o.prototype.saveRule=function(e,t,r){if("function"==typeof t?(r=t,t={}):void 0===t&&(t={}),!e.objectID)throw new l.AlgoliaSearchError("Missing or empty objectID field for rule");var o=t.forwardToReplicas===!0?"true":"false";return this.as._jsonRequest({method:"PUT",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/rules/"+encodeURIComponent(e.objectID)+"?forwardToReplicas="+o,body:e,hostType:"write",callback:r})},o.prototype.getRule=function(e,t){return this.as._jsonRequest({method:"GET",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/rules/"+encodeURIComponent(e),hostType:"read",callback:t})},o.prototype.deleteRule=function(e,t,r){"function"==typeof t?(r=t,t={}):void 0===t&&(t={});var o=t.forwardToReplicas===!0?"true":"false";return this.as._jsonRequest({method:"DELETE",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/rules/"+encodeURIComponent(e)+"?forwardToReplicas="+o,hostType:"write",callback:r})},o.prototype.clearRules=function(e,t){"function"==typeof e?(t=e,e={}):void 0===e&&(e={});var r=e.forwardToReplicas===!0?"true":"false";return this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/rules/clear?forwardToReplicas="+r,hostType:"write",callback:t})},o.prototype.batchRules=function(e,t,r){"function"==typeof t?(r=t,t={}):void 0===t&&(t={});var o=t.forwardToReplicas===!0?"true":"false";return this.as._jsonRequest({method:"POST",url:"/1/indexes/"+encodeURIComponent(this.indexName)+"/rules/batch?forwardToReplicas="+o+"&clearExistingRules="+(t.clearExistingRules===!0?"true":"false"),hostType:"write",body:e,callback:r})},o.prototype.exists=function(e){var t=this.getSettings().then(function(){return!0})["catch"](function(e){if(e instanceof l.AlgoliaSearchError&&404===e.statusCode)return!1;throw e});return"function"!=typeof e?t:void t.then(function(t){e(null,t)})["catch"](function(t){e(t)})},o.prototype.findObject=function(e,t,r){t=void 0===t?{}:t;var o=void 0===t.paginate||t.paginate,n=void 0!==t.query?t.query:"",i=this,s=0,a=function(){return t.page=s,i.search(n,t).then(function(t){for(var r=t.hits,n=0;n=t.nbPages)throw new l.ObjectNotFound("Object not found");return a()})},c=a(s);return void 0===r?c:void c.then(function(e){r(null,e)})["catch"](function(e){r(e)})},o.prototype.getObjectPosition=function(e,t){for(var r=e.hits,o=0;o1&&a()}if(!h.cors&&!h.hasXDomainRequest)return void o(new u.Network("CORS not supported"));e=l(e,t.headers);var d,f,y=t.body,m=h.cors?new XMLHttpRequest:new XDomainRequest,v=!1;d=setTimeout(s,t.timeouts.connect),m.onprogress=c,"onreadystatechange"in m&&(m.onreadystatechange=p),m.onload=n,m.onerror=i,m instanceof XMLHttpRequest?(m.open(t.method,e,!0),t.forceAuthHeaders&&(m.setRequestHeader("x-algolia-application-id",t.headers["x-algolia-application-id"]),m.setRequestHeader("x-algolia-api-key",t.headers["x-algolia-api-key"]))):m.open(t.method,e),h.cors&&(y&&("POST"===t.method?m.setRequestHeader("content-type","application/x-www-form-urlencoded"):m.setRequestHeader("content-type","application/json")),m.setRequestHeader("accept","application/json")),y?m.send(y):m.send()})},a.prototype._request.fallback=function(e,t){return e=l(e,t.headers),new n(function(r,o){p(e,t,function(e,t){return e?void o(e):void r(t)})})},a.prototype._promise={reject:function(e){return n.reject(e)},resolve:function(e){return n.resolve(e)},delay:function(e){return new n(function(t){setTimeout(t,e)})},all:function(e){return n.all(e)}},s}}).call(this,e(12))},{1:1,12:12,23:23,24:24,26:26,3:3,30:30,35:35,37:37,6:6,7:7}],23:[function(e,t,r){"use strict";function o(e,t){return e+=/\?/.test(e)?"&":"?",e+n(t)}t.exports=o;var n=e(14)},{14:14}],24:[function(e,t,r){"use strict";function o(e,t,r){function o(){t.debug("JSONP: success"),m||d||(m=!0,p||(t.debug("JSONP: Fail. Script loaded but did not call the callback"),
+a(),r(new n.JSONPScriptFail)))}function s(){"loaded"!==this.readyState&&"complete"!==this.readyState||o()}function a(){clearTimeout(v),f.onload=null,f.onreadystatechange=null,f.onerror=null,h.removeChild(f)}function c(){try{delete window[y],delete window[y+"_loaded"]}catch(e){window[y]=window[y+"_loaded"]=void 0}}function u(){t.debug("JSONP: Script timeout"),d=!0,a(),r(new n.RequestTimeout)}function l(){t.debug("JSONP: Script error"),m||d||(a(),r(new n.JSONPScriptError))}if("GET"!==t.method)return void r(new Error("Method "+t.method+" "+e+" is not supported by JSONP."));t.debug("JSONP: start");var p=!1,d=!1;i+=1;var h=document.getElementsByTagName("head")[0],f=document.createElement("script"),y="algoliaJSONP_"+i,m=!1;window[y]=function(e){return c(),d?void t.debug("JSONP: Late answer, ignoring"):(p=!0,a(),void r(null,{body:e,responseText:JSON.stringify(e)}))},e+="&callback="+y,t.jsonBody&&t.jsonBody.params&&(e+="&"+t.jsonBody.params);var v=setTimeout(u,t.timeouts.complete);f.onreadystatechange=s,f.onload=o,f.onerror=l,f.async=!0,f.defer=!0,f.src=e,h.appendChild(f)}t.exports=o;var n=e(30),i=0},{30:30}],25:[function(e,t,r){function o(e,t){return function(r,o,i){if("function"==typeof r&&"object"==typeof o||"object"==typeof i)throw new n.AlgoliaSearchError("index.search usage is index.search(query, params, cb)");0===arguments.length||"function"==typeof r?(i=r,r=""):1!==arguments.length&&"function"!=typeof o||(i=o,o=void 0),"object"==typeof r&&null!==r?(o=r,r=void 0):void 0!==r&&null!==r||(r="");var s="";void 0!==r&&(s+=e+"="+encodeURIComponent(r));var a;return void 0!==o&&(o.additionalUA&&(a=o.additionalUA,delete o.additionalUA),s=this.as._getSearchParams(o,s)),this._search(s,t,i,a)}}t.exports=o;var n=e(30)},{30:30}],26:[function(e,t,r){t.exports=function(e){return JSON.parse(JSON.stringify(e))}},{}],27:[function(e,t,r){function o(e,t,r){var o={};return r=r||{},r.hosts=r.hosts||["analytics.algolia.com","analytics.algolia.com","analytics.algolia.com","analytics.algolia.com"],r.protocol=r.protocol||"https:",o.as=n(e,t,r),o.getABTests=function(e,t){var r=r||{},o=r.offset||0,n=r.limit||10;return this.as._jsonRequest({method:"GET",url:"/2/abtests?offset="+encodeURIComponent(o)+"&limit="+encodeURIComponent(n),hostType:"read",forceAuthHeaders:!0,callback:t})},o.getABTest=function(e,t){return this.as._jsonRequest({method:"GET",url:"/2/abtests/"+encodeURIComponent(e),hostType:"read",forceAuthHeaders:!0,callback:t})},o.addABTest=function(e,t){return this.as._jsonRequest({method:"POST",url:"/2/abtests",body:e,hostType:"read",forceAuthHeaders:!0,callback:t})},o.stopABTest=function(e,t){return this.as._jsonRequest({method:"POST",url:"/2/abtests/"+encodeURIComponent(e)+"/stop",hostType:"read",forceAuthHeaders:!0,callback:t})},o.deleteABTest=function(e,t){return this.as._jsonRequest({method:"DELETE",url:"/2/abtests/"+encodeURIComponent(e),hostType:"write",forceAuthHeaders:!0,callback:t})},o.waitTask=function(e,t,r){return this.as.initIndex(e).waitTask(t,r)},o}t.exports=o;var n=e(21)},{21:21}],28:[function(e,t,r){t.exports=function(e,t){function r(){return o||(console.warn(t),o=!0),e.apply(this,arguments)}var o=!1;return r}},{}],29:[function(e,t,r){t.exports=function(e,t){var r=e.toLowerCase().replace(/[\.\(\)]/g,"");return"algoliasearch: `"+e+"` was replaced by `"+t+"`. Please see https://github.com/algolia/algoliasearch-client-javascript/wiki/Deprecated#"+r}},{}],30:[function(e,t,r){"use strict";function o(t,r){var o=e(5),n=this;"function"==typeof Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):n.stack=(new Error).stack||"Cannot get a stacktrace, browser is too old",this.name="AlgoliaSearchError",this.message=t||"Unknown error",r&&o(r,function(e,t){n[t]=e})}function n(e,t){function r(){var r=Array.prototype.slice.call(arguments,0);"string"!=typeof r[0]&&r.unshift(t),o.apply(this,r),this.name="AlgoliaSearch"+e+"Error"}return i(r,o),r}var i=e(7);i(o,Error),t.exports={AlgoliaSearchError:o,UnparsableJSON:n("UnparsableJSON","Could not parse the incoming response as JSON, see err.more for details"),RequestTimeout:n("RequestTimeout","Request timed out before getting a response"),Network:n("Network","Network issue, see err.more for details"),JSONPScriptFail:n("JSONPScriptFail","
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
更多 分类 - Github