summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md5
-rw-r--r--NOTICE.txt37
-rw-r--r--api/command.go30
-rw-r--r--config/config.json2
-rw-r--r--doc/install/SMTP-Email-Setup.md12
-rw-r--r--doc/integrations/Single-Sign-On/Gitlab.md2
-rw-r--r--doc/process/documentation-guidelines.md185
-rw-r--r--doc/usage/Markdown.md2
-rw-r--r--model/outgoing_webhook.go6
-rw-r--r--model/outgoing_webhook_test.go5
-rw-r--r--model/team.go1
-rw-r--r--model/utils.go13
-rw-r--r--store/sql_post_store.go7
-rw-r--r--store/sql_post_store_test.go12
-rw-r--r--store/sql_team_store.go2
-rw-r--r--utils/textgeneration.go237
-rw-r--r--web/react/components/admin_console/admin_controller.jsx4
-rw-r--r--web/react/components/admin_console/admin_sidebar.jsx6
-rw-r--r--web/react/components/center_panel.jsx84
-rw-r--r--web/react/components/channel_header.jsx7
-rw-r--r--web/react/components/channel_view.jsx43
-rw-r--r--web/react/components/create_post.jsx63
-rw-r--r--web/react/components/edit_post_modal.jsx2
-rw-r--r--web/react/components/login.jsx2
-rw-r--r--web/react/components/navbar_dropdown.jsx2
-rw-r--r--web/react/components/post.jsx4
-rw-r--r--web/react/components/post_body.jsx13
-rw-r--r--web/react/components/post_info.jsx18
-rw-r--r--web/react/components/post_list.jsx764
-rw-r--r--web/react/components/post_list_container.jsx63
-rw-r--r--web/react/components/posts_view.jsx303
-rw-r--r--web/react/components/posts_view_container.jsx267
-rw-r--r--web/react/components/rhs_thread.jsx10
-rw-r--r--web/react/components/search_autocomplete.jsx5
-rw-r--r--web/react/components/search_bar.jsx15
-rw-r--r--web/react/components/search_results.jsx11
-rw-r--r--web/react/components/settings_sidebar.jsx4
-rw-r--r--web/react/components/sidebar.jsx94
-rw-r--r--web/react/components/sidebar_header.jsx69
-rw-r--r--web/react/components/sidebar_right.jsx61
-rw-r--r--web/react/components/signup_team.jsx100
-rw-r--r--web/react/components/team_general_tab.jsx61
-rw-r--r--web/react/components/time_since.jsx50
-rw-r--r--web/react/components/tutorial/tutorial_intro_screens.jsx161
-rw-r--r--web/react/components/tutorial/tutorial_tip.jsx131
-rw-r--r--web/react/components/user_settings/manage_outgoing_hooks.jsx39
-rw-r--r--web/react/components/user_settings/user_settings_display.jsx100
-rw-r--r--web/react/components/user_settings/user_settings_integrations.jsx2
-rw-r--r--web/react/pages/channel.jsx93
-rw-r--r--web/react/stores/post_store.jsx43
-rw-r--r--web/react/utils/channel_intro_mssages.jsx218
-rw-r--r--web/react/utils/constants.jsx14
-rw-r--r--web/react/utils/markdown.jsx15
-rw-r--r--web/react/utils/utils.jsx45
-rw-r--r--web/sass-files/sass/partials/_base.scss41
-rw-r--r--web/sass-files/sass/partials/_headers.scss4
-rw-r--r--web/sass-files/sass/partials/_markdown.scss4
-rw-r--r--web/sass-files/sass/partials/_post.scss3
-rw-r--r--web/sass-files/sass/partials/_responsive.scss32
-rw-r--r--web/sass-files/sass/partials/_settings.scss4
-rw-r--r--web/sass-files/sass/partials/_sidebar--right.scss19
-rw-r--r--web/sass-files/sass/partials/_signup.scss2
-rw-r--r--web/sass-files/sass/partials/_tutorial.scss189
-rw-r--r--web/sass-files/sass/partials/_videos.scss5
-rw-r--r--web/sass-files/sass/styles.scss1
-rw-r--r--web/static/images/tutorialTip.gifbin0 -> 18421 bytes
-rw-r--r--web/static/js/babel-es6-polyfill.js2591
-rw-r--r--web/static/js/babel-es6-polyfill.min.js2
-rw-r--r--web/templates/channel.html19
-rw-r--r--web/templates/head.html16
-rw-r--r--web/web.go2
71 files changed, 5287 insertions, 1191 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 46b74ccb3..0bf805e7d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -45,6 +45,11 @@ Team Settings
- Added Team Settings option to include link to given team on root page
- Ability to rotate invite code for invite URL
+Extras
+
+- Added `/shrug KEYWORD` command to output: `¯\_(ツ)_/¯ KEYWORD`
+- Added `/me KEYWORD` command to output: _`KEYWORD`_
+
System Console
- New statistics page
diff --git a/NOTICE.txt b/NOTICE.txt
index cc9e35af8..c43fc2d22 100644
--- a/NOTICE.txt
+++ b/NOTICE.txt
@@ -921,3 +921,40 @@ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+
+---
+
+This product contains a modified portion of 'highlight.js', a syntax highlighter for the web.
+
+by Ivan Sagalaev
+
+* HOMEPAGE:
+ * https://github.com/isagalaev/highlight.js
+
+* LICENSE:
+
+Copyright (c) 2006, Ivan Sagalaev
+All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of highlight.js nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/api/command.go b/api/command.go
index e22a05a5f..50ca41155 100644
--- a/api/command.go
+++ b/api/command.go
@@ -24,6 +24,7 @@ var (
"loadTestCommand": "/loadtest",
"echoCommand": "/echo",
"shrugCommand": "/shrug",
+ "meCommand": "/me",
}
commands = []commandHandler{
logoutCommand,
@@ -31,6 +32,7 @@ var (
loadTestCommand,
echoCommand,
shrugCommand,
+ meCommand,
}
commandNotImplementedErr = model.NewAppError("checkCommand", "Command not implemented", "")
)
@@ -194,6 +196,34 @@ func echoCommand(c *Context, command *model.Command) bool {
return false
}
+func meCommand(c *Context, command *model.Command) bool {
+ cmd := cmds["meCommand"]
+
+ if !command.Suggest && strings.Index(command.Command, cmd) == 0 {
+ message := ""
+
+ parameters := strings.SplitN(command.Command, " ", 2)
+ if len(parameters) > 1 {
+ message += "*" + parameters[1] + "*"
+ }
+
+ post := &model.Post{}
+ post.Message = message
+ post.ChannelId = command.ChannelId
+ if _, err := CreatePost(c, post, false); err != nil {
+ l4g.Error("Unable to create /me post post, err=%v", err)
+ return false
+ }
+ command.Response = model.RESP_EXECUTED
+ return true
+
+ } else if strings.Index(cmd, command.Command) == 0 {
+ command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: "Do an action, /me [message]"})
+ }
+
+ return false
+}
+
func shrugCommand(c *Context, command *model.Command) bool {
cmd := cmds["shrugCommand"]
diff --git a/config/config.json b/config/config.json
index a927620b5..2738546c0 100644
--- a/config/config.json
+++ b/config/config.json
@@ -92,4 +92,4 @@
"TokenEndpoint": "",
"UserApiEndpoint": ""
}
-} \ No newline at end of file
+}
diff --git a/doc/install/SMTP-Email-Setup.md b/doc/install/SMTP-Email-Setup.md
index bb57d95ba..7d9beae89 100644
--- a/doc/install/SMTP-Email-Setup.md
+++ b/doc/install/SMTP-Email-Setup.md
@@ -52,10 +52,18 @@ To enable email, configure an SMTP email service as follows:
* Set **Connection Security** to **(empty)**
##### Gmail
-* Information needed
+* Set **SMTP Username** to **your_email@gmail.com**
+* Set **SMTP Password** to **your_password**
+* Set **SMTP Server** to **smtp.gmail.com**
+* Set **SMTP Port** to **587**
+* Set **Connection Security** to **TLS**
##### Office 365
-* Information needed
+* Set **SMTP Username** to **Office 365 username**
+* Set **SMTP Password** to **Office 365 password**
+* Set **SMTP Server** to **smtp.office365.com**
+* Set **SMTP Port** to **587**
+* Set **Connection Security** to **TLS**
##### Hotmail
* Set **SMTP Username** to **your_email@hotmail.com**
diff --git a/doc/integrations/Single-Sign-On/Gitlab.md b/doc/integrations/Single-Sign-On/Gitlab.md
index 7939c47fb..1242fd13e 100644
--- a/doc/integrations/Single-Sign-On/Gitlab.md
+++ b/doc/integrations/Single-Sign-On/Gitlab.md
@@ -11,7 +11,7 @@ Follow these steps to configure Mattermost to use GitLab as a single-sign-on (SS
3. Submit the application and copy the given _Id_ and _Secret_ into the appropriate _SSOSettings_ fields in config/config.json
-4. Also in config/config.json, set _Allow_ to `true` for the _gitlab_ section, leave _Scope_ blank and use the following for the endpoints:
+4. Also in config/config.json, set _Enable_ to `true` for the _gitlab_ section, leave _Scope_ blank and use the following for the endpoints:
* _AuthEndpoint_: `https://<your-gitlab-url>/oauth/authorize` (example https://example.com/oauth/authorize)
* _TokenEndpoint_: `https://<your-gitlab-url>/oauth/token`
* _UserApiEndpoint_: `https://<your-gitlab-url>/api/v3/user`
diff --git a/doc/process/documentation-guidelines.md b/doc/process/documentation-guidelines.md
new file mode 100644
index 000000000..f37f0c5fc
--- /dev/null
+++ b/doc/process/documentation-guidelines.md
@@ -0,0 +1,185 @@
+# Documentation Conventions
+
+The most important thing about documentation is getting it done and out to the community.
+
+After that, we can work on upgrading the quality of documentation. The below chart summarizes the different levels of documentation and how the quality gates are applied.
+
+_Note: Documentation Guidelines are new, and iterating. Documentation has started to balloon and this is our attempt at reducing ambiguity and increasing consistency, but the conventions here are very open to discussion._
+
+| Stars | Benchmark | Timeline |
+|:-------------|:--------------------------------|:--------------------------------|
+| 1 | Documentation is correct. | First draft checked in by developer. Okay to ship in first release of new feature. |
+| 2 | Documentation a) follows all objective formatting criteria, b) is tested by someone other than the author, c) satisfies above. | First edit under objective rules. Required before second release cycle with this feature included. |
+| 3 | Documentation a) follows all subjective style criteria, b) is reviewed and edited by someone who has previously authored 3-star documentation, and c) satisfies above. | Second edit under subjective rules. Required before third release cycle with this feature included |
+| 4 | Documentation a) has received at least 1 edit due to user feedback, b) has received at least one unprompted compliment from user community on quality, c) satisfies above. | Additional edits to refine documentation based on user feedback |
+
+## 1-Star Requirements: Correctness
+
+### List precise dependencies
+
+1. Be explicit about what specific dependencies have been tested as part of an installation procedure.
+2. Be explicit about assumptions of compatibility on systems that have not been tested.
+3. Do not claim the system works on later versions of a platform if backwards compatibility is not a priority for the dependency (It's okay to say Chrome version 43 and higher, but not Python 2.6 and higher, because Python 3.0 is explicitly incompatible with previous versions).
+
+#### Correct
+
+----
+This procedure works on an Ubuntu 14.04 server with Python 2.6 installed and should work on compatible Linux-based operating systems and compatible versions of Python.
+
+----
+#### Incorrect
+
+----
+This procedure works on Linux servers running Python.
+
+also:
+
+This procedure works on Linux servers running Python 2.6 and higher.
+
+----
+## 2-Star Requirements: Objective Formatting Checklist
+
+### Use headings
+
+Headings in markdown provide anchors that can be used to easily reference sub-sections of long pieces of documentation. This is preferrable to just numbering sections without headings.
+
+##### Correct:
+
+----
+##### Step 1: Add a heading
+This makes things easier to reference via hyperlinks
+##### Step 2: Link to headings
+So things are eariser to find
+
+----
+##### Incorrect:
+
+----
+**Step 1: Add a heading**
+This makes things easier to reference via hyperlinks
+**Step 2: Link to headings**
+So things are eariser to find
+----
+
+### Use appropriate heading case
+
+Cases in headings may vary depending on usage.
+
+#### When to use Title Case
+
+H1, H2, H3 headings should be "Title Case" and less than four words, except if a colon is used, then four words per segment separated by the colon.
+
+These large headings are typically shorter and help with navigating large documents
+
+#### When to use sentence case
+
+H3, H4, H5 headings should be "Sentence case" and can be any length.
+
+These headers are smaller and used to summarize sections. H3 can be considered either a large or small heading.
+
+These conventions are new, so there's flexibility around them, when you're not sure, consider the convention here as default.
+
+### One instruction per line
+
+It's easy to miss instructions when they're compounded. Have only one instruction per line, so documentation looks more like a checklist.
+
+A support person should be able to say "Did you complete step 7?" instead of "Did you complete the second part of step 7 after doing XXX?"
+
+##### Correct:
+
+----
+
+6. For **Predefined configuration** look under **Generic** and select **Docker**.
+ 7. For **Environment type** select **Single instance**
+
+----
+
+##### Incorrect:
+
+----
+
+6. For **Predefined configuration** look under **Generic** and select **Docker**. For **Environment type** select **Single instance**
+
+----
+
+### Lists end without periods
+
+Sentences within bullet points or numbered lists should end in normal punctuation. The sentence or fragment at the end of a bullet point should not have a period.
+
+##### Correct
+
+----
+- This is a sentence within a bullet point. This is the end of a bullet point without a period
+
+----
+##### Incorrect
+
+----
+- This is an incorrect ending of a bullet point with a period.
+
+----
+### Avoid Passive Phrases
+
+Examples of passive phrases include "have", "had", "was", "can be", "has been" and documentation is shorter and clearer without them.
+
+##### Correct
+
+----
+This software **runs** on any server that supports Python.
+
+----
+##### Incorrect
+
+----
+This software **can be run** on any server that supports Python.
+
+----
+## 3-Star Requirements: Subjective Style Guidelines
+
+### Be Concise
+
+Try to use fewer words when possible.
+
+##### Correct:
+
+----
+This integration posts [issue](http://doc.gitlab.com/ee/web_hooks/web_hooks.html#issues-events), [comment](http://doc.gitlab.com/ee/web_hooks/web_hooks.html#comment-events) and [merge request](http://doc.gitlab.com/ee/web_hooks/web_hooks.html#merge-request-events) events from a GitLab repository into specific Mattermost channels by formatting output from [GitLab's outgoing webhooks](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/web_hooks/web_hooks.md) to [Mattermost's incoming webhooks](https://github.com/mattermost/platform/blob/master/doc/integrations/webhooks/Incoming-Webhooks.md).
+
+----
+##### Incorrect:
+
+----
+This integration makes use of GitLab's outgoing webhooks and Mattermost's incoming webhooks to post GitLab events into Mattermost. You can find GitLab's outgoing webhooks described [here](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/web_hooks/web_hooks.md) and Mattermost's incoming webhooks described [here](https://github.com/mattermost/platform/blob/master/doc/integrations/webhooks/Incoming-Webhooks.md).
+
+----
+
+### Use appropriate emphasis
+
+Mention Clickable Controls in **Bold**, Sections and Setting Names in *Italics*, and Key Strokes in `pre-formatted text`.
+
+To make it clear and consistent across documentation on how we describe controls that a user is asked to manipulate, we have a number of guidelines:
+
+**Bold**
+- Please **bold** the names of controls you're asking users to click. The text that is bolded should match the label of the control in the user interface. Do not format these references with _italics_, ALL-CAPS or `pre-formatted text`.
+- Use `>` to express a series of clicks, for example clicking on **Button One** > **Button Two** > **Button Three**.
+- If a button might be difficult to find, give a hint about its location _before_ mentioning the name of the control (this helps people find the hint before they start searching, if the see the name of the button first, they might not continue reading to find the hint before starting to look).
+
+***Italics***
+- Please *italicize* setting names or section headings that identify that the user is looking in the correct area. The text that is italicized should match the name of the setting or section in the user interface.
+- It is helpful to use italics to guide the user to the correct area before mentioning a clickable action in bold.
+
+**`pre-formatted text`**
+- Please use `pre-formatted text` to identify when a user must enter key strokes or paste text into an input box.
+
+#### Correct
+
+----
+Type `mattermost-integration-giphy` in the *repo-name* field, then click **Search** and then the **Connect** button once Heroku finds your repository
+
+----
+#### Incorrect
+
+----
+Type "mattermost-integration-giphy" in the **repo-name** field, then click Search and then the *Connect* button once Heroku finds your repository
+
+----
diff --git a/doc/usage/Markdown.md b/doc/usage/Markdown.md
index 9e2342a0b..055f47619 100644
--- a/doc/usage/Markdown.md
+++ b/doc/usage/Markdown.md
@@ -26,7 +26,7 @@ Renders as:
code block
```
-Create in-line monospaced font by surrounding it with back spaces.
+Create in-line monospaced font by surrounding it with backticks.
```
`monospace`
```
diff --git a/model/outgoing_webhook.go b/model/outgoing_webhook.go
index 8958dd5b0..9a1b89a85 100644
--- a/model/outgoing_webhook.go
+++ b/model/outgoing_webhook.go
@@ -100,6 +100,12 @@ func (o *OutgoingWebhook) IsValid() *AppError {
return NewAppError("OutgoingWebhook.IsValid", "Invalid callback urls", "")
}
+ for _, callback := range o.CallbackURLs {
+ if !IsValidHttpUrl(callback) {
+ return NewAppError("OutgoingWebhook.IsValid", "Invalid callback URLs. Each must be a valid URL and start with http:// or https://", "")
+ }
+ }
+
return nil
}
diff --git a/model/outgoing_webhook_test.go b/model/outgoing_webhook_test.go
index 2ca48c291..0d1cd773e 100644
--- a/model/outgoing_webhook_test.go
+++ b/model/outgoing_webhook_test.go
@@ -80,6 +80,11 @@ func TestOutgoingWebhookIsValid(t *testing.T) {
t.Fatal("should be invalid")
}
+ o.CallbackURLs = []string{"nowhere.com/"}
+ if err := o.IsValid(); err == nil {
+ t.Fatal("should be invalid")
+ }
+
o.CallbackURLs = []string{"http://nowhere.com/"}
if err := o.IsValid(); err != nil {
t.Fatal(err)
diff --git a/model/team.go b/model/team.go
index 4d14ec2ee..5c9cf5a26 100644
--- a/model/team.go
+++ b/model/team.go
@@ -229,6 +229,5 @@ func (o *Team) PreExport() {
func (o *Team) Sanitize() {
o.Email = ""
- o.Type = ""
o.AllowedDomains = ""
}
diff --git a/model/utils.go b/model/utils.go
index bb0669df7..681ade870 100644
--- a/model/utils.go
+++ b/model/utils.go
@@ -11,6 +11,7 @@ import (
"fmt"
"io"
"net/mail"
+ "net/url"
"regexp"
"strings"
"time"
@@ -301,3 +302,15 @@ var UrlRegex = regexp.MustCompile(`^((?:[a-z]+:\/\/)?(?:(?:[a-z0-9\-]+\.)+(?:[a-
var PartialUrlRegex = regexp.MustCompile(`/([A-Za-z0-9]{26})/([A-Za-z0-9]{26})/((?:[A-Za-z0-9]{26})?.+(?:\.[A-Za-z0-9]{3,})?)`)
var SplitRunes = map[rune]bool{',': true, ' ': true, '.': true, '!': true, '?': true, ':': true, ';': true, '\n': true, '<': true, '>': true, '(': true, ')': true, '{': true, '}': true, '[': true, ']': true, '+': true, '/': true, '\\': true}
+
+func IsValidHttpUrl(rawUrl string) bool {
+ if strings.Index(rawUrl, "http://") != 0 && strings.Index(rawUrl, "https://") != 0 {
+ return false
+ }
+
+ if _, err := url.ParseRequestURI(rawUrl); err != nil {
+ return false
+ }
+
+ return true
+}
diff --git a/store/sql_post_store.go b/store/sql_post_store.go
index 8b1cae3dd..fdae20f60 100644
--- a/store/sql_post_store.go
+++ b/store/sql_post_store.go
@@ -541,13 +541,6 @@ func (s SqlPostStore) Search(teamId string, userId string, params *model.SearchP
var posts []*model.Post
- if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_POSTGRES {
- // Parse text for wildcards
- if wildcard, err := regexp.Compile("\\*($| )"); err == nil {
- terms = wildcard.ReplaceAllLiteralString(terms, "* ")
- }
- }
-
searchQuery := `
SELECT
*
diff --git a/store/sql_post_store_test.go b/store/sql_post_store_test.go
index c2348b04c..fe7195a54 100644
--- a/store/sql_post_store_test.go
+++ b/store/sql_post_store_test.go
@@ -631,32 +631,32 @@ func TestPostStoreSearch(t *testing.T) {
o5 = (<-store.Post().Save(o5)).Data.(*model.Post)
r1 := (<-store.Post().Search(teamId, userId, &model.SearchParams{Terms: "corey", IsHashtag: false})).Data.(*model.PostList)
- if len(r1.Order) != 1 && r1.Order[0] != o1.Id {
+ if len(r1.Order) != 1 || r1.Order[0] != o1.Id {
t.Fatal("returned wrong search result")
}
r3 := (<-store.Post().Search(teamId, userId, &model.SearchParams{Terms: "new", IsHashtag: false})).Data.(*model.PostList)
- if len(r3.Order) != 2 && r3.Order[0] != o1.Id {
+ if len(r3.Order) != 2 || (r3.Order[0] != o1.Id && r3.Order[1] != o1.Id) {
t.Fatal("returned wrong search result")
}
r4 := (<-store.Post().Search(teamId, userId, &model.SearchParams{Terms: "john", IsHashtag: false})).Data.(*model.PostList)
- if len(r4.Order) != 1 && r4.Order[0] != o2.Id {
+ if len(r4.Order) != 1 || r4.Order[0] != o2.Id {
t.Fatal("returned wrong search result")
}
r5 := (<-store.Post().Search(teamId, userId, &model.SearchParams{Terms: "matter*", IsHashtag: false})).Data.(*model.PostList)
- if len(r5.Order) != 1 && r5.Order[0] != o1.Id {
+ if len(r5.Order) != 1 || r5.Order[0] != o1.Id {
t.Fatal("returned wrong search result")
}
r6 := (<-store.Post().Search(teamId, userId, &model.SearchParams{Terms: "#hashtag", IsHashtag: true})).Data.(*model.PostList)
- if len(r6.Order) != 1 && r6.Order[0] != o4.Id {
+ if len(r6.Order) != 1 || r6.Order[0] != o4.Id {
t.Fatal("returned wrong search result")
}
r7 := (<-store.Post().Search(teamId, userId, &model.SearchParams{Terms: "#secret", IsHashtag: true})).Data.(*model.PostList)
- if len(r7.Order) != 1 && r7.Order[0] != o5.Id {
+ if len(r7.Order) != 1 || r7.Order[0] != o5.Id {
t.Fatal("returned wrong search result")
}
diff --git a/store/sql_team_store.go b/store/sql_team_store.go
index dfc07d3d8..e0f95fa7e 100644
--- a/store/sql_team_store.go
+++ b/store/sql_team_store.go
@@ -32,7 +32,7 @@ func NewSqlTeamStore(sqlStore *SqlStore) TeamStore {
func (s SqlTeamStore) UpgradeSchemaIfNeeded() {
// REMOVE AFTER 1.2 SHIP see PLT-828
s.RemoveColumnIfExists("Teams", "AllowValet")
- s.CreateColumnIfNotExists("Teams", "InviteId", "varchar(26)", "varchar(26)", "")
+ s.CreateColumnIfNotExists("Teams", "InviteId", "varchar(32)", "varchar(32)", "")
s.CreateColumnIfNotExists("Teams", "AllowOpenInvite", "tinyint(1)", "boolean", "0")
s.CreateColumnIfNotExists("Teams", "AllowTeamListing", "tinyint(1)", "boolean", "0")
}
diff --git a/utils/textgeneration.go b/utils/textgeneration.go
index 420d6fb29..fd0284a2e 100644
--- a/utils/textgeneration.go
+++ b/utils/textgeneration.go
@@ -15,6 +15,241 @@ const (
// Strings that should pass as acceptable posts
var FUZZY_STRINGS_POSTS = []string{
+ `**[1] - [Markdown Tests]**
+_italics_
+more _italics_
+**bold**
+more **bold**
+**_bold-italic_**
+more **_bold-italic_*8
+~~strikethrough~~
+more ~~strikethrough~~
+` + "```" + `
+multi-line code block<enter here>
+multi-line code block
+emoji that should not render in code block: :ice_cream:
+` + "```" + `
+` + "`monospace`" + `
+[Link to Mattermost](www.mattermost.com)
+Inline Image with link, alt text, and hover text: ![Build Status](https://travis-ci.org/mattermost/platform.svg?branch=master)](https://travis-ci.org/mattermost/platform)
+
+Three types of lines:
+***
+___
+---
+`,
+
+ ` **[2] - **[More Markdown Tests]**
+> i am a blockquote!
+
+> i am a 2nd multiline
+> quote.
+i am text right after a multiline quote, but not in the quote
+
+* list item
+* another list item
+ * indented list item
+
+1. numbered list, item number 1
+2. item number two
+
+`,
+
+ ` **[3]** - **[More Markdown Tests]**
+
+Table
+
+| Left-Aligned | Center Aligned | Right Aligned |
+| :------------ |:---------------:| -----:|
+| Left column 1 | this text | $100 |
+| Left column 2 | is | $10 |
+| Left column 3 | centered | $1 |
+
+Ugly table
+
+Markdown | Less | Pretty
+--- | --- | ---
+*Still* | ~~renders~~ | **nicely**
+1 | 2 | 3
+
+# Large heading
+## Smaller heading
+### Even smaller heading
+# Large heading
+## Smaller heading
+### Even smaller heading
+
+`,
+
+ /* `**[2] [Username Linking Test]**
+ I saw @alice--and I said "Hi @alice!" then "What's up @alice?" and then @alice, was totally @alice; she just "@alice"'d me and walked on by. That's @alice...
+ @alice‽‽
+ `,
+
+ `**[3] [Mention Highlighting Test]**
+ `,*/
+
+ `**[4] [Emoji Display Test 1]**
+:+1: :-1: :100: :1234: :8ball: :a: :ab: :abc: :abcd: :accept:
+:aerial_tramway: :airplane: :alarm_clock: :ambulance: :anchor: :angel: :anger: :angry: :anguished: :ant:
+:apple: :aquarius: :aries: :arrow_backward: :arrow_double_down: :arrow_double_up: :arrow_down: :arrow_down_small: :arrow_forward: :arrow_heading_down:
+:arrow_heading_up: :arrow_left: :arrow_lower_left: :arrow_lower_right: :arrow_right: :arrow_right_hook: :arrow_up: :arrow_up_down:
+:arrow_upper_left: :arrow_upper_right: :arrows_clockwise: :arrows_counterclockwise: :art: :articulated_lorry: :astonished: :atm: :arrow_up_small: :b:
+:baby: :baby_bottle: :baby_chick: :baby_symbol: :back: :baggage_claim: :balloon: :ballot_box_with_check: :bamboo: :banana:
+:bangbang: :bank: :bar_chart: :barber: :baseball: :basketball: :bath: :bathtub: :battery: :bear:
+:bee: :beer: :beers: :beetle: :beginner: :bell: :bento: :bicyclist: :bike: :bikini:
+:bird: :birthday: :black_circle: :black_joker: :black_medium_small_square: :black_medium_square: :black_nib: :black_small_square: :black_square: :black_square_button:
+:blossom: :blowfish: :blue_book: :blue_car: :blue_heart: :blush: :boar: :boat: :bomb: :book:
+:bookmark: :bookmark_tabs: :books: :boom: :boot: :bouquet: :bow: :bowling: :bowtie: :boy:
+:bread: :bride_with_veil: :bridge_at_night: :briefcase: :broken_heart: :bug: :bulb: :bullettrain_front: :bullettrain_side: :bus:
+:busstop: :bust_in_silhouette: :busts_in_silhouette: :cactus: :cake: :calendar: :calling: :camel: :camera: :cancer:
+:candy: :capital_abcd: :capricorn: :car: :card_index: :carousel_horse: :cat: :cat2: :cd: :chart:
+:chart_with_downwards_trend: :chart_with_upwards_trend: :checkered_flag: :cherries: :cherry_blossom: :chestnut: :chicken: :children_crossing: :chocolate_bar: :christmas_tree:
+:church: :cinema: :circus_tent: :city_sunrise: :city_sunset: :cl: :clap: :clapper: :clipboard: :clock1:
+:clock10: :clock1030: :clock11: :clock1130: :clock12: :clock1230: :clock130: :clock2: :clock230: :clock3:
+:clock330: :clock4: :clock430: :clock5: :clock530: :clock6: :clock630: :clock7: :clock730: :clock8:
+:clock830: :clock9: :clock930: :closed_book: :closed_lock_with_key: :closed_umbrella: :cloud: :clubs: :cn: :cocktail:
+:coffee: :cold_sweat: :collision: :computer: :confetti_ball: :confounded: :confused: :congratulations: :construction: :construction_worker:
+:convenience_store: :cookie: :cool: :cop: :copyright: :corn: :couple: :couple_with_heart: :couplekiss: :cow:
+:cow2: :credit_card: :crescent_moon: :crocodile: :crossed_flags: :crown: :cry: :crying_cat_face: :crystal_ball: :cupid:
+:curly_loop: :currency_exchange: :curry: :custard: :customs: :cyclone: :dancer: :dancers: :dango: :dart:
+:dash: :date: :de: :deciduous_tree: :department_store: :diamond_shape_with_a_dot_inside: :diamonds: :disappointed: :disappointed_relieved: :dizzy:
+:dizzy_face: :do_not_litter: :dog: :dog2: :dollar: :dolls: :dolphin: :donut: :door: :doughnut:
+:dragon: :dragon_face: :dress: :dromedary_camel: :droplet: :dvd: :e-mail: :ear: :ear_of_rice: :earth_africa:
+:earth_americas: :earth_asia: :egg: :eggplant: :eight: :eight_pointed_black_star: :eight_spoked_asterisk: :electric_plug: :elephant: :email:
+ :end: :envelope: :es: :euro: :european_castle: :european_post_office: :evergreen_tree: :exclamation: :expressionless: :eyeglasses:
+:eyes: :facepunch: :factory: :fallen_leaf: :family: :fast_forward: :fax: :fearful: :feelsgood: :feet:
+:ferris_wheel: :file_folder: :finnadie: :fire: :fire_engine: :fireworks: :first_quarter_moon: :first_quarter_moon_with_face: :fish: :fish_cake:
+:fishing_pole_and_fish: :fist: :five: :flags: :flashlight: :floppy_disk: :flower_playing_cards: :flushed: :foggy: :football:
+:fork_and_knife: :fountain: :four: :four_leaf_clover: :fr: :free: :fried_shrimp: :fries: :frog: :frowning:
+:fu: :fuelpump: :full_moon: :full_moon_with_face: :game_die: :gb: :gem: :gemini: :ghost: :gift:`,
+
+ `**[5] [Emoji Display Test 2]**
+:gift_heart: :girl: :globe_with_meridians: :goat: :goberserk: :godmode: :golf: :grapes: :green_apple: :green_book:
+:green_heart: :grey_exclamation: :grey_question: :grimacing: :grin: :grinning: :guardsman: :guitar: :gun: :haircut:
+:hamburger: :hammer: :hamster: :hand: :handbag: :hankey: :hash: :hatched_chick: :hatching_chick: :headphones:
+:hear_no_evil: :heart: :heart_decoration: :heart_eyes: :heart_eyes_cat: :heartbeat: :heartpulse: :hearts: :heavy_check_mark: :heavy_division_sign:
+:heavy_dollar_sign: :heavy_exclamation_mark: :heavy_minus_sign: :heavy_multiplication_x: :heavy_plus_sign: :helicopter: :herb: :hibiscus: :high_brightness: :high_heel:
+:hocho: :honey_pot: :honeybee: :horse: :horse_racing: :hospital: :hotel: :hotsprings: :hourglass: :hourglass_flowing_sand:
+:house: :house_with_garden: :hurtrealbad: :hushed: :ice_cream: :icecream: :id: :ideograph_advantage: :imp: :inbox_tray:
+:incoming_envelope: :information_desk_person: :information_source: :innocent: :interrobang: :iphone: :it: :izakaya_lantern: :jack_o_lantern:
+:japan: :japanese_castle: :japanese_goblin: :japanese_ogre: :jeans: :joy: :joy_cat: :jp: :key: :keycap_ten:
+:kimono: :kiss: :kissing: :kissing_cat: :kissing_closed_eyes: :kissing_face: :kissing_heart: :kissing_smiling_eyes: :koala: :koko:
+:kr: :large_blue_circle: :large_blue_diamond: :large_orange_diamond: :last_quarter_moon: :last_quarter_moon_with_face: :laughing: :leaves: :ledger: :left_luggage:
+:left_right_arrow: :leftwards_arrow_with_hook: :lemon: :leo: :leopard: :libra: :light_rail: :link: :lips: :lipstick:
+:lock: :lock_with_ink_pen: :lollipop: :loop: :loudspeaker: :love_hotel: :love_letter: :low_brightness: :m: :mag:
+:mag_right: :mahjong: :mailbox: :mailbox_closed: :mailbox_with_mail: :mailbox_with_no_mail: :man: :man_with_gua_pi_mao: :man_with_turban: :mans_shoe:
+:maple_leaf: :mask: :massage: :meat_on_bone: :mega: :melon: :memo: :mens: :metal: :metro:
+:microphone: :microscope: :milky_way: :minibus: :minidisc: :mobile_phone_off: :money_with_wings: :moneybag: :monkey: :monkey_face:
+:monorail: :mortar_board: :mount_fuji: :mountain_bicyclist: :mountain_cableway: :mountain_railway: :mouse: :mouse2: :movie_camera: :moyai:
+:muscle: :mushroom: :musical_keyboard: :musical_note: :musical_score: :mute: :nail_care: :name_badge: :neckbeard: :necktie:
+:negative_squared_cross_mark: :neutral_face: :new: :new_moon: :new_moon_with_face: :newspaper: :ng: :nine: :no_bell:
+:no_bicycles: :no_entry: :no_entry_sign: :no_good: :no_mobile_phones: :no_mouth: :no_pedestrians: :no_smoking: :non-potable_water: :nose:
+:notebook: :notebook_with_decorative_cover: :notes: :nut_and_bolt: :o: :o2: :ocean: :octocat: :octopus: :oden:
+:office: :ok: :ok_hand: :ok_woman: :older_man: :older_woman: :on: :oncoming_automobile: :oncoming_bus: :oncoming_police_car:
+:oncoming_taxi: :one: :open_file_folder: :open_hands: :open_mouth: :ophiuchus: :orange_book: :outbox_tray: :ox: :package:
+:page_facing_up: :page_with_curl: :pager: :palm_tree: :panda_face: :paperclip: :parking: :part_alternation_mark: :partly_sunny: :passport_control:
+:paw_prints: :peach: :pear: :pencil: :pencil2: :penguin: :pensive: :performing_arts: :persevere: :person_frowning:
+:person_with_blond_hair: :person_with_pouting_face: :phone: :pig: :pig2: :pig_nose: :pill: :pineapple: :pisces: :pizza:
+`,
+
+ `**[6] [Emoji Display Test 3]**
+:plus1: :point_down: :point_left: :point_right: :point_up: :point_up_2: :police_car: :poodle: :poop: :post_office:
+:postal_horn: :postbox: :potable_water: :pouch: :poultry_leg: :pound: :pouting_cat: :pray: :princess: :punch:
+:purple_heart: :purse: :pushpin: :put_litter_in_its_place: :question: :rabbit: :rabbit2: :racehorse: :radio: :radio_button:
+:rage: :rage1: :rage2: :rage3: :rage4: :railway_car: :rainbow: :raised_hand: :raised_hands: :raising_hand:
+:ram: :ramen: :rat: :recycle: :red_car: :red_circle: :registered: :relaxed: :relieved: :repeat:
+:repeat_one: :restroom: :revolving_hearts: :rewind: :ribbon: :rice: :rice_ball: :rice_cracker: :rice_scene: :ring:
+:rocket: :roller_coaster: :rooster: :rose: :rotating_light: :round_pushpin: :rowboat: :ru:
+:rugby_football: :runner: :running: :running_shirt_with_sash: :sa: :sagittarius: :sailboat: :sake: :sandal: :santa:
+:satellite: :satisfied: :saxophone: :school: :school_satchel: :scissors: :scorpius: :scream: :scream_cat: :scroll:
+:seat: :secret: :see_no_evil: :seedling: :seven: :shaved_ice: :sheep: :shell: :ship: :shipit:
+:shirt: :shit: :shoe: :shower: :signal_strength: :six: :six_pointed_star: :ski: :skull: :sleeping:
+:sleepy: :slot_machine: :small_blue_diamond: :small_orange_diamond: :small_red_triangle: :small_red_triangle_down: :smile: :smile_cat: :smiley: :smiley_cat:
+:smiling_imp: :smirk: :smirk_cat: :smoking: :snail: :snake: :snowboarder: :snowflake: :snowman: :sob:
+:soccer: :soon: :sos: :sound: :space_invader: :spades: :spaghetti: :sparkle: :sparkler: :sparkles:
+:sparkling_heart: :speak_no_evil: :speaker: :speech_balloon: :speedboat: :squirrel: :star: :star2: :stars: :station:
+:statue_of_liberty: :steam_locomotive: :stew: :straight_ruler: :strawberry: :stuck_out_tongue: :stuck_out_tongue_closed_eyes: :stuck_out_tongue_winking_eye: :sun_with_face: :sunflower:
+ :sunglasses: :sunny: :sunrise: :sunrise_over_mountains: :surfer: :sushi: :suspect: :suspension_railway: :sweat: :sweat_drops:
+:sweat_smile: :sweet_potato: :swimmer: :symbols: :syringe: :tada: :tanabata_tree: :tangerine: :taurus: :taxi:
+:tea: :telephone: :telephone_receiver: :telescope: :tennis: :tent: :thought_balloon: :three: :thumbsdown: :thumbsup:
+:ticket: :tiger: :tiger2: :tired_face: :tm: :toilet: :tokyo_tower: :tomato: :tongue: :top:
+:tophat: :tractor: :traffic_light: :train: :train2: :tram: :triangular_flag_on_post: :triangular_ruler: :trident: :triumph:
+:trolleybus: :trollface: :trophy: :tropical_drink: :tropical_fish: :truck: :trumpet: :tshirt: :tulip: :turtle:
+:tv: :twisted_rightwards_arrows: :two: :two_hearts: :two_men_holding_hands: :two_women_holding_hands:
+:uk: :umbrella: :unamused: :underage: :unlock: :up: :us: :v: :vertical_traffic_light: :vhs:
+:vibration_mode: :video_camera: :video_game: :violin: :virgo: :volcano: :vs: :walking: :waning_crescent_moon: :waning_gibbous_moon:
+:warning: :watch: :water_buffalo: :watermelon: :wave: :wavy_dash: :waxing_crescent_moon: :waxing_gibbous_moon: :wc: :weary:
+:wedding: :whale: :whale2: :wheelchair: :white_check_mark: :white_circle: :white_flower: :white_large_square: :white_medium_small_square: :white_medium_square:
+:white_small_square: :white_square_button: :wind_chime: :wine_glass: :wink: :wolf: :woman: :womans_clothes: :womans_hat: :womens:
+:worried: :wrench: :x: :yellow_heart: :yen: :yum: :zap: :zero: :zzz:
+Unnamed: :u5272: :u5408: :u55b6: :u6307: :u6708: :u6709: :u6e80: :u7121: :u7533: :u7981: :u7a7a:
+`,
+
+ `**[7] [Auto Linking]**
+#### should be turned into links:
+http://example.com
+https://example.com
+www.example.com
+www.example.com/index
+www.example.com/index.html
+www.example.com/index/sub
+www.example.com/index?params=1
+www.example.com/index?params=1&other=2
+www.example.com/index?params=1;other=2
+http://example.com:8065
+<http://example.com>
+<www.example.com>
+http://www.example.com/_/page
+www.example.com/_/page
+https://en.wikipedia.org/wiki/🐬
+https://en.wikipedia.org/wiki/Rendering_(computer_graphics)
+http://127.0.0.1
+http://192.168.1.1:4040
+http://[::1]:80
+http://[::1]:8065
+https://[::1]:80
+http://[2001:0:5ef5:79fb:303a:62d5:3312:ff42]:80
+http://[2001:0:5ef5:79fb:303a:62d5:3312:ff42]:8065
+https://[2001:0:5ef5:79fb:303a:62d5:3312:ff42]:443
+http://username:password@example.com
+http://username:password@127.0.0.1
+http://username:password@[2001:0:5ef5:79fb:303a:62d5:3312:ff42]:80
+test@example.com
+
+#### should be turned into links which link to the correct place:
+[example link](example.com) links to ` + "`" + `http://example.com` + "`" + `
+[example.com](example.com) links to ` + "`" + `http://example.com` + "`" + `
+[example.com/other](example.com) links to ` + "`" + `http://example.com` + "`" + `
+[example.com/other_link](example.com/example) links to ` + "`" + `http://example.com/example` + "`" + `
+www.example.com links to ` + "`" + `http://www.example.com` + "`" + `
+https://example.com links to ` + "`" + `https://example.com` + "`" + `and not ` + "`" + `http://example.com` + "`" + `
+https://en.wikipedia.org/wiki/🐬 links to the Wikipedia article on dolphins
+https://en.wikipedia.org/wiki/URLs#Syntax links to the Syntax section of the Wikipedia article on URLs
+test@example.com links to ` + "`" + `mailto:test@example.com` + "`" + `
+[email link](mailto:test@example.com) links to ` + "`" + `mailto:test@example.com` + "`" + `and not ` + "`" + `http://mailto:test@example.com` + "`" + `
+[other link](ts3server://example.com) links to ` + "`" + `ts3server://example.com` + "`" + `and not ` + "`" + `http://ts3server://example.com` + "`" + `
+
+#### should not be turned into links:
+example.com
+readme.md
+<example.com>
+http://
+@example.com
+
+#### should only turn the actual link into a link and not change surrounding text
+(http://example.com)
+(test@example.com)
+This is a sentence with a http://example.com in it.
+This is a sentence with a [link](http://example.com) in it.
+This is a sentence with a http://example.com/_/underscore in it.
+This is a sentence with a link (http://example.com) in it.
+This is a sentence with a (https://en.wikipedia.org/wiki/Rendering_(computer_graphics)) in it.
+This is a sentence with a http://192.168.1.1:4040 in it.
+This is a sentence with a https://::1 in it.
+This is a link to http://example.com.
+`,
+
"*", "?", ".", "}{][)(><", "{}[]()<>",
"qahwah ( قهوة)",
@@ -238,5 +473,5 @@ func RandomText(length Range, hashtags Range, mentions Range, users []string) st
}
func FuzzPost() string {
- return FUZZY_STRINGS_POSTS[RandIntFromRange(Range{0, len(FUZZY_STRINGS_NAMES) - 1})]
+ return FUZZY_STRINGS_POSTS[RandIntFromRange(Range{0, len(FUZZY_STRINGS_POSTS) - 1})]
}
diff --git a/web/react/components/admin_console/admin_controller.jsx b/web/react/components/admin_console/admin_controller.jsx
index d309ced2e..8e0ab0555 100644
--- a/web/react/components/admin_console/admin_controller.jsx
+++ b/web/react/components/admin_console/admin_controller.jsx
@@ -6,6 +6,7 @@ var AdminStore = require('../../stores/admin_store.jsx');
var TeamStore = require('../../stores/team_store.jsx');
var AsyncClient = require('../../utils/async_client.jsx');
var LoadingScreen = require('../loading_screen.jsx');
+var Utils = require('../../utils/utils.jsx');
var EmailSettingsTab = require('./email_settings.jsx');
var LogSettingsTab = require('./log_settings.jsx');
@@ -46,7 +47,8 @@ export default class AdminController extends React.Component {
};
if (!props.tab) {
- history.replaceState(null, null, `/admin_console/${this.state.selected}`);
+ var tokenIndex = Utils.getUrlParameter('session_token_index');
+ history.replaceState(null, null, `/admin_console/${this.state.selected}?session_token_index=${tokenIndex}`);
}
}
diff --git a/web/react/components/admin_console/admin_sidebar.jsx b/web/react/components/admin_console/admin_sidebar.jsx
index f2fb1c96d..0d52ae347 100644
--- a/web/react/components/admin_console/admin_sidebar.jsx
+++ b/web/react/components/admin_console/admin_sidebar.jsx
@@ -3,6 +3,7 @@
var AdminSidebarHeader = require('./admin_sidebar_header.jsx');
var SelectTeamModal = require('./select_team_modal.jsx');
+var Utils = require('../../utils/utils.jsx');
export default class AdminSidebar extends React.Component {
constructor(props) {
@@ -24,12 +25,13 @@ export default class AdminSidebar extends React.Component {
handleClick(name, teamId, e) {
e.preventDefault();
this.props.selectTab(name, teamId);
- history.pushState({name, teamId}, null, `/admin_console/${name}/${teamId || ''}`);
+ var tokenIndex = Utils.getUrlParameter('session_token_index');
+ history.pushState({name, teamId}, null, `/admin_console/${name}/${teamId || ''}?session_token_index=${tokenIndex}`);
}
isSelected(name, teamId) {
if (this.props.selected === name) {
- if (name === 'team_users') {
+ if (name === 'team_users' || name === 'team_analytics') {
if (this.props.selectedTeam != null && this.props.selectedTeam === teamId) {
return 'active';
}
diff --git a/web/react/components/center_panel.jsx b/web/react/components/center_panel.jsx
new file mode 100644
index 000000000..242c2c637
--- /dev/null
+++ b/web/react/components/center_panel.jsx
@@ -0,0 +1,84 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+const TutorialIntroScreens = require('./tutorial/tutorial_intro_screens.jsx');
+const CreatePost = require('./create_post.jsx');
+const PostsViewContainer = require('./posts_view_container.jsx');
+const ChannelHeader = require('./channel_header.jsx');
+const Navbar = require('./navbar.jsx');
+const FileUploadOverlay = require('./file_upload_overlay.jsx');
+
+const PreferenceStore = require('../stores/preference_store.jsx');
+const UserStore = require('../stores/user_store.jsx');
+
+const Constants = require('../utils/constants.jsx');
+const TutorialSteps = Constants.TutorialSteps;
+const Preferences = Constants.Preferences;
+
+export default class CenterPanel extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.onPreferenceChange = this.onPreferenceChange.bind(this);
+
+ const tutorialPref = PreferenceStore.getPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), {value: '0'});
+ this.state = {showTutorialScreens: parseInt(tutorialPref.value, 10) === TutorialSteps.INTRO_SCREENS};
+ }
+ componentDidMount() {
+ PreferenceStore.addChangeListener(this.onPreferenceChange);
+ }
+ componentWillUnmount() {
+ PreferenceStore.removeChangeListener(this.onPreferenceChange);
+ }
+ onPreferenceChange() {
+ const tutorialPref = PreferenceStore.getPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), {value: '0'});
+ this.setState({showTutorialScreens: parseInt(tutorialPref.value, 10) <= TutorialSteps.INTRO_SCREENS});
+ }
+ render() {
+ let postsContainer;
+ if (this.state.showTutorialScreens) {
+ postsContainer = <TutorialIntroScreens />;
+ } else {
+ postsContainer = <PostsViewContainer />;
+ }
+
+ return (
+ <div className='inner__wrap channel__wrap'>
+ <div className='row header'>
+ <div id='navbar'>
+ <Navbar/>
+ </div>
+ </div>
+ <div className='row main'>
+ <FileUploadOverlay
+ id='file_upload_overlay'
+ overlayType='center'
+ />
+ <div
+ id='app-content'
+ className='app__content'
+ >
+ <div id='channel-header'>
+ <ChannelHeader />
+ </div>
+ <div id='post-list'>
+ {postsContainer}
+ </div>
+ <div
+ className='post-create__container'
+ id='post-create'
+ >
+ <CreatePost />
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+}
+
+CenterPanel.defaultProps = {
+};
+
+CenterPanel.propTypes = {
+};
diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx
index 101fd85e5..20f106f30 100644
--- a/web/react/components/channel_header.jsx
+++ b/web/react/components/channel_header.jsx
@@ -4,6 +4,7 @@
const ChannelStore = require('../stores/channel_store.jsx');
const UserStore = require('../stores/user_store.jsx');
const SearchStore = require('../stores/search_store.jsx');
+const PreferenceStore = require('../stores/preference_store.jsx');
const NavbarSearchBox = require('./search_bar.jsx');
const AsyncClient = require('../utils/async_client.jsx');
const Client = require('../utils/client.jsx');
@@ -46,12 +47,14 @@ export default class ChannelHeader extends React.Component {
ChannelStore.addExtraInfoChangeListener(this.onListenerChange);
SearchStore.addSearchChangeListener(this.onListenerChange);
UserStore.addChangeListener(this.onListenerChange);
+ PreferenceStore.addChangeListener(this.onListenerChange);
}
componentWillUnmount() {
ChannelStore.removeChangeListener(this.onListenerChange);
ChannelStore.removeExtraInfoChangeListener(this.onListenerChange);
SearchStore.removeSearchChangeListener(this.onListenerChange);
- UserStore.addChangeListener(this.onListenerChange);
+ UserStore.removeChangeListener(this.onListenerChange);
+ PreferenceStore.removeChangeListener(this.onListenerChange);
}
onListenerChange() {
const newState = this.getStateFromStores();
@@ -134,7 +137,7 @@ export default class ChannelHeader extends React.Component {
} else {
contact = this.state.users[0];
}
- channelTitle = contact.nickname || contact.username;
+ channelTitle = Utils.displayUsername(contact.id);
}
}
diff --git a/web/react/components/channel_view.jsx b/web/react/components/channel_view.jsx
new file mode 100644
index 000000000..3f53a94c2
--- /dev/null
+++ b/web/react/components/channel_view.jsx
@@ -0,0 +1,43 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+const CenterPanel = require('../components/center_panel.jsx');
+const Sidebar = require('../components/sidebar.jsx');
+const SidebarRight = require('../components/sidebar_right.jsx');
+const SidebarRightMenu = require('../components/sidebar_right_menu.jsx');
+
+export default class ChannelView extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ render() {
+ return (
+ <div className='container-fluid'>
+ <div
+ className='sidebar--right'
+ id='sidebar-right'
+ >
+ <SidebarRight/>
+ </div>
+ <div
+ className='sidebar--menu'
+ id='sidebar-menu'
+ >
+ <SidebarRightMenu/>
+ </div>
+ <div
+ className='sidebar--left'
+ id='sidebar-left'
+ >
+ <Sidebar/>
+ </div>
+ <CenterPanel />
+ </div>
+ );
+ }
+}
+ChannelView.defaultProps = {
+};
+
+ChannelView.propTypes = {
+};
diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx
index cdbc3bc6d..1545cdfaa 100644
--- a/web/react/components/create_post.jsx
+++ b/web/react/components/create_post.jsx
@@ -1,21 +1,26 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
+const MsgTyping = require('./msg_typing.jsx');
+const Textbox = require('./textbox.jsx');
+const FileUpload = require('./file_upload.jsx');
+const FilePreview = require('./file_preview.jsx');
+const TutorialTip = require('./tutorial/tutorial_tip.jsx');
+
const AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
const Client = require('../utils/client.jsx');
const AsyncClient = require('../utils/async_client.jsx');
+const Utils = require('../utils/utils.jsx');
+
const ChannelStore = require('../stores/channel_store.jsx');
const PostStore = require('../stores/post_store.jsx');
const UserStore = require('../stores/user_store.jsx');
-const SocketStore = require('../stores/socket_store.jsx');
const PreferenceStore = require('../stores/preference_store.jsx');
-const MsgTyping = require('./msg_typing.jsx');
-const Textbox = require('./textbox.jsx');
-const FileUpload = require('./file_upload.jsx');
-const FilePreview = require('./file_preview.jsx');
-const Utils = require('../utils/utils.jsx');
+const SocketStore = require('../stores/socket_store.jsx');
const Constants = require('../utils/constants.jsx');
+const Preferences = Constants.Preferences;
+const TutorialSteps = Constants.TutorialSteps;
const ActionTypes = Constants.ActionTypes;
const KeyCodes = Constants.KeyCodes;
@@ -36,15 +41,16 @@ export default class CreatePost extends React.Component {
this.handleTextDrop = this.handleTextDrop.bind(this);
this.removePreview = this.removePreview.bind(this);
this.onChange = this.onChange.bind(this);
+ this.onPreferenceChange = this.onPreferenceChange.bind(this);
this.getFileCount = this.getFileCount.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
this.handleResize = this.handleResize.bind(this);
this.sendMessage = this.sendMessage.bind(this);
- this.onPreferenceChange = this.onPreferenceChange.bind(this);
PostStore.clearDraftUploads();
const draft = this.getCurrentDraft();
+ const tutorialPref = PreferenceStore.getPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), {value: '0'});
this.state = {
channelId: ChannelStore.getCurrentId(),
@@ -55,16 +61,12 @@ export default class CreatePost extends React.Component {
initialText: draft.messageText,
windowWidth: Utils.windowWidth(),
windowHeight: Utils.windowHeight(),
- ctrlSend: PreferenceStore.getPreference(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter', {value: 'false'}).value
+ ctrlSend: PreferenceStore.getPreference(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter', {value: 'false'}).value,
+ showTutorialTip: parseInt(tutorialPref.value, 10) === TutorialSteps.POST_POPOVER
};
PreferenceStore.addChangeListener(this.onPreferenceChange);
}
- onPreferenceChange() {
- this.setState({
- ctrlSend: PreferenceStore.getPreference(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter', {value: 'false'}).value
- });
- }
handleResize() {
this.setState({
windowWidth: Utils.windowWidth(),
@@ -176,6 +178,7 @@ export default class CreatePost extends React.Component {
PostStore.storePendingPost(post);
PostStore.storeDraft(channel.id, null);
+ PostStore.jumpPostsViewToBottom();
this.setState({messageText: '', submitting: false, postError: null, previews: [], serverError: null});
Client.createPost(post, channel,
@@ -317,11 +320,13 @@ export default class CreatePost extends React.Component {
}
componentDidMount() {
ChannelStore.addChangeListener(this.onChange);
+ PreferenceStore.addChangeListener(this.onPreferenceChange);
this.resizePostHolder();
window.addEventListener('resize', this.handleResize);
}
componentWillUnmount() {
ChannelStore.removeChangeListener(this.onChange);
+ PreferenceStore.removeChangeListener(this.onPreferenceChange);
window.removeEventListener('resize', this.handleResize);
}
onChange() {
@@ -332,6 +337,13 @@ export default class CreatePost extends React.Component {
this.setState({channelId, messageText: draft.messageText, initialText: draft.messageText, submitting: false, serverError: null, postError: null, previews: draft.previews, uploadsInProgress: draft.uploadsInProgress});
}
}
+ onPreferenceChange() {
+ const tutorialPref = PreferenceStore.getPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), {value: '0'});
+ this.setState({
+ showTutorialTip: parseInt(tutorialPref.value, 10) === TutorialSteps.POST_POPOVER,
+ ctrlSend: PreferenceStore.getPreference(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter', {value: 'false'}).value
+ });
+ }
getFileCount(channelId) {
if (channelId === this.state.channelId) {
return this.state.previews.length + this.state.uploadsInProgress.length;
@@ -366,6 +378,25 @@ export default class CreatePost extends React.Component {
});
}
}
+ createTutorialTip() {
+ const screens = [];
+
+ screens.push(
+ <div>
+ <h4>{'Sending Messages'}</h4>
+ <p>{'Type here to write a message.'}</p>
+ <p>{'Click the attachment button to upload an image or a file.'}</p>
+ </div>
+ );
+
+ return (
+ <TutorialTip
+ placement='top'
+ screens={screens}
+ overlayClass='tip-overlay--chat'
+ />
+ );
+ }
render() {
let serverError = null;
if (this.state.serverError) {
@@ -397,6 +428,11 @@ export default class CreatePost extends React.Component {
postFooterClassName += ' has-error';
}
+ let tutorialTip = null;
+ if (this.state.showTutorialTip) {
+ tutorialTip = this.createTutorialTip();
+ }
+
return (
<form
id='create_post'
@@ -435,6 +471,7 @@ export default class CreatePost extends React.Component {
>
<i className='fa fa-paper-plane' />
</a>
+ {tutorialTip}
</div>
<div className={postFooterClassName}>
{postError}
diff --git a/web/react/components/edit_post_modal.jsx b/web/react/components/edit_post_modal.jsx
index 2abb3f151..ef32baa7d 100644
--- a/web/react/components/edit_post_modal.jsx
+++ b/web/react/components/edit_post_modal.jsx
@@ -120,7 +120,7 @@ export default class EditPostModal extends React.Component {
PreferenceStore.addChangeListener(this.onPreferenceChange);
}
componentWillUnmount() {
- PostStore.removeEditPostListener(this.handleEditPostEvent);
+ PostStore.removeEditPostListner(this.handleEditPostEvent);
PreferenceStore.removeChangeListener(this.onPreferenceChange);
}
render() {
diff --git a/web/react/components/login.jsx b/web/react/components/login.jsx
index c519959af..2b9ce67ca 100644
--- a/web/react/components/login.jsx
+++ b/web/react/components/login.jsx
@@ -185,7 +185,7 @@ export default class Login extends React.Component {
if (this.props.inviteId) {
userSignUp = (
<div>
- <span>{'Do not have an account? '}
+ <span>{`Don't have an account? `}
<a
href={'/signup_user_complete/?id=' + this.props.inviteId}
className='signup-team-login'
diff --git a/web/react/components/navbar_dropdown.jsx b/web/react/components/navbar_dropdown.jsx
index dc21fad21..f43bdffdf 100644
--- a/web/react/components/navbar_dropdown.jsx
+++ b/web/react/components/navbar_dropdown.jsx
@@ -104,7 +104,7 @@ export default class NavbarDropdown extends React.Component {
</li>
);
- if (this.props.teamType === 'O') {
+ if (this.props.teamType === Constants.OPEN_TEAM) {
teamLink = (
<li>
<a
diff --git a/web/react/components/post.jsx b/web/react/components/post.jsx
index dedac8951..c3c5b3e0b 100644
--- a/web/react/components/post.jsx
+++ b/web/react/components/post.jsx
@@ -204,7 +204,6 @@ export default class Post extends React.Component {
posts={posts}
handleCommentClick={this.handleCommentClick}
retryPost={this.retryPost}
- resize={this.props.resize}
/>
<PostInfo
ref='info'
@@ -228,6 +227,5 @@ Post.propTypes = {
sameUser: React.PropTypes.bool,
sameRoot: React.PropTypes.bool,
hideProfilePic: React.PropTypes.bool,
- isLastComment: React.PropTypes.bool,
- resize: React.PropTypes.func
+ isLastComment: React.PropTypes.bool
};
diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx
index 7138e2cb4..e4094daf3 100644
--- a/web/react/components/post_body.jsx
+++ b/web/react/components/post_body.jsx
@@ -50,7 +50,6 @@ export default class PostBody extends React.Component {
componentDidUpdate() {
this.parseEmojis();
- this.props.resize();
}
componentWillReceiveProps(nextProps) {
@@ -78,12 +77,12 @@ export default class PostBody extends React.Component {
this.isGifLoading = true;
const gif = new Image();
- gif.src = src;
gif.onload = (
() => {
this.setState({gifLoaded: true});
}
);
+ gif.src = src;
}
createGifEmbed(link) {
@@ -93,7 +92,12 @@ export default class PostBody extends React.Component {
if (!this.state.gifLoaded) {
this.loadGif(link);
- return null;
+ return (
+ <img
+ className='gif-div placeholder'
+ height='500px'
+ />
+ );
}
return (
@@ -338,6 +342,5 @@ PostBody.propTypes = {
post: React.PropTypes.object.isRequired,
parentPost: React.PropTypes.object,
retryPost: React.PropTypes.func.isRequired,
- handleCommentClick: React.PropTypes.func.isRequired,
- resize: React.PropTypes.func.isRequired
+ handleCommentClick: React.PropTypes.func.isRequired
};
diff --git a/web/react/components/post_info.jsx b/web/react/components/post_info.jsx
index ddda48e06..a01d842e5 100644
--- a/web/react/components/post_info.jsx
+++ b/web/react/components/post_info.jsx
@@ -3,10 +3,9 @@
var UserStore = require('../stores/user_store.jsx');
var utils = require('../utils/utils.jsx');
+var TimeSince = require('./time_since.jsx');
var Constants = require('../utils/constants.jsx');
-var Tooltip = ReactBootstrap.Tooltip;
-var OverlayTrigger = ReactBootstrap.OverlayTrigger;
export default class PostInfo extends React.Component {
constructor(props) {
@@ -144,21 +143,12 @@ export default class PostInfo extends React.Component {
var dropdown = this.createDropdown();
- let tooltip = <Tooltip id={post.id + 'tooltip'}>{`${utils.displayDate(post.create_at)} at ${utils.displayTime(post.create_at)}`}</Tooltip>;
-
return (
<ul className='post-header post-info'>
<li className='post-header-col'>
- <OverlayTrigger
- delayShow={500}
- container={this}
- placement='top'
- overlay={tooltip}
- >
- <time className='post-profile-time'>
- {utils.displayDateTime(post.create_at)}
- </time>
- </OverlayTrigger>
+ <TimeSince
+ eventTime={post.create_at}
+ />
</li>
<li className='post-header-col post-header__reply'>
<div className='dropdown'>
diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx
deleted file mode 100644
index 444736db5..000000000
--- a/web/react/components/post_list.jsx
+++ /dev/null
@@ -1,764 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-const Post = require('./post.jsx');
-const UserProfile = require('./user_profile.jsx');
-const AsyncClient = require('../utils/async_client.jsx');
-const LoadingScreen = require('./loading_screen.jsx');
-
-const PostStore = require('../stores/post_store.jsx');
-const ChannelStore = require('../stores/channel_store.jsx');
-const UserStore = require('../stores/user_store.jsx');
-const TeamStore = require('../stores/team_store.jsx');
-const SocketStore = require('../stores/socket_store.jsx');
-const PreferenceStore = require('../stores/preference_store.jsx');
-
-const Utils = require('../utils/utils.jsx');
-const Client = require('../utils/client.jsx');
-const Constants = require('../utils/constants.jsx');
-const ActionTypes = Constants.ActionTypes;
-const SocketEvents = Constants.SocketEvents;
-
-const AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
-
-export default class PostList extends React.Component {
- constructor(props) {
- super(props);
-
- this.gotMorePosts = false;
- this.scrolled = false;
- this.prevScrollTop = 0;
- this.seenNewMessages = false;
- this.isUserScroll = true;
- this.userHasSeenNew = false;
- this.loadInProgress = false;
-
- this.onChange = this.onChange.bind(this);
- this.onTimeChange = this.onTimeChange.bind(this);
- this.onSocketChange = this.onSocketChange.bind(this);
- this.createChannelIntroMessage = this.createChannelIntroMessage.bind(this);
- this.loadMorePosts = this.loadMorePosts.bind(this);
- this.loadFirstPosts = this.loadFirstPosts.bind(this);
- this.activate = this.activate.bind(this);
- this.deactivate = this.deactivate.bind(this);
- this.handleResize = this.handleResize.bind(this);
- this.resizePostList = this.resizePostList.bind(this);
- this.updateScroll = this.updateScroll.bind(this);
-
- const state = this.getStateFromStores(props.channelId);
- state.numToDisplay = Constants.POST_CHUNK_SIZE;
- state.isFirstLoadComplete = false;
- state.windowHeight = Utils.windowHeight();
-
- this.state = state;
- }
- getStateFromStores(id) {
- var postList = PostStore.getPosts(id);
-
- if (postList != null) {
- var deletedPosts = PostStore.getUnseenDeletedPosts(id);
-
- if (deletedPosts && Object.keys(deletedPosts).length > 0) {
- for (var pid in deletedPosts) {
- if (deletedPosts.hasOwnProperty(pid)) {
- postList.posts[pid] = deletedPosts[pid];
- postList.order.unshift(pid);
- }
- }
-
- postList.order.sort((a, b) => {
- if (postList.posts[a].create_at > postList.posts[b].create_at) {
- return -1;
- }
- if (postList.posts[a].create_at < postList.posts[b].create_at) {
- return 1;
- }
- return 0;
- });
- }
-
- var pendingPostList = PostStore.getPendingPosts(id);
-
- if (pendingPostList) {
- postList.order = pendingPostList.order.concat(postList.order);
- for (var ppid in pendingPostList.posts) {
- if (pendingPostList.posts.hasOwnProperty(ppid)) {
- postList.posts[ppid] = pendingPostList.posts[ppid];
- }
- }
- }
- }
-
- return {
- postList
- };
- }
- componentDidMount() {
- window.onload = () => this.scrollToBottom();
- if (this.props.isActive) {
- this.activate();
- this.loadFirstPosts(this.props.channelId);
- }
- }
- componentWillUnmount() {
- this.deactivate();
- }
- activate() {
- this.gotMorePosts = false;
- this.scrolled = false;
- this.prevScrollTop = 0;
- this.seenNewMessages = false;
- this.isUserScroll = true;
- this.userHasSeenNew = false;
-
- PostStore.clearUnseenDeletedPosts(this.props.channelId);
- PostStore.addChangeListener(this.onChange);
- UserStore.addStatusesChangeListener(this.onTimeChange);
- PreferenceStore.addChangeListener(this.onTimeChange);
- SocketStore.addChangeListener(this.onSocketChange);
-
- const postHolder = $(ReactDOM.findDOMNode(this.refs.postlist));
-
- window.addEventListener('resize', this.handleResize);
-
- postHolder.on('scroll', () => {
- const position = postHolder.scrollTop() + postHolder.height() + 14;
- const bottom = postHolder[0].scrollHeight;
-
- if (position >= bottom) {
- this.scrolled = false;
- } else {
- this.scrolled = true;
- }
-
- if (this.isUserScroll) {
- this.userHasSeenNew = true;
- }
- this.isUserScroll = true;
-
- $('.top-visible-post').removeClass('top-visible-post');
-
- $(ReactDOM.findDOMNode(this.refs.postlistcontent)).children().each(function select() {
- if ($(this).position().top + $(this).height() / 2 > 0) {
- $(this).addClass('top-visible-post');
- return false;
- }
- });
- });
-
- $('.post-list__content div .post').removeClass('post--last');
- $('.post-list__content div:last-child .post').addClass('post--last');
-
- if (!this.state.isFirstLoadComplete) {
- this.loadFirstPosts(this.props.channelId);
- }
-
- this.resizePostList();
- this.onChange();
- this.scrollToBottom();
- }
- deactivate() {
- PostStore.removeChangeListener(this.onChange);
- UserStore.removeStatusesChangeListener(this.onTimeChange);
- SocketStore.removeChangeListener(this.onSocketChange);
- PreferenceStore.removeChangeListener(this.onTimeChange);
- $('body').off('click.userpopover');
-
- window.removeEventListener('resize', this.handleResize);
-
- var postHolder = $(ReactDOM.findDOMNode(this.refs.postlist));
- postHolder.off('scroll');
- }
- componentDidUpdate(prevProps, prevState) {
- if (!this.props.isActive) {
- return;
- }
-
- if (prevState.windowHeight !== this.state.windowHeight) {
- this.resizePostList();
- if (!this.scrolled) {
- this.scrollToBottom();
- }
- }
-
- $('.post-list__content div .post').removeClass('post--last');
- $('.post-list__content div:last-child .post').addClass('post--last');
-
- if (this.state.postList == null || prevState.postList == null) {
- this.scrollToBottom();
- return;
- }
-
- var order = this.state.postList.order || [];
- var posts = this.state.postList.posts || {};
- var oldOrder = prevState.postList.order || [];
- var oldPosts = prevState.postList.posts || {};
- var userId = UserStore.getCurrentId();
- var firstPost = posts[order[0]] || {};
- var isNewPost = oldOrder.indexOf(order[0]) === -1;
-
- if (this.props.isActive && !prevProps.isActive) {
- this.scrollToBottom();
- } else if (oldOrder.length === 0) {
- this.scrollToBottom();
-
- // the user is scrolled to the bottom
- } else if (!this.scrolled) {
- this.scrollToBottom();
-
- // there's a new post and
- // it's by the user (and not from their webhook) and not a comment
- } else if (isNewPost &&
- userId === firstPost.user_id &&
- !firstPost.props.from_webhook &&
- !Utils.isComment(firstPost)) {
- this.scrollToBottom(true);
-
- // the user clicked 'load more messages'
- } else if (this.gotMorePosts && oldOrder.length > 0) {
- let index;
- if (prevState.numToDisplay >= oldOrder.length) {
- index = oldOrder.length - 1;
- } else {
- index = prevState.numToDisplay;
- }
- const lastPost = oldPosts[oldOrder[index]];
- $('#post_' + lastPost.id)[0].scrollIntoView();
- this.gotMorePosts = false;
- } else {
- this.scrollTo(this.prevScrollTop);
- }
- }
- componentWillUpdate() {
- var postHolder = $(ReactDOM.findDOMNode(this.refs.postlist));
- this.prevScrollTop = postHolder.scrollTop();
- }
- componentWillReceiveProps(nextProps) {
- if (nextProps.isActive === true && this.props.isActive === false) {
- this.activate();
- } else if (nextProps.isActive === false && this.props.isActive === true) {
- this.deactivate();
- }
- }
- updateScroll() {
- if (!this.scrolled) {
- this.scrollToBottom();
- }
- }
- handleResize() {
- this.setState({
- windowHeight: Utils.windowHeight()
- });
- }
- resizePostList() {
- const postHolder = $(ReactDOM.findDOMNode(this.refs.postlist));
- if ($('#create_post').length > 0) {
- const height = this.state.windowHeight - $('#create_post').height() - $('#error_bar').outerHeight() - 50;
- postHolder.css('height', height + 'px');
- }
- }
- scrollTo(val) {
- this.isUserScroll = false;
- var postHolder = $(ReactDOM.findDOMNode(this.refs.postlist));
- postHolder[0].scrollTop = val;
- }
- scrollToBottom(force) {
- this.isUserScroll = false;
- var postHolder = $(ReactDOM.findDOMNode(this.refs.postlist));
- if ($('#new_message_' + this.props.channelId)[0] && !this.userHasSeenNew && !force) {
- $('#new_message_' + this.props.channelId)[0].scrollIntoView();
- } else {
- postHolder.addClass('hide-scroll');
- postHolder[0].scrollTop = postHolder[0].scrollHeight;
- postHolder.removeClass('hide-scroll');
- }
- }
- loadFirstPosts(id) {
- if (this.loadInProgress) {
- return;
- }
-
- if (this.props.channelId == null) {
- return;
- }
-
- this.loadInProgress = true;
- Client.getPosts(
- id,
- PostStore.getLatestUpdate(id),
- () => {
- this.loadInProgress = false;
- this.setState({isFirstLoadComplete: true});
- },
- () => {
- this.loadInProgress = false;
- this.setState({isFirstLoadComplete: true});
- }
- );
- }
- onChange() {
- var newState = this.getStateFromStores(this.props.channelId);
-
- if (!Utils.areStatesEqual(newState.postList, this.state.postList)) {
- this.setState(newState);
- }
- }
- onSocketChange(msg) {
- if (msg.action === SocketEvents.POST_DELETED) {
- var activeRoot = $(document.activeElement).closest('.comment-create-body')[0];
- var activeRootPostId = '';
- if (activeRoot && activeRoot.id.length > 0) {
- activeRootPostId = activeRoot.id;
- }
-
- if (activeRootPostId === msg.props.post_id && UserStore.getCurrentId() !== msg.user_id) {
- $('#post_deleted').modal('show');
- }
- }
- }
- onTimeChange() {
- if (!this.state.postList) {
- return;
- }
-
- for (var id in this.state.postList.posts) {
- if (!this.refs[id]) {
- continue;
- }
- this.refs[id].forceUpdateInfo();
- }
- }
- createDMIntroMessage(channel) {
- var teammate = Utils.getDirectTeammate(channel.id);
-
- if (teammate) {
- var teammateName = teammate.username;
- if (teammate.nickname.length > 0) {
- teammateName = teammate.nickname;
- }
-
- return (
- <div className='channel-intro'>
- <div className='post-profile-img__container channel-intro-img'>
- <img
- className='post-profile-img'
- src={'/api/v1/users/' + teammate.id + '/image?time=' + teammate.update_at + '&' + Utils.getSessionIndex()}
- height='50'
- width='50'
- />
- </div>
- <div className='channel-intro-profile'>
- <strong><UserProfile userId={teammate.id} /></strong>
- </div>
- <p className='channel-intro-text'>
- {'This is the start of your direct message history with ' + teammateName + '.'}<br/>
- {'Direct messages and files shared here are not shown to people outside this area.'}
- </p>
- <a
- className='intro-links'
- href='#'
- data-toggle='modal'
- data-target='#edit_channel'
- data-header={channel.header}
- data-title={channel.display_name}
- data-channelid={channel.id}
- >
- <i className='fa fa-pencil'></i>{'Set a header'}
- </a>
- </div>
- );
- }
-
- return (
- <div className='channel-intro'>
- <p className='channel-intro-text'>{'This is the start of your direct message history with this teammate. Direct messages and files shared here are not shown to people outside this area.'}</p>
- </div>
- );
- }
- createChannelIntroMessage(channel) {
- if (channel.type === 'D') {
- return this.createDMIntroMessage(channel);
- } else if (ChannelStore.isDefault(channel)) {
- return this.createDefaultIntroMessage(channel);
- } else if (channel.name === Constants.OFFTOPIC_CHANNEL) {
- return this.createOffTopicIntroMessage(channel);
- } else if (channel.type === 'O' || channel.type === 'P') {
- return this.createStandardIntroMessage(channel);
- }
- }
- createDefaultIntroMessage(channel) {
- const team = TeamStore.getCurrent();
- let inviteModalLink;
- if (team.type === Constants.INVITE_TEAM) {
- inviteModalLink = (
- <a
- className='intro-links'
- href='#'
- data-toggle='modal'
- data-target='#invite_member'
- >
- <i className='fa fa-user-plus'></i>{'Invite others to this team'}
- </a>
- );
- } else {
- inviteModalLink = (
- <a
- className='intro-links'
- href='#'
- data-toggle='modal'
- data-target='#get_link'
- data-title='Team Invite'
- data-value={Utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + team.id}
- >
- <i className='fa fa-user-plus'></i>{'Invite others to this team'}
- </a>
- );
- }
-
- return (
- <div className='channel-intro'>
- <h4 className='channel-intro__title'>{'Beginning of ' + channel.display_name}</h4>
- <p className='channel-intro__content'>
- <strong>{'Welcome to ' + channel.display_name + '!'}</strong>
- <br/><br/>
- {'This is the first channel teammates see when they sign up - use it for posting updates everyone needs to know.'}
- </p>
- {inviteModalLink}
- <a
- className='intro-links'
- href='#'
- data-toggle='modal'
- data-target='#edit_channel'
- data-header={channel.header}
- data-title={channel.display_name}
- data-channelid={channel.id}
- >
- <i className='fa fa-pencil'></i>{'Set a header'}
- </a>
- <br/>
- </div>
- );
- }
- createOffTopicIntroMessage(channel) {
- return (
- <div className='channel-intro'>
- <h4 className='channel-intro__title'>{'Beginning of ' + channel.display_name}</h4>
- <p className='channel-intro__content'>
- {'This is the start of ' + channel.display_name + ', a channel for non-work-related conversations.'}
- <br/>
- </p>
- <a
- className='intro-links'
- href='#'
- data-toggle='modal'
- data-target='#edit_channel'
- data-header={channel.header}
- data-title={channel.display_name}
- data-channelid={channel.id}
- >
- <i className='fa fa-pencil'></i>{'Set a header'}
- </a>
- <a
- className='intro-links'
- href='#'
- data-toggle='modal'
- data-target='#channel_invite'
- >
- <i className='fa fa-user-plus'></i>{'Invite others to this channel'}
- </a>
- </div>
- );
- }
- getChannelCreator(channel) {
- if (channel.creator_id.length > 0) {
- var creator = UserStore.getProfile(channel.creator_id);
- if (creator) {
- return creator.username;
- }
- }
-
- var members = ChannelStore.getExtraInfo(channel.id).members;
- for (var i = 0; i < members.length; i++) {
- if (Utils.isAdmin(members[i].roles)) {
- return members[i].username;
- }
- }
- }
- createStandardIntroMessage(channel) {
- var uiName = channel.display_name;
- var creatorName = '';
-
- var uiType;
- var memberMessage;
- if (channel.type === 'P') {
- uiType = 'private group';
- memberMessage = ' Only invited members can see this private group.';
- } else {
- uiType = 'channel';
- memberMessage = ' Any member can join and read this channel.';
- }
-
- var createMessage;
- if (creatorName === '') {
- createMessage = 'This is the start of the ' + uiName + ' ' + uiType + ', created on ' + Utils.displayDate(channel.create_at) + '.';
- } else {
- createMessage = (<span>This is the start of the <strong>{uiName}</strong> {uiType}, created by <strong>{creatorName}</strong> on <strong>{Utils.displayDate(channel.create_at)}</strong></span>);
- }
-
- return (
- <div className='channel-intro'>
- <h4 className='channel-intro__title'>{'Beginning of ' + uiName}</h4>
- <p className='channel-intro__content'>
- {createMessage}
- {memberMessage}
- <br/>
- </p>
- <a
- className='intro-links'
- href='#'
- data-toggle='modal'
- data-target='#edit_channel'
- data-header={channel.header}
- data-title={channel.display_name}
- data-channelid={channel.id}
- >
- <i className='fa fa-pencil'></i>{'Set a header'}
- </a>
- <a
- className='intro-links'
- href='#'
- data-toggle='modal'
- data-target='#channel_invite'
- >
- <i className='fa fa-user-plus'></i>{'Invite others to this ' + uiType}
- </a>
- </div>
- );
- }
- createPosts(posts, order) {
- var postCtls = [];
- var previousPostDay = new Date(0);
- var userId = UserStore.getCurrentId();
-
- var renderedLastViewed = false;
- var lastViewed = Number.MAX_VALUE;
-
- if (ChannelStore.getMember(this.props.channelId) != null) {
- lastViewed = ChannelStore.getMember(this.props.channelId).last_viewed_at;
- }
-
- var numToDisplay = this.state.numToDisplay;
- if (order.length - 1 < numToDisplay) {
- numToDisplay = order.length - 1;
- }
-
- for (var i = numToDisplay; i >= 0; i--) {
- var post = posts[order[i]];
- var parentPost = posts[post.parent_id];
-
- // If the post is a comment whose parent has been deleted, don't add it to the list.
- if (parentPost && parentPost.state === Constants.POST_DELETED) {
- continue;
- }
-
- var sameUser = false;
- var sameRoot = false;
- var hideProfilePic = false;
- var prevPost = posts[order[i + 1]];
-
- if (prevPost) {
- sameUser = prevPost.user_id === post.user_id && post.create_at - prevPost.create_at <= 1000 * 60 * 5;
-
- sameRoot = Utils.isComment(post) && (prevPost.id === post.root_id || prevPost.root_id === post.root_id);
-
- // hide the profile pic if:
- // the previous post was made by the same user as the current post,
- // the previous post is not a comment,
- // the current post is not a comment,
- // the current post is not from a webhook
- // and the previous post is not from a webhook
- if ((prevPost.user_id === post.user_id) &&
- !Utils.isComment(prevPost) &&
- !Utils.isComment(post) &&
- (!post.props || !post.props.from_webhook) &&
- (!prevPost.props || !prevPost.props.from_webhook)) {
- hideProfilePic = true;
- }
- }
-
- // check if it's the last comment in a consecutive string of comments on the same post
- // it is the last comment if it is last post in the channel or the next post has a different root post
- var isLastComment = Utils.isComment(post) && (i === 0 || posts[order[i - 1]].root_id !== post.root_id);
-
- var postCtl = (
- <Post
- key={post.id + 'postKey'}
- ref={post.id}
- sameUser={sameUser}
- sameRoot={sameRoot}
- post={post}
- parentPost={parentPost}
- posts={posts}
- hideProfilePic={hideProfilePic}
- isLastComment={isLastComment}
- resize={this.updateScroll}
- />
- );
-
- const currentPostDay = Utils.getDateForUnixTicks(post.create_at);
- if (currentPostDay.toDateString() !== previousPostDay.toDateString()) {
- postCtls.push(
- <div
- key={currentPostDay.toDateString()}
- className='date-separator'
- >
- <hr className='separator__hr' />
- <div className='separator__text'>{currentPostDay.toDateString()}</div>
- </div>
- );
- }
-
- if (post.user_id !== userId && post.create_at > lastViewed && !renderedLastViewed) {
- renderedLastViewed = true;
-
- // Temporary fix to solve ie11 rendering issue
- let newSeparatorId = '';
- if (!Utils.isBrowserIE()) {
- newSeparatorId = 'new_message_' + this.props.channelId;
- }
- postCtls.push(
- <div
- id={newSeparatorId}
- key='unviewed'
- className='new-separator'
- >
- <hr
- className='separator__hr'
- />
- <div className='separator__text'>{'New Messages'}</div>
- </div>
- );
- }
- postCtls.push(postCtl);
- previousPostDay = currentPostDay;
- }
-
- return postCtls;
- }
- loadMorePosts() {
- if (this.state.postList == null) {
- return;
- }
-
- var posts = this.state.postList.posts;
- var order = this.state.postList.order;
- var channelId = this.props.channelId;
-
- $(ReactDOM.findDOMNode(this.refs.loadmore)).text('Retrieving more messages...');
-
- Client.getPostsPage(
- channelId,
- order.length,
- Constants.POST_CHUNK_SIZE,
- function success(data) {
- $(ReactDOM.findDOMNode(this.refs.loadmore)).text('Load more messages');
- this.gotMorePosts = true;
- this.setState({numToDisplay: this.state.numToDisplay + Constants.POST_CHUNK_SIZE});
-
- if (!data) {
- return;
- }
-
- if (data.order.length === 0) {
- return;
- }
-
- var postList = {};
- postList.posts = $.extend(posts, data.posts);
- postList.order = order.concat(data.order);
-
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECIEVED_POSTS,
- id: channelId,
- post_list: postList
- });
-
- Client.getProfiles();
- }.bind(this),
- function fail(err) {
- $(ReactDOM.findDOMNode(this.refs.loadmore)).text('Load more messages');
- AsyncClient.dispatchError(err, 'getPosts');
- }.bind(this)
- );
- }
- render() {
- var order = [];
- var posts;
- var channel = ChannelStore.get(this.props.channelId);
-
- if (this.state.postList != null) {
- posts = this.state.postList.posts;
- order = this.state.postList.order;
- }
-
- var moreMessages = <p className='beginning-messages-text'>{'Beginning of Channel'}</p>;
- if (channel != null) {
- if (order.length >= this.state.numToDisplay) {
- moreMessages = (
- <a
- ref='loadmore'
- className='more-messages-text theme'
- href='#'
- onClick={this.loadMorePosts}
- >
- {'Load more messages'}
- </a>
- );
- } else {
- moreMessages = this.createChannelIntroMessage(channel);
- }
- }
-
- var postCtls = [];
- if (posts && this.state.isFirstLoadComplete) {
- postCtls = this.createPosts(posts, order);
- } else {
- postCtls.push(
- <LoadingScreen
- position='absolute'
- key='loading'
- />);
- }
-
- var activeClass = '';
- if (!this.props.isActive) {
- activeClass = 'inactive';
- }
-
- return (
- <div
- ref='postlist'
- className={'post-list-holder-by-time ' + activeClass}
- >
- <div className='post-list__table'>
- <div
- ref='postlistcontent'
- className='post-list__content'
- >
- {moreMessages}
- {postCtls}
- </div>
- </div>
- </div>
- );
- }
-}
-
-PostList.defaultProps = {
- isActive: false,
- channelId: null
-};
-PostList.propTypes = {
- isActive: React.PropTypes.bool,
- channelId: React.PropTypes.string
-};
diff --git a/web/react/components/post_list_container.jsx b/web/react/components/post_list_container.jsx
deleted file mode 100644
index 09cee6218..000000000
--- a/web/react/components/post_list_container.jsx
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-const PostList = require('./post_list.jsx');
-const ChannelStore = require('../stores/channel_store.jsx');
-
-export default class PostListContainer extends React.Component {
- constructor() {
- super();
-
- this.onChange = this.onChange.bind(this);
- this.onLeave = this.onLeave.bind(this);
-
- let currentChannelId = ChannelStore.getCurrentId();
- if (currentChannelId) {
- this.state = {currentChannelId: currentChannelId, postLists: [currentChannelId]};
- } else {
- this.state = {currentChannelId: null, postLists: []};
- }
- }
- componentDidMount() {
- ChannelStore.addChangeListener(this.onChange);
- ChannelStore.addLeaveListener(this.onLeave);
- }
- onChange() {
- let channelId = ChannelStore.getCurrentId();
- if (channelId === this.state.currentChannelId) {
- return;
- }
-
- let postLists = this.state.postLists;
- if (postLists.indexOf(channelId) === -1) {
- postLists.push(channelId);
- }
- this.setState({currentChannelId: channelId, postLists: postLists});
- }
- onLeave(id) {
- let postLists = this.state.postLists;
- var index = postLists.indexOf(id);
- if (index !== -1) {
- postLists.splice(index, 1);
- }
- }
- render() {
- let postLists = this.state.postLists;
- let channelId = this.state.currentChannelId;
-
- let postListCtls = [];
- for (let i = 0; i <= this.state.postLists.length - 1; i++) {
- postListCtls.push(
- <PostList
- key={'postlistkey' + i}
- channelId={postLists[i]}
- isActive={postLists[i] === channelId}
- />
- );
- }
-
- return (
- <div>{postListCtls}</div>
- );
- }
-}
diff --git a/web/react/components/posts_view.jsx b/web/react/components/posts_view.jsx
new file mode 100644
index 000000000..2b81d1d79
--- /dev/null
+++ b/web/react/components/posts_view.jsx
@@ -0,0 +1,303 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+const UserStore = require('../stores/user_store.jsx');
+const Utils = require('../utils/utils.jsx');
+const Post = require('./post.jsx');
+const Constants = require('../utils/constants.jsx');
+
+export default class PostsView extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.handleScroll = this.handleScroll.bind(this);
+ this.isAtBottom = this.isAtBottom.bind(this);
+ this.loadMorePostsTop = this.loadMorePostsTop.bind(this);
+ this.createPosts = this.createPosts.bind(this);
+ this.updateScrolling = this.updateScrolling.bind(this);
+ this.handleResize = this.handleResize.bind(this);
+
+ this.jumpToPostNode = null;
+ this.wasAtBottom = true;
+ this.scrollHeight = 0;
+ }
+ static get SCROLL_TYPE_FREE() {
+ return 1;
+ }
+ static get SCROLL_TYPE_BOTTOM() {
+ return 2;
+ }
+ static get SIDEBAR_OPEN() {
+ return 3;
+ }
+ isAtBottom() {
+ return ((this.refs.postlist.scrollHeight - this.refs.postlist.scrollTop) === this.refs.postlist.clientHeight);
+ }
+ handleScroll() {
+ // HACK FOR RHS -- REMOVE WHEN RHS DIES
+ const childNodes = this.refs.postlistcontent.childNodes;
+ for (let i = 0; i < childNodes.length; i++) {
+ // If the node is 1/3 down the page
+ if (childNodes[i].offsetTop > (this.refs.postlist.scrollTop + (this.refs.postlist.offsetHeight / 3))) {
+ this.jumpToPostNode = childNodes[i];
+ break;
+ }
+ }
+ this.wasAtBottom = this.isAtBottom();
+
+ // --- --------
+
+ this.props.postViewScrolled(this.isAtBottom());
+ this.prevScrollHeight = this.refs.postlist.scrollHeight;
+ }
+ loadMorePostsTop() {
+ this.props.loadMorePostsTopClicked();
+ }
+ createPosts(posts, order) {
+ const postCtls = [];
+ let previousPostDay = new Date(0);
+ const userId = UserStore.getCurrentId();
+
+ let renderedLastViewed = false;
+
+ let numToDisplay = this.props.numPostsToDisplay;
+ if (order.length - 1 < numToDisplay) {
+ numToDisplay = order.length - 1;
+ }
+
+ for (let i = numToDisplay; i >= 0; i--) {
+ const post = posts[order[i]];
+ const parentPost = posts[post.parent_id];
+ const prevPost = posts[order[i + 1]];
+
+ // If the post is a comment whose parent has been deleted, don't add it to the list.
+ if (parentPost && parentPost.state === Constants.POST_DELETED) {
+ continue;
+ }
+
+ let sameUser = false;
+ let sameRoot = false;
+ let hideProfilePic = false;
+
+ if (prevPost) {
+ sameUser = prevPost.user_id === post.user_id && post.create_at - prevPost.create_at <= 1000 * 60 * 5;
+
+ sameRoot = Utils.isComment(post) && (prevPost.id === post.root_id || prevPost.root_id === post.root_id);
+
+ // hide the profile pic if:
+ // the previous post was made by the same user as the current post,
+ // the previous post is not a comment,
+ // the current post is not a comment,
+ // the current post is not from a webhook
+ // and the previous post is not from a webhook
+ if ((prevPost.user_id === post.user_id) &&
+ !Utils.isComment(prevPost) &&
+ !Utils.isComment(post) &&
+ (!post.props || !post.props.from_webhook) &&
+ (!prevPost.props || !prevPost.props.from_webhook)) {
+ hideProfilePic = true;
+ }
+ }
+
+ // check if it's the last comment in a consecutive string of comments on the same post
+ // it is the last comment if it is last post in the channel or the next post has a different root post
+ var isLastComment = Utils.isComment(post) && (i === 0 || posts[order[i - 1]].root_id !== post.root_id);
+
+ var postCtl = (
+ <Post
+ key={post.id + 'postKey'}
+ ref={post.id}
+ sameUser={sameUser}
+ sameRoot={sameRoot}
+ post={post}
+ parentPost={parentPost}
+ posts={posts}
+ hideProfilePic={hideProfilePic}
+ isLastComment={isLastComment}
+ />
+ );
+
+ const currentPostDay = Utils.getDateForUnixTicks(post.create_at);
+ if (currentPostDay.toDateString() !== previousPostDay.toDateString()) {
+ postCtls.push(
+ <div
+ key={currentPostDay.toDateString()}
+ className='date-separator'
+ >
+ <hr className='separator__hr' />
+ <div className='separator__text'>{currentPostDay.toDateString()}</div>
+ </div>
+ );
+ }
+
+ if (post.user_id !== userId &&
+ this.props.messageSeparatorTime !== 0 &&
+ post.create_at > this.props.messageSeparatorTime &&
+ !renderedLastViewed) {
+ renderedLastViewed = true;
+
+ // Temporary fix to solve ie11 rendering issue
+ let newSeparatorId = '';
+ if (!Utils.isBrowserIE()) {
+ newSeparatorId = 'new_message_' + post.id;
+ }
+ postCtls.push(
+ <div
+ id={newSeparatorId}
+ key='unviewed'
+ className='new-separator'
+ >
+ <hr
+ className='separator__hr'
+ />
+ <div className='separator__text'>{'New Messages'}</div>
+ </div>
+ );
+ }
+ postCtls.push(postCtl);
+ previousPostDay = currentPostDay;
+ }
+
+ return postCtls;
+ }
+ updateScrolling() {
+ if (this.props.scrollType === PostsView.SCROLL_TYPE_BOTTOM) {
+ window.requestAnimationFrame(() => {
+ this.refs.postlist.scrollTop = this.refs.postlist.scrollHeight;
+ });
+ } else if (this.props.scrollType === PostsView.SCROLL_TYPE_POST && this.props.scrollPost) {
+ window.requestAnimationFrame(() => {
+ const postNode = ReactDOM.findDOMNode(this.refs[this.props.scrollPost]);
+ postNode.scrollIntoView();
+ if (this.refs.postlist.scrollTop === postNode.offsetTop) {
+ this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / 3);
+ } else {
+ this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / 3) + (this.refs.postlist.scrollTop - postNode.offsetTop);
+ }
+ });
+ } else if (this.props.scrollType === PostsView.SIDEBAR_OPEN) {
+ // If we are at the bottom then stay there
+ if (this.wasAtBottom) {
+ this.refs.postlist.scrollTop = this.refs.postlist.scrollHeight;
+ } else {
+ window.requestAnimationFrame(() => {
+ this.jumpToPostNode.scrollIntoView();
+ if (this.refs.postlist.scrollTop === this.jumpToPostNode.offsetTop) {
+ this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / 3);
+ } else {
+ this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / 3) + (this.refs.postlist.scrollTop - this.jumpToPostNode.offsetTop);
+ }
+ });
+ }
+ } else if (this.refs.postlist.scrollHeight !== this.prevScrollHeight) {
+ window.requestAnimationFrame(() => {
+ this.refs.postlist.scrollTop += (this.refs.postlist.scrollHeight - this.prevScrollHeight);
+ });
+ }
+ }
+ handleResize() {
+ this.updateScrolling();
+ }
+ componentDidMount() {
+ this.updateScrolling();
+ window.addEventListener('resize', this.handleResize);
+ }
+ componentWillUnmount() {
+ window.removeEventListener('resize', this.handleResize);
+ }
+ componentDidUpdate() {
+ this.updateScrolling();
+ }
+ shouldComponentUpdate(nextProps) {
+ if (this.props.isActive !== nextProps.isActive) {
+ return true;
+ }
+ if (this.props.postList !== nextProps.postList) {
+ return true;
+ }
+ if (this.props.scrollPost !== nextProps.scrollPost) {
+ return true;
+ }
+ if (this.props.scrollType !== nextProps.scrollType && nextProps.scrollType !== PostsView.SCROLL_TYPE_FREE) {
+ return true;
+ }
+ if (this.props.numPostsToDisplay !== nextProps.numPostsToDisplay) {
+ return true;
+ }
+ if (this.props.messageSeparatorTime !== nextProps.messageSeparatorTime) {
+ return true;
+ }
+ if (!Utils.areStatesEqual(this.props.postList, nextProps.postList)) {
+ return true;
+ }
+
+ return false;
+ }
+ render() {
+ let posts = [];
+ let order = [];
+ let moreMessages;
+ let postElements;
+ let activeClass = 'inactive';
+ if (this.props.postList != null) {
+ posts = this.props.postList.posts;
+ order = this.props.postList.order;
+
+ // Create intro message or top loadmore link
+ if (order.length >= this.props.numPostsToDisplay) {
+ moreMessages = (
+ <a
+ ref='loadmore'
+ className='more-messages-text theme'
+ href='#'
+ onClick={this.loadMorePostsTop}
+ >
+ {'Load more messages'}
+ </a>
+ );
+ } else {
+ moreMessages = this.props.introText;
+ }
+
+ // Create post elements
+ postElements = this.createPosts(posts, order);
+
+ // Show ourselves if we are marked active
+ if (this.props.isActive) {
+ activeClass = '';
+ }
+ }
+
+ return (
+ <div
+ ref='postlist'
+ className={'post-list-holder-by-time ' + activeClass}
+ onScroll={this.handleScroll}
+ >
+ <div className='post-list__table'>
+ <div
+ ref='postlistcontent'
+ className='post-list__content'
+ >
+ {moreMessages}
+ {postElements}
+ </div>
+ </div>
+ </div>
+ );
+ }
+}
+PostsView.defaultProps = {
+};
+
+PostsView.propTypes = {
+ isActive: React.PropTypes.bool,
+ postList: React.PropTypes.object,
+ scrollPost: React.PropTypes.string,
+ scrollType: React.PropTypes.number,
+ postViewScrolled: React.PropTypes.func.isRequired,
+ loadMorePostsTopClicked: React.PropTypes.func.isRequired,
+ numPostsToDisplay: React.PropTypes.number,
+ introText: React.PropTypes.element,
+ messageSeparatorTime: React.PropTypes.number
+};
diff --git a/web/react/components/posts_view_container.jsx b/web/react/components/posts_view_container.jsx
new file mode 100644
index 000000000..7671ca01d
--- /dev/null
+++ b/web/react/components/posts_view_container.jsx
@@ -0,0 +1,267 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+const PostsView = require('./posts_view.jsx');
+const LoadingScreen = require('./loading_screen.jsx');
+
+const ChannelStore = require('../stores/channel_store.jsx');
+const PostStore = require('../stores/post_store.jsx');
+
+const Utils = require('../utils/utils.jsx');
+const Client = require('../utils/client.jsx');
+const AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
+const AsyncClient = require('../utils/async_client.jsx');
+
+const Constants = require('../utils/constants.jsx');
+const ActionTypes = Constants.ActionTypes;
+
+import {createChannelIntroMessage} from '../utils/channel_intro_mssages.jsx';
+
+export default class PostsViewContainer extends React.Component {
+ constructor() {
+ super();
+
+ this.onChannelChange = this.onChannelChange.bind(this);
+ this.onChannelLeave = this.onChannelLeave.bind(this);
+ this.onPostsChange = this.onPostsChange.bind(this);
+ this.handlePostsViewScroll = this.handlePostsViewScroll.bind(this);
+ this.loadMorePostsTop = this.loadMorePostsTop.bind(this);
+ this.postsLoaded = this.postsLoaded.bind(this);
+ this.postsLoadedFailure = this.postsLoadedFailure.bind(this);
+ this.handlePostsViewJumpRequest = this.handlePostsViewJumpRequest.bind(this);
+
+ const currentChannelId = ChannelStore.getCurrentId();
+ const state = {
+ scrollType: PostsView.SCROLL_TYPE_BOTTOM,
+ scrollPost: null,
+ numPostsToDisplay: Constants.POST_CHUNK_SIZE
+ };
+ if (currentChannelId) {
+ Object.assign(state, {
+ currentChannelIndex: 0,
+ channels: [currentChannelId],
+ postLists: [this.getChannelPosts(currentChannelId)]
+ });
+ } else {
+ Object.assign(state, {
+ currentChannelIndex: null,
+ channels: [],
+ postLists: []
+ });
+ }
+
+ this.state = state;
+ }
+ componentDidMount() {
+ ChannelStore.addChangeListener(this.onChannelChange);
+ ChannelStore.addLeaveListener(this.onChannelLeave);
+ PostStore.addChangeListener(this.onPostsChange);
+ PostStore.addPostsViewJumpListener(this.handlePostsViewJumpRequest);
+ }
+ componentWillUnmount() {
+ ChannelStore.removeChangeListener(this.onChannelChange);
+ ChannelStore.removeLeaveListener(this.onChannelLeave);
+ PostStore.removeChangeListener(this.onPostsChange);
+ PostStore.removePostsViewJumpListener(this.handlePostsViewJumpRequest);
+ }
+ handlePostsViewJumpRequest(type, post) {
+ switch (type) {
+ case Constants.PostsViewJumpTypes.BOTTOM:
+ this.setState({scrollType: PostsView.SCROLL_TYPE_BOTTOM});
+ break;
+ case Constants.PostsViewJumpTypes.POST:
+ this.setState({
+ scrollType: PostsView.SCROLL_TYPE_POST,
+ scrollPost: post
+ });
+ break;
+ case Constants.PostsViewJumpTypes.SIDEBAR_OPEN:
+ this.setState({scrollType: PostsView.SIDEBAR_OPEN});
+ break;
+ }
+ }
+ onChannelChange() {
+ const postLists = Object.assign({}, this.state.postLists);
+ const channels = this.state.channels.slice();
+ const channelId = ChannelStore.getCurrentId();
+
+ // Has the channel really changed?
+ if (channelId === channels[this.state.currentChannelIndex]) {
+ return;
+ }
+
+ PostStore.clearUnseenDeletedPosts(channelId);
+
+ let lastViewed = Number.MAX_VALUE;
+ const member = ChannelStore.getMember(channelId);
+ if (member != null) {
+ lastViewed = member.last_viewed_at;
+ }
+
+ let newIndex = channels.indexOf(channelId);
+ if (newIndex === -1) {
+ newIndex = channels.length;
+ channels.push(channelId);
+ postLists[newIndex] = this.getChannelPosts(channelId);
+ }
+ this.setState({
+ currentChannelIndex: newIndex,
+ currentLastViewed: lastViewed,
+ scrollType: PostsView.SCROLL_TYPE_BOTTOM,
+ channels,
+ postLists});
+ }
+ onChannelLeave(id) {
+ const postLists = Object.assign({}, this.state.postLists);
+ const channels = this.state.channels.slice();
+ const index = channels.indexOf(id);
+ if (index !== -1) {
+ postLists.splice(index, 1);
+ channels.splice(index, 1);
+ }
+ this.setState({channels, postLists});
+ }
+ onPostsChange() {
+ const channels = this.state.channels;
+ const postLists = Object.assign({}, this.state.postLists);
+ const newPostsView = this.getChannelPosts(channels[this.state.currentChannelIndex]);
+
+ postLists[this.state.currentChannelIndex] = newPostsView;
+ this.setState({postLists});
+ }
+ getChannelPosts(id) {
+ const postList = PostStore.getPosts(id);
+
+ if (postList != null) {
+ const deletedPosts = PostStore.getUnseenDeletedPosts(id);
+
+ if (deletedPosts && Object.keys(deletedPosts).length > 0) {
+ for (const pid in deletedPosts) {
+ if (deletedPosts.hasOwnProperty(pid)) {
+ postList.posts[pid] = deletedPosts[pid];
+ postList.order.unshift(pid);
+ }
+ }
+
+ postList.order.sort((a, b) => {
+ if (postList.posts[a].create_at > postList.posts[b].create_at) {
+ return -1;
+ }
+ if (postList.posts[a].create_at < postList.posts[b].create_at) {
+ return 1;
+ }
+ return 0;
+ });
+ }
+
+ const pendingPostList = PostStore.getPendingPosts(id);
+
+ if (pendingPostList) {
+ postList.order = pendingPostList.order.concat(postList.order);
+ for (const ppid in pendingPostList.posts) {
+ if (pendingPostList.posts.hasOwnProperty(ppid)) {
+ postList.posts[ppid] = pendingPostList.posts[ppid];
+ }
+ }
+ }
+ }
+
+ return postList;
+ }
+ loadMorePostsTop() {
+ const postLists = this.state.postLists;
+ const channels = this.state.channels;
+ const currentChannelId = channels[this.state.currentChannelIndex];
+ const currentPostList = postLists[this.state.currentChannelIndex];
+
+ this.setState({numPostsToDisplay: this.state.numPostsToDisplay + Constants.POST_CHUNK_SIZE});
+
+ Client.getPostsPage(
+ currentChannelId,
+ currentPostList.order.length,
+ Constants.POST_CHUNK_SIZE,
+ this.postsLoaded,
+ this.postsLoadedFailure
+ );
+ }
+ postsLoaded(data) {
+ if (!data) {
+ return;
+ }
+
+ if (data.order.length === 0) {
+ return;
+ }
+
+ const postLists = this.state.postLists;
+ const currentPostList = postLists[this.state.currentChannelIndex];
+ const channels = this.state.channels;
+ const currentChannelId = channels[this.state.currentChannelIndex];
+
+ var newPostList = {};
+ newPostList.posts = Object.assign(currentPostList.posts, data.posts);
+ newPostList.order = currentPostList.order.concat(data.order);
+
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECIEVED_POSTS,
+ id: currentChannelId,
+ post_list: newPostList
+ });
+
+ Client.getProfiles();
+ }
+ postsLoadedFailure(err) {
+ AsyncClient.dispatchError(err, 'getPosts');
+ }
+ handlePostsViewScroll(atBottom) {
+ if (atBottom) {
+ this.setState({scrollType: PostsView.SCROLL_TYPE_BOTTOM});
+ } else {
+ this.setState({scrollType: PostsView.SCROLL_TYPE_FREE});
+ }
+ }
+ shouldComponentUpdate(nextProps, nextState) {
+ if (Utils.areStatesEqual(this.state, nextState)) {
+ return false;
+ }
+
+ return true;
+ }
+ render() {
+ const postLists = this.state.postLists;
+ const channels = this.state.channels;
+ const currentChannelId = channels[this.state.currentChannelIndex];
+ const channel = ChannelStore.get(currentChannelId);
+
+ const postListCtls = [];
+ for (let i = 0; i < channels.length; i++) {
+ const isActive = (channels[i] === currentChannelId);
+ postListCtls.push(
+ <PostsView
+ key={'postsviewkey' + i}
+ isActive={isActive}
+ postList={postLists[i]}
+ scrollType={this.state.scrollType}
+ scrollPost={this.state.scrollPost}
+ postViewScrolled={this.handlePostsViewScroll}
+ loadMorePostsTopClicked={this.loadMorePostsTop}
+ numPostsToDisplay={this.state.numPostsToDisplay}
+ introText={channel ? createChannelIntroMessage(channel) : null}
+ messageSeparatorTime={this.state.currentLastViewed}
+ />
+ );
+ if ((!postLists[i] || !channel) && isActive) {
+ postListCtls.push(
+ <LoadingScreen
+ position='absolute'
+ key='loading'
+ />
+ );
+ }
+ }
+
+ return (
+ <div>{postListCtls}</div>
+ );
+ }
+}
diff --git a/web/react/components/rhs_thread.jsx b/web/react/components/rhs_thread.jsx
index bcdec2870..fe57bed28 100644
--- a/web/react/components/rhs_thread.jsx
+++ b/web/react/components/rhs_thread.jsx
@@ -34,12 +34,12 @@ export default class RhsThread extends React.Component {
}
var channelId = postList.posts[postList.order[0]].channel_id;
- var pendingPostList = PostStore.getPendingPosts(channelId);
+ var pendingPostsList = PostStore.getPendingPosts(channelId);
- if (pendingPostList) {
- for (var pid in pendingPostList.posts) {
- if (pendingPostList.posts.hasOwnProperty(pid)) {
- postList.posts[pid] = pendingPostList.posts[pid];
+ if (pendingPostsList) {
+ for (var pid in pendingPostsList.posts) {
+ if (pendingPostsList.posts.hasOwnProperty(pid)) {
+ postList.posts[pid] = pendingPostsList.posts[pid];
}
}
}
diff --git a/web/react/components/search_autocomplete.jsx b/web/react/components/search_autocomplete.jsx
index f7d772677..03e14ec49 100644
--- a/web/react/components/search_autocomplete.jsx
+++ b/web/react/components/search_autocomplete.jsx
@@ -142,7 +142,10 @@ export default class SearchAutocomplete extends React.Component {
let channels = ChannelStore.getAll();
if (filter) {
- channels = channels.filter((channel) => channel.name.startsWith(filter));
+ channels = channels.filter((channel) => channel.name.startsWith(filter) && channel.type !== 'D');
+ } else {
+ // don't show direct channels
+ channels = channels.filter((channel) => channel.type !== 'D');
}
channels.sort((a, b) => a.name.localeCompare(b.name));
diff --git a/web/react/components/search_bar.jsx b/web/react/components/search_bar.jsx
index 1b81a5ee0..90865475b 100644
--- a/web/react/components/search_bar.jsx
+++ b/web/react/components/search_bar.jsx
@@ -90,14 +90,10 @@ export default class SearchBar extends React.Component {
this.refs.autocomplete.handleInputChange(e.target, term);
}
- handleMouseInput(e) {
- e.preventDefault();
- }
handleUserBlur() {
this.setState({focused: false});
}
- handleUserFocus(e) {
- e.target.select();
+ handleUserFocus() {
$('.search-bar__container').addClass('focused');
this.setState({focused: true});
@@ -106,14 +102,8 @@ export default class SearchBar extends React.Component {
if (terms.length) {
this.setState({isSearching: true});
- // append * if not present
- let searchTerms = terms;
- if (searchTerms.search(/\*\s*$/) === -1) {
- searchTerms = searchTerms + '*';
- }
-
client.search(
- searchTerms,
+ terms,
(data) => {
this.setState({isSearching: false});
if (utils.isMobile()) {
@@ -198,7 +188,6 @@ export default class SearchBar extends React.Component {
onBlur={this.handleUserBlur}
onChange={this.handleUserInput}
onKeyDown={this.handleKeyDown}
- onMouseUp={this.handleMouseInput}
/>
{isSearching}
<SearchAutocomplete
diff --git a/web/react/components/search_results.jsx b/web/react/components/search_results.jsx
index ce19c48f0..b56a7b006 100644
--- a/web/react/components/search_results.jsx
+++ b/web/react/components/search_results.jsx
@@ -83,7 +83,16 @@ export default class SearchResults extends React.Component {
var ctls = null;
if (noResults) {
- ctls = <div className='sidebar--right__subheader'>No results</div>;
+ ctls =
+ (
+ <div className='sidebar--right__subheader'>
+ <h4>{'NO RESULTS'}</h4>
+ <ul>
+ <li>If you're searching a partial phrase (ex. searching "rea", looking for "reach" or "reaction"), append a * to your search term</li>
+ <li>Due to the volume of results, two letter searches and common words like "this", "a" and "is" won't appear in search results</li>
+ </ul>
+ </div>
+ );
} else {
ctls = results.order.map(function mymap(id) {
var post = results.posts[id];
diff --git a/web/react/components/settings_sidebar.jsx b/web/react/components/settings_sidebar.jsx
index 4af46c35a..68d9cea48 100644
--- a/web/react/components/settings_sidebar.jsx
+++ b/web/react/components/settings_sidebar.jsx
@@ -1,10 +1,14 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
+var utils = require('../utils/utils.jsx');
export default class SettingsSidebar extends React.Component {
componentDidUpdate() {
$('.settings-modal').find('.modal-body').scrollTop(0);
$('.settings-modal').find('.modal-body').perfectScrollbar('update');
+ if (utils.isSafari()) {
+ $('.settings-modal .settings-links .nav').addClass('absolute');
+ }
}
constructor(props) {
super(props);
diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx
index 5cb6d168b..c47919885 100644
--- a/web/react/components/sidebar.jsx
+++ b/web/react/components/sidebar.jsx
@@ -1,19 +1,26 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-const AsyncClient = require('../utils/async_client.jsx');
-const ChannelStore = require('../stores/channel_store.jsx');
-const Client = require('../utils/client.jsx');
-const Constants = require('../utils/constants.jsx');
-const PreferenceStore = require('../stores/preference_store.jsx');
const NewChannelFlow = require('./new_channel_flow.jsx');
const MoreDirectChannels = require('./more_direct_channels.jsx');
const SearchBox = require('./search_bar.jsx');
const SidebarHeader = require('./sidebar_header.jsx');
-const TeamStore = require('../stores/team_store.jsx');
const UnreadChannelIndicator = require('./unread_channel_indicator.jsx');
+const TutorialTip = require('./tutorial/tutorial_tip.jsx');
+
+const ChannelStore = require('../stores/channel_store.jsx');
const UserStore = require('../stores/user_store.jsx');
+const TeamStore = require('../stores/team_store.jsx');
+const PreferenceStore = require('../stores/preference_store.jsx');
+
+const AsyncClient = require('../utils/async_client.jsx');
+const Client = require('../utils/client.jsx');
const Utils = require('../utils/utils.jsx');
+
+const Constants = require('../utils/constants.jsx');
+const Preferences = Constants.Preferences;
+const TutorialSteps = Constants.TutorialSteps;
+
const Tooltip = ReactBootstrap.Tooltip;
const OverlayTrigger = ReactBootstrap.OverlayTrigger;
@@ -136,7 +143,7 @@ export default class Sidebar extends React.Component {
channel.type = 'D';
}
- channel.display_name = teammate.username;
+ channel.display_name = Utils.displayUsername(teammate.id);
channel.teammate_id = teammate.id;
channel.status = UserStore.getStatus(teammate.id);
@@ -155,12 +162,15 @@ export default class Sidebar extends React.Component {
visibleDirectChannels.sort(this.sortChannelsByDisplayName);
+ const tutorialPref = PreferenceStore.getPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), {value: '0'});
+
return {
activeId: currentId,
channels: ChannelStore.getAll(),
members,
visibleDirectChannels,
- hiddenDirectChannelCount
+ hiddenDirectChannelCount,
+ showTutorialTip: parseInt(tutorialPref.value, 10) === TutorialSteps.CHANNEL_POPOVER
};
}
@@ -178,10 +188,6 @@ export default class Sidebar extends React.Component {
window.addEventListener('resize', this.handleResize);
}
shouldComponentUpdate(nextProps, nextState) {
- if (!Utils.areStatesEqual(nextProps, this.props)) {
- return true;
- }
-
if (!Utils.areStatesEqual(nextState, this.state)) {
return true;
}
@@ -235,7 +241,7 @@ export default class Sidebar extends React.Component {
const unread = this.getUnreadCount();
const mentionTitle = unread.mentions > 0 ? '(' + unread.mentions + ') ' : '';
const unreadTitle = unread.msgs > 0 ? '* ' : '';
- document.title = mentionTitle + unreadTitle + currentChannelName + ' - ' + this.props.teamDisplayName + ' ' + currentSiteName;
+ document.title = mentionTitle + unreadTitle + currentChannelName + ' - ' + TeamStore.getCurrent().display_name + ' ' + currentSiteName;
}
}
onScroll() {
@@ -312,6 +318,51 @@ export default class Sidebar extends React.Component {
this.setState({showDirectChannelsModal: false});
}
+ createTutorialTip() {
+ const screens = [];
+
+ screens.push(
+ <div>
+ <h4>{'Channels'}</h4>
+ <p><strong>{'Channels'}</strong>{' organize conversations across different topics. They’re open to everyone on your team. To send private communications use '}<strong>{'Direct Messages'}</strong>{' for a single person or '}<strong>{'Private Groups'}</strong>{' for multiple people.'}
+ </p>
+ </div>
+ );
+
+ screens.push(
+ <div>
+ <h4>{'"Town Square" and "Off-Topic" channels'}</h4>
+ <p>{'Here are two public channels to start:'}</p>
+ <p>
+ <strong>{'Town Square'}</strong>{' is a place for team-wide communication. Everyone in your team is a member of this channel.'}
+ </p>
+ <p>
+ <strong>{'Off-Topic'}</strong>{' is a place for fun and humor outside of work-related channels. You and your team can decide what other channels to create.'}
+ </p>
+ </div>
+ );
+
+ screens.push(
+ <div>
+ <h4>{'Creating and Joining Channels'}</h4>
+ <p>
+ {'Click '}<strong>{'"More..."'}</strong>{' to create a new channel or join an existing one.'}
+ </p>
+ <p>
+ {'You can also create a new channel or private group by clicking the '}<strong>{'"+" symbol'}</strong>{' next to the channel or private group header.'}
+ </p>
+ </div>
+ );
+
+ return (
+ <TutorialTip
+ placement='right'
+ screens={screens}
+ overlayClass='tip-overlay--sidebar'
+ />
+ );
+ }
+
createChannelElement(channel, index, arr, handleClose) {
var members = this.state.members;
var activeId = this.state.activeId;
@@ -448,6 +499,11 @@ export default class Sidebar extends React.Component {
rowClass += ' has-close';
}
+ let tutorialTip = null;
+ if (this.state.showTutorialTip && channel.name === Constants.DEFAULT_CHANNEL) {
+ tutorialTip = this.createTutorialTip();
+ }
+
return (
<li
key={channel.name}
@@ -464,6 +520,7 @@ export default class Sidebar extends React.Component {
{badge}
{closeButton}
</a>
+ {tutorialTip}
</li>
);
}
@@ -543,9 +600,9 @@ export default class Sidebar extends React.Component {
/>
<SidebarHeader
- teamDisplayName={this.props.teamDisplayName}
- teamName={this.props.teamName}
- teamType={this.props.teamType}
+ teamDisplayName={TeamStore.getCurrent().display_name}
+ teamName={TeamStore.getCurrent().name}
+ teamType={TeamStore.getCurrent().type}
/>
<SearchBox />
@@ -631,11 +688,6 @@ export default class Sidebar extends React.Component {
}
Sidebar.defaultProps = {
- teamType: '',
- teamDisplayName: ''
};
Sidebar.propTypes = {
- teamType: React.PropTypes.string,
- teamDisplayName: React.PropTypes.string,
- teamName: React.PropTypes.string
};
diff --git a/web/react/components/sidebar_header.jsx b/web/react/components/sidebar_header.jsx
index 65e4c6d7e..46730e1e6 100644
--- a/web/react/components/sidebar_header.jsx
+++ b/web/react/components/sidebar_header.jsx
@@ -1,9 +1,16 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-var NavbarDropdown = require('./navbar_dropdown.jsx');
-var UserStore = require('../stores/user_store.jsx');
+const NavbarDropdown = require('./navbar_dropdown.jsx');
+const TutorialTip = require('./tutorial/tutorial_tip.jsx');
+
+const UserStore = require('../stores/user_store.jsx');
+const PreferenceStore = require('../stores/preference_store.jsx');
+
const Utils = require('../utils/utils.jsx');
+const Constants = require('../utils/constants.jsx');
+const Preferences = Constants.Preferences;
+const TutorialSteps = Constants.TutorialSteps;
const Tooltip = ReactBootstrap.Tooltip;
const OverlayTrigger = ReactBootstrap.OverlayTrigger;
@@ -13,8 +20,23 @@ export default class SidebarHeader extends React.Component {
super(props);
this.toggleDropdown = this.toggleDropdown.bind(this);
+ this.onPreferenceChange = this.onPreferenceChange.bind(this);
- this.state = {};
+ this.state = this.getStateFromStores();
+ }
+ componentDidMount() {
+ PreferenceStore.addChangeListener(this.onPreferenceChange);
+ }
+ componentWillUnmount() {
+ PreferenceStore.removeChangeListener(this.onPreferenceChange);
+ }
+ getStateFromStores() {
+ const tutorialPref = PreferenceStore.getPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), {value: '0'});
+
+ return {showTutorialTip: parseInt(tutorialPref.value, 10) === TutorialSteps.MENU_POPOVER};
+ }
+ onPreferenceChange() {
+ this.setState(this.getStateFromStores());
}
toggleDropdown(e) {
e.preventDefault();
@@ -24,6 +46,41 @@ export default class SidebarHeader extends React.Component {
}
$('.team__header').find('.dropdown-toggle').dropdown('toggle');
}
+ createTutorialTip() {
+ const screens = [];
+
+ screens.push(
+ <div>
+ <h4>{'Main Menu'}</h4>
+ <p>
+ {'The '}<strong>{'Main Menu'}</strong>{' is where you can '}
+ <strong>{'Invite New Members'}</strong>
+ {', access your '}
+ <strong>{'Account Settings'}</strong>
+ {' and set your '}<strong>{'Theme Color'}</strong>{'.'}
+ </p>
+ <p>
+ {'Team administrators can also access their '}<strong>{'Team Settings'}</strong>{' from this menu.'}
+ </p>
+ <p>
+ {'System administrators will find a '}<strong>{'System Console'}</strong>{' option to administrate the entire system.'}
+ </p>
+ </div>
+ );
+
+ return (
+ <div
+ onClick={this.toggleDropdown}
+ >
+ <TutorialTip
+ ref='tip'
+ placement='right'
+ screens={screens}
+ overlayClass='tip-overlay--header'
+ />
+ </div>
+ );
+ }
render() {
var me = UserStore.getCurrentUser();
var profilePicture = null;
@@ -41,8 +98,14 @@ export default class SidebarHeader extends React.Component {
);
}
+ let tutorialTip = null;
+ if (this.state.showTutorialTip) {
+ tutorialTip = this.createTutorialTip();
+ }
+
return (
<div className='team__header theme'>
+ {tutorialTip}
<a
href='#'
onClick={this.toggleDropdown}
diff --git a/web/react/components/sidebar_right.jsx b/web/react/components/sidebar_right.jsx
index 51225cbbe..e2ef60959 100644
--- a/web/react/components/sidebar_right.jsx
+++ b/web/react/components/sidebar_right.jsx
@@ -20,23 +20,48 @@ export default class SidebarRight extends React.Component {
this.onSelectedChange = this.onSelectedChange.bind(this);
this.onSearchChange = this.onSearchChange.bind(this);
+ this.doStrangeThings = this.doStrangeThings.bind(this);
+
this.state = getStateFromStores();
}
componentDidMount() {
SearchStore.addSearchChangeListener(this.onSearchChange);
PostStore.addSelectedPostChangeListener(this.onSelectedChange);
+ this.doStrangeThings();
}
componentWillUnmount() {
SearchStore.removeSearchChangeListener(this.onSearchChange);
PostStore.removeSelectedPostChangeListener(this.onSelectedChange);
}
- componentDidUpdate() {
- if (this.plScrolledToBottom) {
- var postHolder = $('.post-list-holder-by-time').not('.inactive');
- postHolder.scrollTop(postHolder[0].scrollHeight);
- } else {
- $('.top-visible-post')[0].scrollIntoView();
+ componentWillUpdate() {
+ PostStore.jumpPostsViewSidebarOpen();
+ }
+ doStrangeThings() {
+ // We should have a better way to do this stuff
+ // Hence the function name.
+ $('.inner__wrap').removeClass('.move--right');
+ $('.inner__wrap').addClass('move--left');
+ $('.sidebar--left').removeClass('move--right');
+ $('.sidebar--right').addClass('move--left');
+
+ //$('.sidebar--right').prepend('<div class="sidebar__overlay"></div>');
+
+ if (!(this.state.search_visible || this.state.post_right_visible)) {
+ $('.inner__wrap').removeClass('move--left').removeClass('move--right');
+ $('.sidebar--right').removeClass('move--left');
+ return (
+ <div></div>
+ );
}
+
+ /*setTimeout(() => {
+ $('.sidebar__overlay').fadeOut('200', () => {
+ $('.sidebar__overlay').remove();
+ });
+ }, 500);*/
+ }
+ componentDidUpdate() {
+ this.doStrangeThings();
}
onSelectedChange(fromSearch) {
var newState = getStateFromStores(fromSearch);
@@ -52,30 +77,6 @@ export default class SidebarRight extends React.Component {
}
}
render() {
- var postHolder = $('.post-list-holder-by-time').not('.inactive');
- const position = postHolder.scrollTop() + postHolder.height() + 14;
- const bottom = postHolder[0].scrollHeight;
- this.plScrolledToBottom = position >= bottom;
-
- if (!(this.state.search_visible || this.state.post_right_visible)) {
- $('.inner__wrap').removeClass('move--left').removeClass('move--right');
- $('.sidebar--right').removeClass('move--left');
- return (
- <div></div>
- );
- }
-
- $('.inner__wrap').removeClass('.move--right').addClass('move--left');
- $('.sidebar--left').removeClass('move--right');
- $('.sidebar--right').addClass('move--left');
- $('.sidebar--right').prepend('<div class="sidebar__overlay"></div>');
-
- setTimeout(() => {
- $('.sidebar__overlay').fadeOut('200', function fadeOverlay() {
- $(this).remove();
- });
- }, 500);
-
var content = '';
if (this.state.search_visible) {
diff --git a/web/react/components/signup_team.jsx b/web/react/components/signup_team.jsx
index f926f5cbb..37760a2a2 100644
--- a/web/react/components/signup_team.jsx
+++ b/web/react/components/signup_team.jsx
@@ -12,11 +12,6 @@ export default class TeamSignUp extends React.Component {
this.updatePage = this.updatePage.bind(this);
- if (global.window.mm_config.EnableTeamListing === 'true') {
- this.state = {page: 'team_listing'};
- return;
- }
-
var count = 0;
if (global.window.mm_config.EnableSignUpWithEmail === 'true') {
@@ -41,50 +36,83 @@ export default class TeamSignUp extends React.Component {
}
render() {
- if (this.state.page === 'team_listing') {
- return (
- <div>
- <h3>{'Choose a Team'}</h3>
- <div className='signup-team-all'>
- {
- this.props.teams.map((team) => {
- return (
- <div
- key={'team_' + team.name}
- className='signup-team-dir'
- >
- <a
- href={'/' + team.name}
+ var teamListing = null;
+
+ if (global.window.mm_config.EnableTeamListing === 'true') {
+ if (this.props.teams.length === 0) {
+ if (global.window.mm_config.EnableTeamCreation !== 'true') {
+ teamListing = (<div>{'There are no teams include in the Team Directory and team creation has been disabled.'}</div>);
+ }
+ } else {
+ teamListing = (
+ <div>
+ <h3>{'Choose a Team'}</h3>
+ <div className='signup-team-all'>
+ {
+ this.props.teams.map((team) => {
+ return (
+ <div
+ key={'team_' + team.name}
+ className='signup-team-dir'
>
- <div className='signup-team-dir__group'>
- <span className='signup-team-dir__name'>{team.display_name}</span>
- <span
- className='glyphicon glyphicon-menu-right right signup-team-dir__arrow'
- aria-hidden='true'
- />
- </div>
- </a>
- </div>
- );
- })
- }
+ <a
+ href={'/' + team.name}
+ >
+ <div className='signup-team-dir__group'>
+ <span className='signup-team-dir__name'>{team.display_name}</span>
+ <span
+ className='glyphicon glyphicon-menu-right right signup-team-dir__arrow'
+ aria-hidden='true'
+ />
+ </div>
+ </a>
+ </div>
+ );
+ })
+ }
+ </div>
</div>
+ );
+ }
+ }
+
+ if (global.window.mm_config.EnableTeamCreation !== 'true') {
+ if (teamListing == null) {
+ return (<div>{'Team creation has been disabled. Please contact an administrator for access.'}</div>);
+ }
+
+ return (
+ <div>
+ {teamListing}
</div>
);
}
if (this.state.page === 'choose') {
return (
- <ChoosePage
- updatePage={this.updatePage}
- />
+ <div>
+ {teamListing}
+ <ChoosePage
+ updatePage={this.updatePage}
+ />
+ </div>
);
}
if (this.state.page === 'email') {
- return <EmailSignUpPage />;
+ return (
+ <div>
+ {teamListing}
+ <EmailSignUpPage />
+ </div>
+ );
} else if (this.state.page === 'gitlab') {
- return <SSOSignupPage service={Constants.GITLAB_SERVICE} />;
+ return (
+ <div>
+ {teamListing}
+ <SSOSignupPage service={Constants.GITLAB_SERVICE} />
+ </div>
+ );
}
}
}
diff --git a/web/react/components/team_general_tab.jsx b/web/react/components/team_general_tab.jsx
index 69ba44664..587ef5ec2 100644
--- a/web/react/components/team_general_tab.jsx
+++ b/web/react/components/team_general_tab.jsx
@@ -54,7 +54,6 @@ export default class GeneralTab extends React.Component {
handleTeamListingRadio(listing) {
if (global.window.mm_config.EnableTeamListing !== 'true' && listing) {
- ReactDOM.findDOMNode(this.refs.teamListingRadioNo).checked = true;
this.setState({clientError: 'Team directory has been disabled. Please ask a system admin to enable it.'});
} else {
this.setState({allow_team_listing: listing});
@@ -278,13 +277,13 @@ export default class GeneralTab extends React.Component {
</label>
<br/>
</div>
- <div><br/>{'When allowed the team will appear on the main page as part of team directory.'}</div>
+ <div><br/>{'Including this team will display the team name from the Team Directory section of the Home Page, and provide a link to the sign-in page.'}</div>
</div>
];
teamListingSection = (
<SettingItemMax
- title='Allow in Team Directory'
+ title='Include this team in the Team Directory'
inputs={inputs}
submit={this.handleTeamListingSubmit}
server_error={serverError}
@@ -302,7 +301,7 @@ export default class GeneralTab extends React.Component {
teamListingSection = (
<SettingItemMin
- title='Allow in Team Directory'
+ title='Include this team in the Team Directory'
describe={describe}
updateSection={this.onUpdateTeamListingSection}
/>
@@ -337,13 +336,13 @@ export default class GeneralTab extends React.Component {
</label>
<br/>
</div>
- <div><br/>{'When allowed the team signup link will be included on the login page and anyone can signup to this team.'}</div>
+ <div><br/>{'When allowed, a link to account creation will be included on the sign-in page of this team and allow any visitor to sign-up.'}</div>
</div>
];
openInviteSection = (
<SettingItemMax
- title='Allow Open Invitations'
+ title='Allow anyone to sign-up from login page'
inputs={inputs}
submit={this.handleOpenInviteSubmit}
server_error={serverError}
@@ -360,7 +359,7 @@ export default class GeneralTab extends React.Component {
openInviteSection = (
<SettingItemMin
- title='Allow Open Invitations'
+ title='Allow anyone to sign-up from login page'
describe={describe}
updateSection={this.onUpdateOpenInviteSection}
/>
@@ -373,29 +372,28 @@ export default class GeneralTab extends React.Component {
const inputs = [];
inputs.push(
- <div
- key='teamInviteSetting'
- className='form-group'
- >
- <label className='col-sm-5 control-label'>{'Invite Code'}</label>
- <div className='col-sm-7'>
- <input
- className='form-control'
- type='text'
- onChange={this.updateInviteId}
- value={this.state.invite_id}
- maxLength='32'
- />
- </div>
- <div><br/>{'When allowing open invites this code is used as part of the signup process. Changing this code will invalidate the previous open signup link.'}</div>
- <div className='help-text'>
- <button
- className='btn btn-default'
- onClick={this.handleGenerateInviteId}
- >
- {'Re-Generate'}
- </button>
+ <div key='teamInviteSetting'>
+ <div className='row'>
+ <label className='col-sm-5 control-label'>{'Invite Code'}</label>
+ <div className='col-sm-7'>
+ <input
+ className='form-control'
+ type='text'
+ onChange={this.updateInviteId}
+ value={this.state.invite_id}
+ maxLength='32'
+ />
+ <div className='padding-top x2'>
+ <a
+ href='#'
+ onClick={this.handleGenerateInviteId}
+ >
+ {'Re-Generate'}
+ </a>
+ </div>
+ </div>
</div>
+ <div className='setting-list__hint'>{'When allowing open invites this code is used as part of the signup process. Changing this code will invalidate the previous open signup link.'}</div>
</div>
);
@@ -413,7 +411,7 @@ export default class GeneralTab extends React.Component {
inviteSection = (
<SettingItemMin
title={`Invite Code`}
- describe={`Click 'Edit' to re-generate invite Code.`}
+ describe={`Click 'Edit' to regenerate Invite Code.`}
updateSection={this.onUpdateInviteIdSection}
/>
);
@@ -494,8 +492,11 @@ export default class GeneralTab extends React.Component {
<h3 className='tab-header'>{'General Settings'}</h3>
<div className='divider-dark first'/>
{nameSection}
+ <div className='divider-light'/>
{openInviteSection}
+ <div className='divider-light'/>
{teamListingSection}
+ <div className='divider-light'/>
{inviteSection}
<div className='divider-dark'/>
</div>
diff --git a/web/react/components/time_since.jsx b/web/react/components/time_since.jsx
new file mode 100644
index 000000000..c37739b9c
--- /dev/null
+++ b/web/react/components/time_since.jsx
@@ -0,0 +1,50 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+var Utils = require('../utils/utils.jsx');
+
+var Tooltip = ReactBootstrap.Tooltip;
+var OverlayTrigger = ReactBootstrap.OverlayTrigger;
+
+export default class TimeSince extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+ componentDidMount() {
+ this.intervalId = setInterval(() => {
+ this.forceUpdate();
+ }, 30000);
+ }
+ componentWillUnmount() {
+ clearInterval(this.intervalId);
+ }
+ render() {
+ const displayDate = Utils.displayDate(this.props.eventTime);
+ const displayTime = Utils.displayTime(this.props.eventTime);
+
+ const tooltip = (
+ <Tooltip id={'time-since-tooltip-' + this.props.eventTime}>
+ {displayDate + ' at ' + displayTime}
+ </Tooltip>
+ );
+
+ return (
+ <OverlayTrigger
+ delayShow={400}
+ placement='top'
+ overlay={tooltip}
+ >
+ <time className='post-profile-time'>
+ {Utils.displayDateTime(this.props.eventTime)}
+ </time>
+ </OverlayTrigger>
+ );
+ }
+}
+TimeSince.defaultProps = {
+ eventTime: 0
+};
+
+TimeSince.propTypes = {
+ eventTime: React.PropTypes.number.isRequired
+};
diff --git a/web/react/components/tutorial/tutorial_intro_screens.jsx b/web/react/components/tutorial/tutorial_intro_screens.jsx
new file mode 100644
index 000000000..a99e9fe28
--- /dev/null
+++ b/web/react/components/tutorial/tutorial_intro_screens.jsx
@@ -0,0 +1,161 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+const UserStore = require('../../stores/user_store.jsx');
+const ChannelStore = require('../../stores/channel_store.jsx');
+const TeamStore = require('../../stores/team_store.jsx');
+const PreferenceStore = require('../../stores/preference_store.jsx');
+const Utils = require('../../utils/utils.jsx');
+const AsyncClient = require('../../utils/async_client.jsx');
+
+const Constants = require('../../utils/constants.jsx');
+const Preferences = Constants.Preferences;
+
+export default class TutorialIntroScreens extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.handleNext = this.handleNext.bind(this);
+ this.createScreen = this.createScreen.bind(this);
+
+ this.state = {currentScreen: 0};
+ }
+ handleNext() {
+ if (this.state.currentScreen < 2) {
+ this.setState({currentScreen: this.state.currentScreen + 1});
+ return;
+ }
+
+ Utils.switchChannel(ChannelStore.getByName(Constants.DEFAULT_CHANNEL));
+
+ let preference = PreferenceStore.getPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), {value: '0'});
+
+ const newValue = (parseInt(preference.value, 10) + 1).toString();
+
+ preference = PreferenceStore.setPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), newValue);
+ AsyncClient.savePreferences([preference]);
+ }
+ componentDidMount() {
+ $('.tutorials__scroll').perfectScrollbar();
+ }
+ createScreen() {
+ switch (this.state.currentScreen) {
+ case 0:
+ return this.createScreenOne();
+ case 1:
+ return this.createScreenTwo();
+ case 2:
+ return this.createScreenThree();
+ }
+ }
+ createScreenOne() {
+ return (
+ <div>
+ <h3>{'Welcome to:'}</h3>
+ <h1>{'Mattermost'}</h1>
+ <p>{'Your team communication all in one place, instantly searchable and available anywhere.'}</p>
+ <p>{'Keep your team connected to help them achieve what matters most.'}</p>
+ <div className='tutorial__circles'>
+ <div className='circle active'/>
+ <div className='circle'/>
+ <div className='circle'/>
+ </div>
+ </div>
+ );
+ }
+ createScreenTwo() {
+ return (
+ <div>
+ <h3>{'How Mattermost works:'}</h3>
+ <p>{'Communication happens in public discussion channels, private groups and direct messages.'}</p>
+ <p>{'Everything is archived and searchable from any web-enabled desktop, laptop or phone.'}</p>
+ <div className='tutorial__circles'>
+ <div className='circle'/>
+ <div className='circle active'/>
+ <div className='circle'/>
+ </div>
+ </div>
+ );
+ }
+ createScreenThree() {
+ const team = TeamStore.getCurrent();
+ let inviteModalLink;
+ if (team.type === Constants.INVITE_TEAM) {
+ inviteModalLink = (
+ <a
+ className='intro-links'
+ href='#'
+ data-toggle='modal'
+ data-target='#invite_member'
+ >
+ {'Invite teammates'}
+ </a>
+ );
+ } else {
+ inviteModalLink = (
+ <a
+ className='intro-links'
+ href='#'
+ data-toggle='modal'
+ data-target='#get_link'
+ data-title='Team Invite'
+ data-value={Utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + team.id}
+ >
+ {'Invite teammates'}
+ </a>
+ );
+ }
+
+ return (
+ <div>
+ <h3>{'You’re all set'}</h3>
+ <p>
+ {inviteModalLink}
+ {' when you’re ready.'}
+ </p>
+ <p>
+ {'Need anything, just email us at '}
+ <a
+ href='mailto:feedback@mattermost.com'
+ target='_blank'
+ >
+ {'feedback@mattermost.com'}
+ </a>
+ {'.'}
+ </p>
+ {'Click “Next” to enter Town Square. This is the first channel teammates see when they sign up. Use it for posting updates everyone needs to know.'}
+ <div className='tutorial__circles'>
+ <div className='circle'/>
+ <div className='circle'/>
+ <div className='circle active'/>
+ </div>
+ </div>
+ );
+ }
+ render() {
+ const height = Utils.windowHeight() - 100;
+ const screen = this.createScreen();
+
+ return (
+ <div
+ className='tutorials__scroll'
+ style={{height}}
+ >
+ <div className='tutorial-steps__container'>
+ <div className='tutorial__content'>
+ <div className='tutorial__steps'>
+ {screen}
+ <button
+ className='btn btn-primary'
+ tabIndex='1'
+ onClick={this.handleNext}
+ >
+ {'Next'}
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+}
diff --git a/web/react/components/tutorial/tutorial_tip.jsx b/web/react/components/tutorial/tutorial_tip.jsx
new file mode 100644
index 000000000..c85acb346
--- /dev/null
+++ b/web/react/components/tutorial/tutorial_tip.jsx
@@ -0,0 +1,131 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+const UserStore = require('../../stores/user_store.jsx');
+const PreferenceStore = require('../../stores/preference_store.jsx');
+const AsyncClient = require('../../utils/async_client.jsx');
+
+const Constants = require('../../utils/constants.jsx');
+const Preferences = Constants.Preferences;
+
+const Overlay = ReactBootstrap.Overlay;
+
+export default class TutorialTip extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.handleNext = this.handleNext.bind(this);
+ this.toggle = this.toggle.bind(this);
+
+ this.state = {currentScreen: 0, show: false};
+ }
+ toggle() {
+ const show = !this.state.show;
+ this.setState({show});
+
+ if (!show && this.state.currentScreen >= this.props.screens.length - 1) {
+ let preference = PreferenceStore.getPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), {value: '0'});
+
+ const newValue = (parseInt(preference.value, 10) + 1).toString();
+
+ preference = PreferenceStore.setPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), newValue);
+ AsyncClient.savePreferences([preference]);
+ }
+ }
+ handleNext() {
+ if (this.state.currentScreen < this.props.screens.length - 1) {
+ this.setState({currentScreen: this.state.currentScreen + 1});
+ return;
+ }
+
+ this.toggle();
+ }
+ skipTutorial(e) {
+ e.preventDefault();
+ const preference = PreferenceStore.setPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), '999');
+ AsyncClient.savePreferences([preference]);
+ }
+ render() {
+ const buttonText = this.state.currentScreen === this.props.screens.length - 1 ? 'Okay' : 'Next';
+
+ const dots = [];
+ if (this.props.screens.length > 1) {
+ for (let i = 0; i < this.props.screens.length; i++) {
+ if (i === this.state.currentScreen) {
+ dots.push(
+ <div
+ className='circle active'
+ key={'dotactive' + i}
+ />
+ );
+ } else {
+ dots.push(
+ <div
+ className='circle'
+ key={'dotinactive' + i}
+ />
+ );
+ }
+ }
+ }
+
+ return (
+ <div className={'tip-div ' + this.props.overlayClass}>
+ <img
+ className='tip-button'
+ src='/static/images/tutorialTip.gif'
+ width='35'
+ onClick={this.toggle}
+ ref='target'
+ />
+
+ <Overlay
+ show={this.state.show}
+ >
+ <div className='tip-backdrop'/>
+ </Overlay>
+
+ <Overlay
+ placement={this.props.placement}
+ show={this.state.show}
+ rootClose={true}
+ onHide={this.toggle}
+ target={() => this.refs.target}
+ >
+ <div className={'tip-overlay ' + this.props.overlayClass}>
+ <div className='arrow'></div>
+ {this.props.screens[this.state.currentScreen]}
+ <div className='tutorial__circles'>{dots}</div>
+ <div className='text-right'>
+ <button
+ className='btn btn-default'
+ onClick={this.handleNext}
+ >
+ {buttonText}
+ </button>
+ <div className='tip-opt'>
+ {'Seen this before? '}
+ <a
+ href='#'
+ onClick={this.skipTutorial}
+ >
+ {'Opt out of these tips.'}
+ </a>
+ </div>
+ </div>
+ </div>
+ </Overlay>
+ </div>
+ );
+ }
+}
+
+TutorialTip.defaultProps = {
+ overlayClass: ''
+};
+
+TutorialTip.propTypes = {
+ screens: React.PropTypes.array.isRequired,
+ placement: React.PropTypes.string.isRequired,
+ overlayClass: React.PropTypes.string
+};
diff --git a/web/react/components/user_settings/manage_outgoing_hooks.jsx b/web/react/components/user_settings/manage_outgoing_hooks.jsx
index 9b0701583..93be988d1 100644
--- a/web/react/components/user_settings/manage_outgoing_hooks.jsx
+++ b/web/react/components/user_settings/manage_outgoing_hooks.jsx
@@ -1,10 +1,12 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
-var Client = require('../../utils/client.jsx');
-var Constants = require('../../utils/constants.jsx');
-var ChannelStore = require('../../stores/channel_store.jsx');
-var LoadingScreen = require('../loading_screen.jsx');
+const LoadingScreen = require('../loading_screen.jsx');
+
+const ChannelStore = require('../../stores/channel_store.jsx');
+
+const Client = require('../../utils/client.jsx');
+const Constants = require('../../utils/constants.jsx');
export default class ManageOutgoingHooks extends React.Component {
constructor() {
@@ -44,10 +46,10 @@ export default class ManageOutgoingHooks extends React.Component {
hooks = [];
}
hooks.push(data);
- this.setState({hooks, serverError: null, channelId: '', triggerWords: '', callbackURLs: ''});
+ this.setState({hooks, addError: null, channelId: '', triggerWords: '', callbackURLs: ''});
},
(err) => {
- this.setState({serverError: err});
+ this.setState({addError: err.message});
}
);
}
@@ -74,7 +76,7 @@ export default class ManageOutgoingHooks extends React.Component {
this.setState({hooks});
},
(err) => {
- this.setState({serverError: err});
+ this.setState({editError: err.message});
}
);
}
@@ -93,10 +95,10 @@ export default class ManageOutgoingHooks extends React.Component {
}
}
- this.setState({hooks, serverError: null});
+ this.setState({hooks, editError: null});
},
(err) => {
- this.setState({serverError: err});
+ this.setState({editError: err.message});
}
);
}
@@ -104,11 +106,11 @@ export default class ManageOutgoingHooks extends React.Component {
Client.listOutgoingHooks(
(data) => {
if (data) {
- this.setState({hooks: data, getHooksComplete: true, serverError: null});
+ this.setState({hooks: data, getHooksComplete: true, editError: null});
}
},
(err) => {
- this.setState({serverError: err});
+ this.setState({editError: err.message});
}
);
}
@@ -122,9 +124,13 @@ export default class ManageOutgoingHooks extends React.Component {
this.setState({callbackURLs: e.target.value});
}
render() {
- let serverError;
- if (this.state.serverError) {
- serverError = <label className='has-error'>{this.state.serverError}</label>;
+ let addError;
+ if (this.state.addError) {
+ addError = <label className='has-error'>{this.state.addError}</label>;
+ }
+ let editError;
+ if (this.state.editError) {
+ addError = <label className='has-error'>{this.state.editError}</label>;
}
const channels = ChannelStore.getAll();
@@ -234,6 +240,7 @@ export default class ManageOutgoingHooks extends React.Component {
return (
<div key='addOutgoingHook'>
+ {'Create webhooks to send new message events to an external integration. Please see '}<a href='http://mattermost.org/webhooks'>{'http://mattermost.org/webhooks'}</a> {' to learn more.'}
<label className='control-label'>{'Add a new outgoing webhook'}</label>
<div className='padding-top divider-light'></div>
<div className='padding-top'>
@@ -274,10 +281,11 @@ export default class ManageOutgoingHooks extends React.Component {
resize={false}
rows={3}
onChange={this.updateCallbackURLs}
+ placeholder='Each URL must start with http:// or https://'
/>
</div>
<div className='padding-top'>{'New line separated URLs that will receive the HTTP POST event'}</div>
- {serverError}
+ {addError}
</div>
<div className='padding-top padding-bottom'>
<a
@@ -291,6 +299,7 @@ export default class ManageOutgoingHooks extends React.Component {
</div>
</div>
{existingHooks}
+ {editError}
</div>
);
}
diff --git a/web/react/components/user_settings/user_settings_display.jsx b/web/react/components/user_settings/user_settings_display.jsx
index 22a62273c..d086c78a9 100644
--- a/web/react/components/user_settings/user_settings_display.jsx
+++ b/web/react/components/user_settings/user_settings_display.jsx
@@ -9,8 +9,12 @@ import PreferenceStore from '../../stores/preference_store.jsx';
function getDisplayStateFromStores() {
const militaryTime = PreferenceStore.getPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', {value: 'false'});
+ const nameFormat = PreferenceStore.getPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', {value: 'username'});
- return {militaryTime: militaryTime.value};
+ return {
+ militaryTime: militaryTime.value,
+ nameFormat: nameFormat.value
+ };
}
export default class UserSettingsDisplay extends React.Component {
@@ -19,15 +23,17 @@ export default class UserSettingsDisplay extends React.Component {
this.handleSubmit = this.handleSubmit.bind(this);
this.handleClockRadio = this.handleClockRadio.bind(this);
+ this.handleNameRadio = this.handleNameRadio.bind(this);
this.updateSection = this.updateSection.bind(this);
this.handleClose = this.handleClose.bind(this);
this.state = getDisplayStateFromStores();
}
handleSubmit() {
- const preference = PreferenceStore.setPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', this.state.militaryTime);
+ const timePreference = PreferenceStore.setPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', this.state.militaryTime);
+ const namePreference = PreferenceStore.setPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', this.state.nameFormat);
- savePreferences([preference],
+ savePreferences([timePreference, namePreference],
() => {
PreferenceStore.emitChange();
this.updateSection('');
@@ -40,6 +46,9 @@ export default class UserSettingsDisplay extends React.Component {
handleClockRadio(militaryTime) {
this.setState({militaryTime});
}
+ handleNameRadio(nameFormat) {
+ this.setState({nameFormat});
+ }
updateSection(section) {
this.setState(getDisplayStateFromStores());
this.props.updateSection(section);
@@ -56,6 +65,7 @@ export default class UserSettingsDisplay extends React.Component {
render() {
const serverError = this.state.serverError || null;
let clockSection;
+ let nameFormatSection;
if (this.props.activeSection === 'clock') {
const clockFormat = [false, false];
if (this.state.militaryTime === 'true') {
@@ -127,6 +137,88 @@ export default class UserSettingsDisplay extends React.Component {
);
}
+ if (this.props.activeSection === 'name_format') {
+ const nameFormat = [false, false, false];
+ if (this.state.nameFormat === 'nickname_full_name') {
+ nameFormat[0] = true;
+ } else if (this.state.nameFormat === 'full_name') {
+ nameFormat[2] = true;
+ } else {
+ nameFormat[1] = true;
+ }
+
+ const inputs = [
+ <div key='userDisplayNameOptions'>
+ <div className='radio'>
+ <label>
+ <input
+ type='radio'
+ checked={nameFormat[0]}
+ onChange={this.handleNameRadio.bind(this, 'nickname_full_name')}
+ />
+ {'Show nickname if one exists, otherwise show first and last name (team default)'}
+ </label>
+ <br/>
+ </div>
+ <div className='radio'>
+ <label>
+ <input
+ type='radio'
+ checked={nameFormat[1]}
+ onChange={this.handleNameRadio.bind(this, 'username')}
+ />
+ {'Show username'}
+ </label>
+ <br/>
+ </div>
+ <div className='radio'>
+ <label>
+ <input
+ type='radio'
+ checked={nameFormat[2]}
+ onChange={this.handleNameRadio.bind(this, 'full_name')}
+ />
+ {'Show first and last name'}
+ </label>
+ <br/>
+ </div>
+ <div><br/>{'How should other users be shown in Direct Messages list?'}</div>
+ </div>
+ ];
+
+ nameFormatSection = (
+ <SettingItemMax
+ title='Show real names, nick names or usernames?'
+ inputs={inputs}
+ submit={this.handleSubmit}
+ server_error={serverError}
+ updateSection={(e) => {
+ this.updateSection('');
+ e.preventDefault();
+ }}
+ />
+ );
+ } else {
+ let describe = '';
+ if (this.state.nameFormat === 'username') {
+ describe = 'Show username';
+ } else if (this.state.nameFormat === 'full_name') {
+ describe = 'Show first and last name';
+ } else {
+ describe = 'Show nickname if one exists, otherwise show first and last name (team default)';
+ }
+
+ nameFormatSection = (
+ <SettingItemMin
+ title='Show real names, nick names or usernames?'
+ describe={describe}
+ updateSection={() => {
+ this.props.updateSection('name_format');
+ }}
+ />
+ );
+ }
+
return (
<div>
<div className='modal-header'>
@@ -151,6 +243,8 @@ export default class UserSettingsDisplay extends React.Component {
<div className='divider-dark first'/>
{clockSection}
<div className='divider-dark'/>
+ {nameFormatSection}
+ <div className='divider-dark'/>
</div>
</div>
);
diff --git a/web/react/components/user_settings/user_settings_integrations.jsx b/web/react/components/user_settings/user_settings_integrations.jsx
index 9bee74343..4a9915a1f 100644
--- a/web/react/components/user_settings/user_settings_integrations.jsx
+++ b/web/react/components/user_settings/user_settings_integrations.jsx
@@ -56,7 +56,7 @@ export default class UserSettingsIntegrationsTab extends React.Component {
<SettingItemMin
title='Incoming Webhooks'
width='medium'
- describe='Manage your incoming webhooks (Developer feature)'
+ describe='Manage your incoming webhooks'
updateSection={() => {
this.updateSection('incoming-hooks');
}}
diff --git a/web/react/pages/channel.jsx b/web/react/pages/channel.jsx
index 7a04c5979..067dcde50 100644
--- a/web/react/pages/channel.jsx
+++ b/web/react/pages/channel.jsx
@@ -2,13 +2,12 @@
// See License.txt for license information.
var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
-var Navbar = require('../components/navbar.jsx');
-var Sidebar = require('../components/sidebar.jsx');
-var ChannelHeader = require('../components/channel_header.jsx');
-var PostListContainer = require('../components/post_list_container.jsx');
-var CreatePost = require('../components/create_post.jsx');
-var SidebarRight = require('../components/sidebar_right.jsx');
-var SidebarRightMenu = require('../components/sidebar_right_menu.jsx');
+var ChannelView = require('../components/channel_view.jsx');
+var ChannelLoader = require('../components/channel_loader.jsx');
+var ErrorBar = require('../components/error_bar.jsx');
+var ErrorStore = require('../stores/error_store.jsx');
+
+var MentionList = require('../components/mention_list.jsx');
var GetLinkModal = require('../components/get_link_modal.jsx');
var MemberInviteModal = require('../components/invite_member_modal.jsx');
var EditChannelModal = require('../components/edit_channel_modal.jsx');
@@ -24,15 +23,10 @@ var TeamSettingsModal = require('../components/team_settings_modal.jsx');
var ChannelMembersModal = require('../components/channel_members.jsx');
var ChannelInviteModal = require('../components/channel_invite_modal.jsx');
var TeamMembersModal = require('../components/team_members.jsx');
-var ErrorBar = require('../components/error_bar.jsx');
-var ErrorStore = require('../stores/error_store.jsx');
-var ChannelLoader = require('../components/channel_loader.jsx');
-var MentionList = require('../components/mention_list.jsx');
var ChannelInfoModal = require('../components/channel_info_modal.jsx');
var AccessHistoryModal = require('../components/access_history_modal.jsx');
var ActivityLogModal = require('../components/activity_log_modal.jsx');
var RemovedFromChannelModal = require('../components/removed_from_channel_modal.jsx');
-var FileUploadOverlay = require('../components/file_upload_overlay.jsx');
var RegisterAppModal = require('../components/register_app_modal.jsx');
var ImportThemeModal = require('../components/user_settings/import_theme_modal.jsx');
@@ -61,20 +55,29 @@ function setupChannelPage(props) {
);
ReactDOM.render(
- <Navbar teamDisplayName={props.TeamDisplayName} />,
- document.getElementById('navbar')
+ <ChannelView/>,
+ document.getElementById('channel_view')
);
ReactDOM.render(
- <Sidebar
- teamDisplayName={props.TeamDisplayName}
- teamName={props.TeamName}
- teamType={props.TeamType}
- />,
- document.getElementById('sidebar-left')
+ <MentionList id='post_textbox' />,
+ document.getElementById('post_mention_tab')
);
ReactDOM.render(
+ <MentionList id='reply_textbox' />,
+ document.getElementById('reply_mention_tab')
+ );
+
+ ReactDOM.render(
+ <MentionList id='edit_textbox' />,
+ document.getElementById('edit_mention_tab')
+ );
+
+ //
+ // Modals
+ //
+ ReactDOM.render(
<GetLinkModal />,
document.getElementById('get_link_modal')
);
@@ -105,11 +108,6 @@ function setupChannelPage(props) {
);
ReactDOM.render(
- <ChannelHeader />,
- document.getElementById('channel-header')
- );
-
- ReactDOM.render(
<EditChannelModal />,
document.getElementById('edit_channel_modal')
);
@@ -150,11 +148,6 @@ function setupChannelPage(props) {
);
ReactDOM.render(
- <PostListContainer />,
- document.getElementById('post-list')
- );
-
- ReactDOM.render(
<EditPostModal />,
document.getElementById('edit_post_modal')
);
@@ -170,39 +163,6 @@ function setupChannelPage(props) {
);
ReactDOM.render(
- <CreatePost />,
- document.getElementById('post-create')
- );
-
- ReactDOM.render(
- <SidebarRight />,
- document.getElementById('sidebar-right')
- );
-
- ReactDOM.render(
- <SidebarRightMenu
- teamDisplayName={props.TeamDisplayName}
- teamType={props.TeamType}
- />,
- document.getElementById('sidebar-menu')
- );
-
- ReactDOM.render(
- <MentionList id='post_textbox' />,
- document.getElementById('post_mention_tab')
- );
-
- ReactDOM.render(
- <MentionList id='reply_textbox' />,
- document.getElementById('reply_mention_tab')
- );
-
- ReactDOM.render(
- <MentionList id='edit_textbox' />,
- document.getElementById('edit_mention_tab')
- );
-
- ReactDOM.render(
<AccessHistoryModal />,
document.getElementById('access_history_modal')
);
@@ -218,13 +178,6 @@ function setupChannelPage(props) {
);
ReactDOM.render(
- <FileUploadOverlay
- overlayType='center'
- />,
- document.getElementById('file_upload_overlay')
- );
-
- ReactDOM.render(
<RegisterAppModal />,
document.getElementById('register_app_modal')
);
diff --git a/web/react/stores/post_store.jsx b/web/react/stores/post_store.jsx
index 8f4e30e7c..0fe253310 100644
--- a/web/react/stores/post_store.jsx
+++ b/web/react/stores/post_store.jsx
@@ -14,6 +14,7 @@ var ActionTypes = Constants.ActionTypes;
var CHANGE_EVENT = 'change';
var SELECTED_POST_CHANGE_EVENT = 'selected_post_change';
var EDIT_POST_EVENT = 'edit_post';
+var POSTS_VIEW_JUMP_EVENT = 'post_list_jump';
class PostStoreClass extends EventEmitter {
constructor() {
@@ -29,7 +30,11 @@ class PostStoreClass extends EventEmitter {
this.emitEditPost = this.emitEditPost.bind(this);
this.addEditPostListener = this.addEditPostListener.bind(this);
- this.removeEditPostListener = this.removeEditPostListener.bind(this);
+ this.removeEditPostListener = this.removeEditPostListner.bind(this);
+
+ this.emitPostsViewJump = this.emitPostsViewJump.bind(this);
+ this.addPostsViewJumpListener = this.addPostsViewJumpListener.bind(this);
+ this.removePostsViewJumpListener = this.removePostsViewJumpListener.bind(this);
this.getCurrentPosts = this.getCurrentPosts.bind(this);
this.storePosts = this.storePosts.bind(this);
@@ -96,10 +101,34 @@ class PostStoreClass extends EventEmitter {
this.on(EDIT_POST_EVENT, callback);
}
- removeEditPostListener(callback) {
+ removeEditPostListner(callback) {
this.removeListener(EDIT_POST_EVENT, callback);
}
+ emitPostsViewJump(type, post) {
+ this.emit(POSTS_VIEW_JUMP_EVENT, type, post);
+ }
+
+ addPostsViewJumpListener(callback) {
+ this.on(POSTS_VIEW_JUMP_EVENT, callback);
+ }
+
+ removePostsViewJumpListener(callback) {
+ this.removeListener(POSTS_VIEW_JUMP_EVENT, callback);
+ }
+
+ jumpPostsViewToBottom() {
+ this.emitPostsViewJump(Constants.PostsViewJumpTypes.BOTTOM, null);
+ }
+
+ jumpPostsViewToPost(post) {
+ this.emitPostsViewJump(Constants.PostsViewJumpTypes.POST, post);
+ }
+
+ jumpPostsViewSidebarOpen() {
+ this.emitPostsViewJump(Constants.PostsViewJumpTypes.SIDEBAR_OPEN, null);
+ }
+
getCurrentPosts() {
var currentId = ChannelStore.getCurrentId();
@@ -108,16 +137,16 @@ class PostStoreClass extends EventEmitter {
}
return null;
}
- storePosts(channelId, newPostList) {
- if (isPostListNull(newPostList)) {
+ storePosts(channelId, newPostsView) {
+ if (isPostListNull(newPostsView)) {
return;
}
var postList = makePostListNonNull(this.getPosts(channelId));
- for (const pid in newPostList.posts) {
- if (newPostList.posts.hasOwnProperty(pid)) {
- const np = newPostList.posts[pid];
+ for (const pid in newPostsView.posts) {
+ if (newPostsView.posts.hasOwnProperty(pid)) {
+ const np = newPostsView.posts[pid];
if (np.delete_at === 0) {
postList.posts[pid] = np;
if (postList.order.indexOf(pid) === -1) {
diff --git a/web/react/utils/channel_intro_mssages.jsx b/web/react/utils/channel_intro_mssages.jsx
new file mode 100644
index 000000000..b3f868456
--- /dev/null
+++ b/web/react/utils/channel_intro_mssages.jsx
@@ -0,0 +1,218 @@
+
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+const Utils = require('./utils.jsx');
+const UserProfile = require('../components/user_profile.jsx');
+const ChannelStore = require('../stores/channel_store.jsx');
+const Constants = require('../utils/constants.jsx');
+const TeamStore = require('../stores/team_store.jsx');
+
+export function createChannelIntroMessage(channel) {
+ if (channel.type === 'D') {
+ return createDMIntroMessage(channel);
+ } else if (ChannelStore.isDefault(channel)) {
+ return createDefaultIntroMessage(channel);
+ } else if (channel.name === Constants.OFFTOPIC_CHANNEL) {
+ return createOffTopicIntroMessage(channel);
+ } else if (channel.type === 'O' || channel.type === 'P') {
+ return createStandardIntroMessage(channel);
+ }
+}
+
+export function createDMIntroMessage(channel) {
+ var teammate = Utils.getDirectTeammate(channel.id);
+
+ if (teammate) {
+ var teammateName = teammate.username;
+ if (teammate.nickname.length > 0) {
+ teammateName = teammate.nickname;
+ }
+
+ return (
+ <div className='channel-intro'>
+ <div className='post-profile-img__container channel-intro-img'>
+ <img
+ className='post-profile-img'
+ src={'/api/v1/users/' + teammate.id + '/image?time=' + teammate.update_at + '&' + Utils.getSessionIndex()}
+ height='50'
+ width='50'
+ />
+ </div>
+ <div className='channel-intro-profile'>
+ <strong>
+ <UserProfile userId={teammate.id} />
+ </strong>
+ </div>
+ <p className='channel-intro-text'>
+ {'This is the start of your direct message history with ' + teammateName + '.'}<br/>
+ {'Direct messages and files shared here are not shown to people outside this area.'}
+ </p>
+ <a
+ className='intro-links'
+ href='#'
+ data-toggle='modal'
+ data-target='#edit_channel'
+ data-header={channel.header}
+ data-title={channel.display_name}
+ data-channelid={channel.id}
+ >
+ <i className='fa fa-pencil'></i>{'Set a header'}
+ </a>
+ </div>
+ );
+ }
+
+ return (
+ <div className='channel-intro'>
+ <p className='channel-intro-text'>{'This is the start of your direct message history with this teammate. Direct messages and files shared here are not shown to people outside this area.'}</p>
+ </div>
+ );
+}
+
+export function createOffTopicIntroMessage(channel) {
+ return (
+ <div className='channel-intro'>
+ <h4 className='channel-intro__title'>{'Beginning of ' + channel.display_name}</h4>
+ <p className='channel-intro__content'>
+ {'This is the start of ' + channel.display_name + ', a channel for non-work-related conversations.'}
+ <br/>
+ </p>
+ <a
+ className='intro-links'
+ href='#'
+ data-toggle='modal'
+ data-target='#edit_channel'
+ data-header={channel.header}
+ data-title={channel.display_name}
+ data-channelid={channel.id}
+ >
+ <i className='fa fa-pencil'></i>{'Set a header'}
+ </a>
+ <a
+ className='intro-links'
+ href='#'
+ data-toggle='modal'
+ data-target='#channel_invite'
+ >
+ <i className='fa fa-user-plus'></i>{'Invite others to this channel'}
+ </a>
+ </div>
+ );
+}
+
+export function createDefaultIntroMessage(channel) {
+ const team = TeamStore.getCurrent();
+ let inviteModalLink;
+ if (team.type === Constants.INVITE_TEAM) {
+ inviteModalLink = (
+ <a
+ className='intro-links'
+ href='#'
+ data-toggle='modal'
+ data-target='#invite_member'
+ >
+ <i className='fa fa-user-plus'></i>{'Invite others to this team'}
+ </a>
+ );
+ } else {
+ inviteModalLink = (
+ <a
+ className='intro-links'
+ href='#'
+ data-toggle='modal'
+ data-target='#get_link'
+ data-title='Team Invite'
+ data-value={Utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + team.id}
+ >
+ <i className='fa fa-user-plus'></i>{'Invite others to this team'}
+ </a>
+ );
+ }
+
+ return (
+ <div className='channel-intro'>
+ <h4 className='channel-intro__title'>{'Beginning of ' + channel.display_name}</h4>
+ <p className='channel-intro__content'>
+ <strong>{'Welcome to ' + channel.display_name + '!'}</strong>
+ <br/><br/>
+ {'This is the first channel teammates see when they sign up - use it for posting updates everyone needs to know.'}
+ </p>
+ {inviteModalLink}
+ <a
+ className='intro-links'
+ href='#'
+ data-toggle='modal'
+ data-target='#edit_channel'
+ data-header={channel.header}
+ data-title={channel.display_name}
+ data-channelid={channel.id}
+ >
+ <i className='fa fa-pencil'></i>{'Set a header'}
+ </a>
+ <br/>
+ </div>
+ );
+}
+
+export function createStandardIntroMessage(channel) {
+ var uiName = channel.display_name;
+ var creatorName = '';
+
+ var uiType;
+ var memberMessage;
+ if (channel.type === 'P') {
+ uiType = 'private group';
+ memberMessage = ' Only invited members can see this private group.';
+ } else {
+ uiType = 'channel';
+ memberMessage = ' Any member can join and read this channel.';
+ }
+
+ var createMessage;
+ if (creatorName === '') {
+ createMessage = 'This is the start of the ' + uiName + ' ' + uiType + ', created on ' + Utils.displayDate(channel.create_at) + '.';
+ } else {
+ createMessage = (
+ <span>
+ {'This is the start of the '}
+ <strong>{uiName}</strong>
+ {' '}
+ {uiType}{', created by '}
+ <strong>{creatorName}</strong>
+ {' on '}
+ <strong>{Utils.displayDate(channel.create_at)}</strong>
+ </span>
+ );
+ }
+
+ return (
+ <div className='channel-intro'>
+ <h4 className='channel-intro__title'>{'Beginning of ' + uiName}</h4>
+ <p className='channel-intro__content'>
+ {createMessage}
+ {memberMessage}
+ <br/>
+ </p>
+ <a
+ className='intro-links'
+ href='#'
+ data-toggle='modal'
+ data-target='#edit_channel'
+ data-header={channel.header}
+ data-title={channel.display_name}
+ data-channelid={channel.id}
+ >
+ <i className='fa fa-pencil'></i>{'Set a header'}
+ </a>
+ <a
+ className='intro-links'
+ href='#'
+ data-toggle='modal'
+ data-target='#channel_invite'
+ >
+ <i className='fa fa-user-plus'></i>{'Invite others to this ' + uiType}
+ </a>
+ </div>
+ );
+}
diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx
index 1593f6706..fd64b1554 100644
--- a/web/react/utils/constants.jsx
+++ b/web/react/utils/constants.jsx
@@ -314,7 +314,14 @@ module.exports = {
Preferences: {
CATEGORY_DIRECT_CHANNEL_SHOW: 'direct_channel_show',
CATEGORY_DISPLAY_SETTINGS: 'display_settings',
- CATEGORY_ADVANCED_SETTINGS: 'advanced_settings'
+ CATEGORY_ADVANCED_SETTINGS: 'advanced_settings',
+ TUTORIAL_STEP: 'tutorial_step'
+ },
+ TutorialSteps: {
+ INTRO_SCREENS: 0,
+ POST_POPOVER: 1,
+ CHANNEL_POPOVER: 2,
+ MENU_POPOVER: 3
},
KeyCodes: {
UP: 38,
@@ -350,5 +357,10 @@ module.exports = {
ruby: 'Ruby',
java: 'Java',
ini: 'ini'
+ },
+ PostsViewJumpTypes: {
+ BOTTOM: 1,
+ POST: 2,
+ SIDEBAR_OPEN: 3
}
};
diff --git a/web/react/utils/markdown.jsx b/web/react/utils/markdown.jsx
index 179416ea0..f9416b2de 100644
--- a/web/react/utils/markdown.jsx
+++ b/web/react/utils/markdown.jsx
@@ -34,6 +34,11 @@ const highlightJsIni = require('highlight.js/lib/languages/ini.js');
const Constants = require('../utils/constants.jsx');
const HighlightedLanguages = Constants.HighlightedLanguages;
+function markdownImageLoaded(image) {
+ image.style.height = 'auto';
+}
+window.markdownImageLoaded = markdownImageLoaded;
+
class MattermostInlineLexer extends marked.InlineLexer {
constructor(links, options) {
super(links, options);
@@ -132,6 +137,16 @@ class MattermostMarkdownRenderer extends marked.Renderer {
return super.br();
}
+ image(href, title, text) {
+ let out = '<img src="' + href + '" alt="' + text + '"';
+ if (title) {
+ out += ' title="' + title + '"';
+ }
+ out += ' onload="window.markdownImageLoaded(this)" class="markdown-inline-img"';
+ out += this.options.xhtml ? '/>' : '>';
+ return out;
+ }
+
heading(text, level, raw) {
const id = `${this.options.headerPrefix}${raw.toLowerCase().replace(/[^\w]+/g, '-')}`;
return `<h${level} id="${id}" class="markdown__heading">${text}</h${level}>`;
diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index c7c8549b9..c82bd1065 100644
--- a/web/react/utils/utils.jsx
+++ b/web/react/utils/utils.jsx
@@ -59,6 +59,20 @@ export function isTestDomain() {
return false;
}
+export function isChrome() {
+ if (navigator.userAgent.indexOf('Chrome') > -1) {
+ return true;
+ }
+ return false;
+}
+
+export function isSafari() {
+ if (navigator.userAgent.indexOf('Safari') !== -1 && navigator.userAgent.indexOf('Chrome') === -1) {
+ return true;
+ }
+ return false;
+}
+
export function isInRole(roles, inRole) {
var parts = roles.split(' ');
for (var i = 0; i < parts.length; i++) {
@@ -500,16 +514,16 @@ export function applyTheme(theme) {
changeCss('#post-create', 'background:' + theme.centerChannelBg, 1);
changeCss('.date-separator .separator__text, .new-separator .separator__text', 'background:' + theme.centerChannelBg, 1);
changeCss('.post-image__column .post-image__details', 'background:' + theme.centerChannelBg, 1);
- changeCss('.sidebar--right, .dropdown-menu, .popover', 'background:' + theme.centerChannelBg, 1);
+ changeCss('.sidebar--right, .dropdown-menu, .popover, .tip-overlay', 'background:' + theme.centerChannelBg, 1);
changeCss('.popover.bottom>.arrow:after', 'border-bottom-color:' + theme.centerChannelBg, 1);
- changeCss('.popover.right>.arrow:after', 'border-right-color:' + theme.centerChannelBg, 1);
+ changeCss('.popover.right>.arrow:after, .tip-overlay.tip-overlay--sidebar .arrow, .tip-overlay.tip-overlay--header .arrow', 'border-right-color:' + theme.centerChannelBg, 1);
changeCss('.popover.left>.arrow:after', 'border-left-color:' + theme.centerChannelBg, 1);
- changeCss('.popover.top>.arrow:after', 'border-top-color:' + theme.centerChannelBg, 1);
+ changeCss('.popover.top>.arrow:after, .tip-overlay.tip-overlay--chat .arrow', 'border-top-color:' + theme.centerChannelBg, 1);
changeCss('.search-bar__container .search__form .search-bar, .form-control', 'background:' + theme.centerChannelBg, 1);
}
if (theme.centerChannelColor) {
- changeCss('.app__content, .post-create__container .post-create-body .btn-file, .post-create__container .post-create-footer .msg-typing, .command-name, .modal .modal-content, .dropdown-menu, .popover, .mentions-name', 'color:' + theme.centerChannelColor, 1);
+ changeCss('.app__content, .post-create__container .post-create-body .btn-file, .post-create__container .post-create-footer .msg-typing, .command-name, .modal .modal-content, .dropdown-menu, .popover, .mentions-name, .tip-overlay', 'color:' + theme.centerChannelColor, 1);
changeCss('#post-create', 'color:' + theme.centerChannelColor, 2);
changeCss('.channel-header__links a', 'fill:' + changeOpacity(theme.centerChannelColor, 0.7), 1);
changeCss('.channel-header__links a:hover, .channel-header__links a:active', 'fill:' + theme.centerChannelColor, 2);
@@ -519,7 +533,7 @@ export function applyTheme(theme) {
changeCss('.dropdown-menu, .popover ', 'box-shadow:' + changeOpacity(theme.centerChannelColor, 0.1) + ' 0px 6px 12px', 3);
changeCss('.dropdown-menu, .popover ', '-webkit-box-shadow:' + changeOpacity(theme.centerChannelColor, 0.1) + ' 0px 6px 12px', 2);
changeCss('.dropdown-menu, .popover ', '-moz-box-shadow:' + changeOpacity(theme.centerChannelColor, 0.1) + ' 0px 6px 12px', 1);
- changeCss('.post-body hr, .loading-screen .loading__content .round', 'background:' + theme.centerChannelColor, 1);
+ changeCss('.post-body hr, .loading-screen .loading__content .round, .tutorial__circles .circle, .tip-overlay .tutorial__circles .circle.active', 'background:' + theme.centerChannelColor, 1);
changeCss('.channel-header .heading', 'color:' + theme.centerChannelColor, 1);
changeCss('.markdown__table tbody tr:nth-child(2n)', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1);
changeCss('.channel-header__info>div.dropdown .header-dropdown__icon', 'color:' + changeOpacity(theme.centerChannelColor, 0.8), 1);
@@ -568,7 +582,7 @@ export function applyTheme(theme) {
}
if (theme.buttonBg) {
- changeCss('.btn.btn-primary', 'background:' + theme.buttonBg, 1);
+ changeCss('.btn.btn-primary, .tutorial__circles .circle.active', 'background:' + theme.buttonBg, 1);
changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background:' + changeColor(theme.buttonBg, -0.25), 1);
changeCss('.file-playback-controls', 'color:' + changeColor(theme.buttonBg, -0.25), 1);
}
@@ -884,6 +898,23 @@ export function getDisplayName(user) {
return user.username;
}
+export function displayUsername(userId) {
+ const user = UserStore.getProfile(userId);
+ const nameFormat = PreferenceStore.getPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', {value: 'false'}).value;
+
+ let username = '';
+ if (nameFormat === 'nickname_full_name') {
+ username = user.nickname || getFullName(user);
+ } else if (nameFormat === 'full_name') {
+ username = getFullName(user);
+ }
+ if (!username.trim().length) {
+ username = user.username;
+ }
+
+ return username;
+}
+
//IE10 does not set window.location.origin automatically so this must be called instead when using it
export function getWindowLocationOrigin() {
var windowLocationOrigin = window.location.origin;
@@ -976,7 +1007,7 @@ export function isBrowserIE() {
}
export function isBrowserEdge() {
- return window.naviagtor && navigator.userAgent && navigator.userAgent.toLowerCase().indexOf('edge') > -1;
+ return window.navigator && navigator.userAgent && navigator.userAgent.toLowerCase().indexOf('edge') > -1;
}
export function getDirectChannelName(id, otherId) {
diff --git a/web/sass-files/sass/partials/_base.scss b/web/sass-files/sass/partials/_base.scss
index 635928fe3..2830026c9 100644
--- a/web/sass-files/sass/partials/_base.scss
+++ b/web/sass-files/sass/partials/_base.scss
@@ -9,29 +9,37 @@ body {
position: relative;
height: 100%;
&.white {
- background: #fff;
- > .container-fluid {
- overflow: auto;
- }
- .inner__wrap {
- > .row.content {
- min-height: 100%;
- margin-bottom: -89px;
+ background: #fff;
+ > .container-fluid {
+ overflow: auto;
+ }
+ .inner__wrap {
+ > .row.content {
+ min-height: 100%;
+ margin-bottom: -89px;
+ }
}
- }
}
- .inner__wrap {
+}
+
+.inner__wrap {
height: 100%;
> .row.main {
- height: 100%;
- position: relative;
+ height: 100%;
+ position: relative;
}
- }
- > .container-fluid {
+}
+
+.container-fluid {
+ @include clearfix;
+ height: 100%;
+ position: relative;
+}
+
+.channel-view {
@include clearfix;
height: 100%;
position: relative;
- }
}
img {
@@ -125,9 +133,6 @@ a:focus, a:hover {
&.no-resize {
resize: none;
}
- &.min-height {
- min-height: 100px;
- }
}
.form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control {
diff --git a/web/sass-files/sass/partials/_headers.scss b/web/sass-files/sass/partials/_headers.scss
index feb392234..5c8313454 100644
--- a/web/sass-files/sass/partials/_headers.scss
+++ b/web/sass-files/sass/partials/_headers.scss
@@ -1,5 +1,6 @@
#channel-header {
padding: 3px 0;
+ height: 58px;
}
.row {
&.header {
@@ -42,6 +43,9 @@
text-overflow: ellipsis;
margin-top: 2px;
max-height: 45px;
+ .markdown-inline-img {
+ max-height: 45px
+ }
}
&.popover {
white-space: normal;
diff --git a/web/sass-files/sass/partials/_markdown.scss b/web/sass-files/sass/partials/_markdown.scss
index 70c99f504..87e809694 100644
--- a/web/sass-files/sass/partials/_markdown.scss
+++ b/web/sass-files/sass/partials/_markdown.scss
@@ -8,6 +8,10 @@
margin-left: 4px;
}
}
+.markdown-inline-img {
+ max-height: 500px;
+ height: 500px;
+}
.post-body {
hr {
height: 4px;
diff --git a/web/sass-files/sass/partials/_post.scss b/web/sass-files/sass/partials/_post.scss
index 7709e17f3..e11f9b640 100644
--- a/web/sass-files/sass/partials/_post.scss
+++ b/web/sass-files/sass/partials/_post.scss
@@ -441,7 +441,10 @@ body.ios {
&.post-profile-img__container {
float: left;
.post-profile-img {
+ width: 36px;
+ height: 36px;
margin-right: 10px;
+ vertical-align: inherit;
@include border-radius(50px);
}
}
diff --git a/web/sass-files/sass/partials/_responsive.scss b/web/sass-files/sass/partials/_responsive.scss
index 8f353c401..8f25f58cd 100644
--- a/web/sass-files/sass/partials/_responsive.scss
+++ b/web/sass-files/sass/partials/_responsive.scss
@@ -179,6 +179,15 @@
}
@media screen and (max-width: 1140px) {
+ .tip-overlay {
+ &.tip-overlay--chat {
+ margin: -10px 0 0 -10px;
+ .arrow {
+ right: 15px;
+ left: auto;
+ }
+ }
+ }
.inner__wrap {
&.move--left {
.file-overlay {
@@ -266,6 +275,26 @@
}
}
@media screen and (max-width: 768px) {
+ .form-control {
+ &.min-height {
+ min-height: 100px;
+ }
+ }
+ .gif-div {
+ max-width: 100%;
+ }
+ .tip-div {
+ left: 15px;
+ right: auto;
+ }
+ .tip-overlay {
+ &.tip-overlay--chat {
+ margin-left: 10px;
+ .arrow {
+ left: 32px;
+ }
+ }
+ }
.file-details__container {
display: block;
.file-details__preview {
@@ -439,7 +468,7 @@
}
}
.settings-table {
- .nav {
+ .nav, .nav.absolute {
position: relative;
top: auto;
width: 100%;
@@ -482,7 +511,6 @@
padding-bottom: 10px;
display: table;
width: 100%;
- table-layout: fixed;
.post-body__cell {
display: table-cell;
padding-left: 45px;
diff --git a/web/sass-files/sass/partials/_settings.scss b/web/sass-files/sass/partials/_settings.scss
index fbbd07485..96a6cf2ab 100644
--- a/web/sass-files/sass/partials/_settings.scss
+++ b/web/sass-files/sass/partials/_settings.scss
@@ -72,6 +72,10 @@
position: fixed;
top: 57px;
width: 179px;
+ &.absolute {
+ position: absolute;
+ top: 0;
+ }
}
.security-links {
margin-right: 20px;
diff --git a/web/sass-files/sass/partials/_sidebar--right.scss b/web/sass-files/sass/partials/_sidebar--right.scss
index c954b03d8..a4267294c 100644
--- a/web/sass-files/sass/partials/_sidebar--right.scss
+++ b/web/sass-files/sass/partials/_sidebar--right.scss
@@ -80,13 +80,18 @@
}
.sidebar--right__subheader {
font-size: 1em;
- text-transform: uppercase;
- color: #999;
- font-weight: 400;
- color: #888;
- height: 44px;
- line-height: 44px;
- padding: 0 1em;
+ padding: 1em 1em 0;
+ h4 {
+ font-size: 1em;
+ }
+ ul {
+ @include opacity(0.7);
+ padding: 10px 0 0 30px;
+ }
+ li {
+ font-size: 0.95em;
+ padding-bottom: 10px;
+ }
}
}
diff --git a/web/sass-files/sass/partials/_signup.scss b/web/sass-files/sass/partials/_signup.scss
index 14c676f82..84f9892f4 100644
--- a/web/sass-files/sass/partials/_signup.scss
+++ b/web/sass-files/sass/partials/_signup.scss
@@ -316,7 +316,7 @@
.signup-team-all {
width: 280px;
box-shadow: 3px 3px 1px #d5d5d5;
- margin: 0px 0px 0px 5px;
+ margin: 0px 0px 50px 5px;
}
.signup-team-dir {
diff --git a/web/sass-files/sass/partials/_tutorial.scss b/web/sass-files/sass/partials/_tutorial.scss
new file mode 100644
index 000000000..70216aa97
--- /dev/null
+++ b/web/sass-files/sass/partials/_tutorial.scss
@@ -0,0 +1,189 @@
+.tip-backdrop {
+ background: rgba(black, 0.5);
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 999;
+}
+
+.tip-overlay {
+ width: 350px;
+ max-width: 90%;
+ position:absolute;
+ background-color: #fff;
+ @include border-radius(3px);
+ padding: 20px;
+ z-index: 1000;
+
+ .arrow {
+ border-width: 10px;
+ position: absolute;
+ display: block;
+ width: 0;
+ height: 0;
+ border-color: transparent;
+ border-style: solid;
+ }
+
+ &.tip-overlay--sidebar {
+ max-width: 75%;
+ margin: 50px 0 0 10px;
+ .arrow {
+ top: 80px;
+ left: -10px;
+ margin-top: -10px;
+ border-left-width: 0;
+ border-right-color: #fff;
+ }
+ }
+
+ &.tip-overlay--header {
+ margin: 10px 0 0 10px;
+ .arrow {
+ top: 15px;
+ left: -10px;
+ border-left-width: 0;
+ border-right-color: #fff;
+ }
+ }
+
+ &.tip-overlay--chat {
+ margin-top: -10px;
+ .arrow {
+ left: 50%;
+ margin-left: -10px;
+ border-bottom-width: 0;
+ border-top-color: #fff;
+ bottom: -10px;
+ }
+ }
+
+ h4 {
+ font-size: em(16px);
+ font-weight: 600;
+ margin: 5px 0 15px;
+ }
+
+ p {
+ font-size: 13px;
+ line-height: 1.6;
+
+ strong {
+ font-weight: 600;
+ }
+
+ }
+
+ .btn {
+ background: #cccccc;
+ color: #fff;
+ @include border-radius(3px);
+ border: none;
+ margin-bottom: 10px;
+
+ &:hover, &:active, &:focus {
+ color: #fff;
+ border: none;
+ background: #bbb;
+ }
+
+ }
+
+ .tip-opt {
+ font-size: 12px;
+
+ span {
+ @include opacity(0.5);
+ }
+
+ }
+
+ .tutorial__circles {
+ margin: 1.5em 0px -1.7em -4px;
+
+ .circle {
+ width: 7px;
+ height: 7px;
+ margin: 0 4px;
+ &.active {
+ background: #000;
+ @include opacity(0.4);
+ }
+
+ }
+
+ }
+
+}
+
+.tip-button {
+ z-index: 998;
+ right: -10px;
+ top: -10px;
+ position: relative;
+ cursor: pointer;
+}
+
+.tip-div {
+ position:absolute;
+ top:0px;
+ right:0px;
+
+ &.tip-overlay--header {
+ top: 20px;
+ }
+
+ &.tip-overlay--sidebar {
+ left: 0;
+ top: -9px;
+ }
+
+}
+
+.tutorial-steps__container {
+ text-align: center;
+ width: 100%;
+ display: table;
+ height: 100%;
+ .tutorial__content {
+ display: table-cell;
+ vertical-align: middle;
+ padding-bottom: 100px;
+ padding: 20px 40px 40px;
+ .tutorial__steps {
+ max-width: 310px;
+ min-height: 420px;
+ text-align: left;
+ display: inline-block;
+ }
+ }
+ h1 {
+ font-size: em(40px);
+ margin: -20px 0 30px;
+ font-weight: 600;
+ }
+ h3 {
+ font-size: em(24px);
+ margin-bottom: 30px;
+ font-weight: 600;
+ }
+}
+
+.tutorial__circles {
+ margin: 2em 0;
+ .circle {
+ width: 9px;
+ height: 9px;
+ @include border-radius(9px);
+ @include opacity(0.2);
+ background: #000;
+ display: inline-block;
+ margin: 0 5px;
+ &.active {
+ background: $primary-color;
+ @include opacity(1);
+ }
+ }
+} \ No newline at end of file
diff --git a/web/sass-files/sass/partials/_videos.scss b/web/sass-files/sass/partials/_videos.scss
index f6999d15c..bcfc28f19 100644
--- a/web/sass-files/sass/partials/_videos.scss
+++ b/web/sass-files/sass/partials/_videos.scss
@@ -56,5 +56,8 @@
max-width: 450px;
max-height: 500px;
margin-bottom: 8px;
- border-radius:5px
+ border-radius:5px;
+ &.placeholder {
+ height: 500px;
+ }
}
diff --git a/web/sass-files/sass/styles.scss b/web/sass-files/sass/styles.scss
index ad2cae194..5c83d5c5b 100644
--- a/web/sass-files/sass/styles.scss
+++ b/web/sass-files/sass/styles.scss
@@ -38,6 +38,7 @@
@import "partials/loading";
@import "partials/get-link";
@import "partials/markdown";
+@import "partials/tutorial";
@import "partials/statistics";
// Responsive Css
diff --git a/web/static/images/tutorialTip.gif b/web/static/images/tutorialTip.gif
new file mode 100644
index 000000000..f185ff4b9
--- /dev/null
+++ b/web/static/images/tutorialTip.gif
Binary files differ
diff --git a/web/static/js/babel-es6-polyfill.js b/web/static/js/babel-es6-polyfill.js
new file mode 100644
index 000000000..29161d1aa
--- /dev/null
+++ b/web/static/js/babel-es6-polyfill.js
@@ -0,0 +1,2591 @@
+(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<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+(function (global){
+"use strict";
+
+if (global._babelPolyfill) {
+ throw new Error("only one instance of babel/polyfill is allowed");
+}
+global._babelPolyfill = true;
+
+require("./es6-shim");
+
+require("regenerator-babel/runtime");
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{"./es6-shim":2,"regenerator-babel/runtime":60}],2:[function(require,module,exports){
+require('core-js/es6');
+module.exports = require('core-js/modules/$').core;
+},{"core-js/es6":3,"core-js/modules/$":16}],3:[function(require,module,exports){
+require('../modules/es6.symbol');
+require('../modules/es6.object.assign');
+require('../modules/es6.object.is');
+require('../modules/es6.object.set-prototype-of');
+require('../modules/es6.object.to-string');
+require('../modules/es6.object.statics-accept-primitives');
+require('../modules/es6.function.name');
+require('../modules/es6.number.constructor');
+require('../modules/es6.number.statics');
+require('../modules/es6.math');
+require('../modules/es6.string.from-code-point');
+require('../modules/es6.string.raw');
+require('../modules/es6.string.iterator');
+require('../modules/es6.string.code-point-at');
+require('../modules/es6.string.ends-with');
+require('../modules/es6.string.includes');
+require('../modules/es6.string.repeat');
+require('../modules/es6.string.starts-with');
+require('../modules/es6.array.from');
+require('../modules/es6.array.of');
+require('../modules/es6.array.species');
+require('../modules/es6.array.iterator');
+require('../modules/es6.array.copy-within');
+require('../modules/es6.array.fill');
+require('../modules/es6.array.find');
+require('../modules/es6.array.find-index');
+require('../modules/es6.regexp');
+require('../modules/es6.promise');
+require('../modules/es6.map');
+require('../modules/es6.set');
+require('../modules/es6.weak-map');
+require('../modules/es6.weak-set');
+require('../modules/es6.reflect');
+module.exports = require('../modules/$').core;
+},{"../modules/$":16,"../modules/es6.array.copy-within":27,"../modules/es6.array.fill":28,"../modules/es6.array.find":30,"../modules/es6.array.find-index":29,"../modules/es6.array.from":31,"../modules/es6.array.iterator":32,"../modules/es6.array.of":33,"../modules/es6.array.species":34,"../modules/es6.function.name":35,"../modules/es6.map":36,"../modules/es6.math":37,"../modules/es6.number.constructor":38,"../modules/es6.number.statics":39,"../modules/es6.object.assign":40,"../modules/es6.object.is":41,"../modules/es6.object.set-prototype-of":42,"../modules/es6.object.statics-accept-primitives":43,"../modules/es6.object.to-string":44,"../modules/es6.promise":45,"../modules/es6.reflect":46,"../modules/es6.regexp":47,"../modules/es6.set":48,"../modules/es6.string.code-point-at":49,"../modules/es6.string.ends-with":50,"../modules/es6.string.from-code-point":51,"../modules/es6.string.includes":52,"../modules/es6.string.iterator":53,"../modules/es6.string.raw":54,"../modules/es6.string.repeat":55,"../modules/es6.string.starts-with":56,"../modules/es6.symbol":57,"../modules/es6.weak-map":58,"../modules/es6.weak-set":59}],4:[function(require,module,exports){
+'use strict';
+// 0 -> Array#forEach
+// 1 -> Array#map
+// 2 -> Array#filter
+// 3 -> Array#some
+// 4 -> Array#every
+// 5 -> Array#find
+// 6 -> Array#findIndex
+var $ = require('./$')
+ , ctx = require('./$.ctx');
+module.exports = function(TYPE){
+ var IS_MAP = TYPE == 1
+ , IS_FILTER = TYPE == 2
+ , IS_SOME = TYPE == 3
+ , IS_EVERY = TYPE == 4
+ , IS_FIND_INDEX = TYPE == 6
+ , NO_HOLES = TYPE == 5 || IS_FIND_INDEX;
+ return function(callbackfn/*, that = undefined */){
+ var O = Object($.assertDefined(this))
+ , self = $.ES5Object(O)
+ , f = ctx(callbackfn, arguments[1], 3)
+ , length = $.toLength(self.length)
+ , index = 0
+ , result = IS_MAP ? Array(length) : IS_FILTER ? [] : undefined
+ , val, res;
+ for(;length > index; index++)if(NO_HOLES || index in self){
+ val = self[index];
+ res = f(val, index, O);
+ if(TYPE){
+ if(IS_MAP)result[index] = res; // map
+ else if(res)switch(TYPE){
+ case 3: return true; // some
+ case 5: return val; // find
+ case 6: return index; // findIndex
+ case 2: result.push(val); // filter
+ } else if(IS_EVERY)return false; // every
+ }
+ }
+ return IS_FIND_INDEX ? -1 : IS_SOME || IS_EVERY ? IS_EVERY : result;
+ };
+};
+},{"./$":16,"./$.ctx":11}],5:[function(require,module,exports){
+var $ = require('./$');
+function assert(condition, msg1, msg2){
+ if(!condition)throw TypeError(msg2 ? msg1 + msg2 : msg1);
+}
+assert.def = $.assertDefined;
+assert.fn = function(it){
+ if(!$.isFunction(it))throw TypeError(it + ' is not a function!');
+ return it;
+};
+assert.obj = function(it){
+ if(!$.isObject(it))throw TypeError(it + ' is not an object!');
+ return it;
+};
+assert.inst = function(it, Constructor, name){
+ if(!(it instanceof Constructor))throw TypeError(name + ": use the 'new' operator!");
+ return it;
+};
+module.exports = assert;
+},{"./$":16}],6:[function(require,module,exports){
+var $ = require('./$');
+// 19.1.2.1 Object.assign(target, source, ...)
+module.exports = Object.assign || function(target, source){ // eslint-disable-line no-unused-vars
+ var T = Object($.assertDefined(target))
+ , l = arguments.length
+ , i = 1;
+ while(l > i){
+ var S = $.ES5Object(arguments[i++])
+ , keys = $.getKeys(S)
+ , length = keys.length
+ , j = 0
+ , key;
+ while(length > j)T[key = keys[j++]] = S[key];
+ }
+ return T;
+};
+},{"./$":16}],7:[function(require,module,exports){
+var $ = require('./$')
+ , TAG = require('./$.wks')('toStringTag')
+ , toString = {}.toString;
+function cof(it){
+ return toString.call(it).slice(8, -1);
+}
+cof.classof = function(it){
+ var O, T;
+ return it == undefined ? it === undefined ? 'Undefined' : 'Null'
+ : typeof (T = (O = Object(it))[TAG]) == 'string' ? T : cof(O);
+};
+cof.set = function(it, tag, stat){
+ if(it && !$.has(it = stat ? it : it.prototype, TAG))$.hide(it, TAG, tag);
+};
+module.exports = cof;
+},{"./$":16,"./$.wks":26}],8:[function(require,module,exports){
+'use strict';
+var $ = require('./$')
+ , ctx = require('./$.ctx')
+ , safe = require('./$.uid').safe
+ , assert = require('./$.assert')
+ , $iter = require('./$.iter')
+ , has = $.has
+ , set = $.set
+ , isObject = $.isObject
+ , hide = $.hide
+ , step = $iter.step
+ , isFrozen = Object.isFrozen || $.core.Object.isFrozen
+ , ID = safe('id')
+ , O1 = safe('O1')
+ , LAST = safe('last')
+ , FIRST = safe('first')
+ , ITER = safe('iter')
+ , SIZE = $.DESC ? safe('size') : 'size'
+ , id = 0;
+
+function fastKey(it, create){
+ // return primitive with prefix
+ if(!isObject(it))return (typeof it == 'string' ? 'S' : 'P') + it;
+ // can't set id to frozen object
+ if(isFrozen(it))return 'F';
+ if(!has(it, ID)){
+ // not necessary to add id
+ if(!create)return 'E';
+ // add missing object id
+ hide(it, ID, ++id);
+ // return object id with prefix
+ } return 'O' + it[ID];
+}
+
+function getEntry(that, key){
+ // fast case
+ var index = fastKey(key), entry;
+ if(index != 'F')return that[O1][index];
+ // frozen object case
+ for(entry = that[FIRST]; entry; entry = entry.n){
+ if(entry.k == key)return entry;
+ }
+}
+
+module.exports = {
+ getConstructor: function(NAME, IS_MAP, ADDER){
+ function C(iterable){
+ var that = assert.inst(this, C, NAME);
+ set(that, O1, $.create(null));
+ set(that, SIZE, 0);
+ set(that, LAST, undefined);
+ set(that, FIRST, undefined);
+ if(iterable != undefined)$iter.forOf(iterable, IS_MAP, that[ADDER], that);
+ }
+ $.mix(C.prototype, {
+ // 23.1.3.1 Map.prototype.clear()
+ // 23.2.3.2 Set.prototype.clear()
+ clear: function(){
+ for(var that = this, data = that[O1], entry = that[FIRST]; entry; entry = entry.n){
+ entry.r = true;
+ if(entry.p)entry.p = entry.p.n = undefined;
+ delete data[entry.i];
+ }
+ that[FIRST] = that[LAST] = undefined;
+ that[SIZE] = 0;
+ },
+ // 23.1.3.3 Map.prototype.delete(key)
+ // 23.2.3.4 Set.prototype.delete(value)
+ 'delete': function(key){
+ var that = this
+ , entry = getEntry(that, key);
+ if(entry){
+ var next = entry.n
+ , prev = entry.p;
+ delete that[O1][entry.i];
+ entry.r = true;
+ if(prev)prev.n = next;
+ if(next)next.p = prev;
+ if(that[FIRST] == entry)that[FIRST] = next;
+ if(that[LAST] == entry)that[LAST] = prev;
+ that[SIZE]--;
+ } return !!entry;
+ },
+ // 23.2.3.6 Set.prototype.forEach(callbackfn, thisArg = undefined)
+ // 23.1.3.5 Map.prototype.forEach(callbackfn, thisArg = undefined)
+ forEach: function(callbackfn /*, that = undefined */){
+ var f = ctx(callbackfn, arguments[1], 3)
+ , entry;
+ while(entry = entry ? entry.n : this[FIRST]){
+ f(entry.v, entry.k, this);
+ // revert to the last existing entry
+ while(entry && entry.r)entry = entry.p;
+ }
+ },
+ // 23.1.3.7 Map.prototype.has(key)
+ // 23.2.3.7 Set.prototype.has(value)
+ has: function(key){
+ return !!getEntry(this, key);
+ }
+ });
+ if($.DESC)$.setDesc(C.prototype, 'size', {
+ get: function(){
+ return assert.def(this[SIZE]);
+ }
+ });
+ return C;
+ },
+ def: function(that, key, value){
+ var entry = getEntry(that, key)
+ , prev, index;
+ // change existing entry
+ if(entry){
+ entry.v = value;
+ // create new entry
+ } else {
+ that[LAST] = entry = {
+ i: index = fastKey(key, true), // <- index
+ k: key, // <- key
+ v: value, // <- value
+ p: prev = that[LAST], // <- previous entry
+ n: undefined, // <- next entry
+ r: false // <- removed
+ };
+ if(!that[FIRST])that[FIRST] = entry;
+ if(prev)prev.n = entry;
+ that[SIZE]++;
+ // add to index
+ if(index != 'F')that[O1][index] = entry;
+ } return that;
+ },
+ getEntry: getEntry,
+ getIterConstructor: function(){
+ return function(iterated, kind){
+ set(this, ITER, {o: iterated, k: kind});
+ };
+ },
+ next: function(){
+ var iter = this[ITER]
+ , kind = iter.k
+ , entry = iter.l;
+ // revert to the last existing entry
+ while(entry && entry.r)entry = entry.p;
+ // get next entry
+ if(!iter.o || !(iter.l = entry = entry ? entry.n : iter.o[FIRST])){
+ // or finish the iteration
+ iter.o = undefined;
+ return step(1);
+ }
+ // return step by kind
+ if(kind == 'key' )return step(0, entry.k);
+ if(kind == 'value')return step(0, entry.v);
+ return step(0, [entry.k, entry.v]);
+ }
+};
+},{"./$":16,"./$.assert":5,"./$.ctx":11,"./$.iter":15,"./$.uid":24}],9:[function(require,module,exports){
+'use strict';
+var $ = require('./$')
+ , safe = require('./$.uid').safe
+ , assert = require('./$.assert')
+ , forOf = require('./$.iter').forOf
+ , has = $.has
+ , isObject = $.isObject
+ , hide = $.hide
+ , isFrozen = Object.isFrozen || $.core.Object.isFrozen
+ , id = 0
+ , ID = safe('id')
+ , WEAK = safe('weak')
+ , LEAK = safe('leak')
+ , method = require('./$.array-methods')
+ , find = method(5)
+ , findIndex = method(6);
+function findFrozen(store, key){
+ return find.call(store.array, function(it){
+ return it[0] === key;
+ });
+}
+// fallback for frozen keys
+function leakStore(that){
+ return that[LEAK] || hide(that, LEAK, {
+ array: [],
+ get: function(key){
+ var entry = findFrozen(this, key);
+ if(entry)return entry[1];
+ },
+ has: function(key){
+ return !!findFrozen(this, key);
+ },
+ set: function(key, value){
+ var entry = findFrozen(this, key);
+ if(entry)entry[1] = value;
+ else this.array.push([key, value]);
+ },
+ 'delete': function(key){
+ var index = findIndex.call(this.array, function(it){
+ return it[0] === key;
+ });
+ if(~index)this.array.splice(index, 1);
+ return !!~index;
+ }
+ })[LEAK];
+}
+
+module.exports = {
+ getConstructor: function(NAME, IS_MAP, ADDER){
+ function C(iterable){
+ $.set(assert.inst(this, C, NAME), ID, id++);
+ if(iterable != undefined)forOf(iterable, IS_MAP, this[ADDER], this);
+ }
+ $.mix(C.prototype, {
+ // 23.3.3.2 WeakMap.prototype.delete(key)
+ // 23.4.3.3 WeakSet.prototype.delete(value)
+ 'delete': function(key){
+ if(!isObject(key))return false;
+ if(isFrozen(key))return leakStore(this)['delete'](key);
+ return has(key, WEAK) && has(key[WEAK], this[ID]) && delete key[WEAK][this[ID]];
+ },
+ // 23.3.3.4 WeakMap.prototype.has(key)
+ // 23.4.3.4 WeakSet.prototype.has(value)
+ has: function(key){
+ if(!isObject(key))return false;
+ if(isFrozen(key))return leakStore(this).has(key);
+ return has(key, WEAK) && has(key[WEAK], this[ID]);
+ }
+ });
+ return C;
+ },
+ def: function(that, key, value){
+ if(isFrozen(assert.obj(key))){
+ leakStore(that).set(key, value);
+ } else {
+ has(key, WEAK) || hide(key, WEAK, {});
+ key[WEAK][that[ID]] = value;
+ } return that;
+ },
+ leakStore: leakStore,
+ WEAK: WEAK,
+ ID: ID
+};
+},{"./$":16,"./$.array-methods":4,"./$.assert":5,"./$.iter":15,"./$.uid":24}],10:[function(require,module,exports){
+'use strict';
+var $ = require('./$')
+ , $def = require('./$.def')
+ , $iter = require('./$.iter')
+ , assertInstance = require('./$.assert').inst;
+
+module.exports = function(NAME, methods, common, IS_MAP, isWeak){
+ var Base = $.g[NAME]
+ , C = Base
+ , ADDER = IS_MAP ? 'set' : 'add'
+ , proto = C && C.prototype
+ , O = {};
+ function fixMethod(KEY, CHAIN){
+ var method = proto[KEY];
+ if($.FW)proto[KEY] = function(a, b){
+ var result = method.call(this, a === 0 ? 0 : a, b);
+ return CHAIN ? this : result;
+ };
+ }
+ if(!$.isFunction(C) || !(isWeak || !$iter.BUGGY && proto.forEach && proto.entries)){
+ // create collection constructor
+ C = common.getConstructor(NAME, IS_MAP, ADDER);
+ $.mix(C.prototype, methods);
+ } else {
+ var inst = new C
+ , chain = inst[ADDER](isWeak ? {} : -0, 1)
+ , buggyZero;
+ // wrap for init collections from iterable
+ if($iter.fail(function(iter){
+ new C(iter); // eslint-disable-line no-new
+ }) || $iter.DANGER_CLOSING){
+ C = function(iterable){
+ assertInstance(this, C, NAME);
+ var that = new Base;
+ if(iterable != undefined)$iter.forOf(iterable, IS_MAP, that[ADDER], that);
+ return that;
+ };
+ C.prototype = proto;
+ if($.FW)proto.constructor = C;
+ }
+ isWeak || inst.forEach(function(val, key){
+ buggyZero = 1 / key === -Infinity;
+ });
+ // fix converting -0 key to +0
+ if(buggyZero){
+ fixMethod('delete');
+ fixMethod('has');
+ IS_MAP && fixMethod('get');
+ }
+ // + fix .add & .set for chaining
+ if(buggyZero || chain !== inst)fixMethod(ADDER, true);
+ }
+
+ require('./$.cof').set(C, NAME);
+ require('./$.species')(C);
+
+ O[NAME] = C;
+ $def($def.G + $def.W + $def.F * (C != Base), O);
+
+ // add .keys, .values, .entries, [@@iterator]
+ // 23.1.3.4, 23.1.3.8, 23.1.3.11, 23.1.3.12, 23.2.3.5, 23.2.3.8, 23.2.3.10, 23.2.3.11
+ if(!isWeak)$iter.std(
+ C, NAME,
+ common.getIterConstructor(), common.next,
+ IS_MAP ? 'key+value' : 'value' , !IS_MAP, true
+ );
+
+ return C;
+};
+},{"./$":16,"./$.assert":5,"./$.cof":7,"./$.def":12,"./$.iter":15,"./$.species":21}],11:[function(require,module,exports){
+// Optional / simple context binding
+var assertFunction = require('./$.assert').fn;
+module.exports = function(fn, that, length){
+ assertFunction(fn);
+ if(~length && that === undefined)return fn;
+ switch(length){
+ case 1: return function(a){
+ return fn.call(that, a);
+ };
+ case 2: return function(a, b){
+ return fn.call(that, a, b);
+ };
+ case 3: return function(a, b, c){
+ return fn.call(that, a, b, c);
+ };
+ } return function(/* ...args */){
+ return fn.apply(that, arguments);
+ };
+};
+},{"./$.assert":5}],12:[function(require,module,exports){
+var $ = require('./$')
+ , global = $.g
+ , core = $.core
+ , isFunction = $.isFunction;
+function ctx(fn, that){
+ return function(){
+ return fn.apply(that, arguments);
+ };
+}
+global.core = core;
+// type bitmap
+$def.F = 1; // forced
+$def.G = 2; // global
+$def.S = 4; // static
+$def.P = 8; // proto
+$def.B = 16; // bind
+$def.W = 32; // wrap
+function $def(type, name, source){
+ var key, own, out, exp
+ , isGlobal = type & $def.G
+ , target = isGlobal ? global : type & $def.S
+ ? global[name] : (global[name] || {}).prototype
+ , exports = isGlobal ? core : core[name] || (core[name] = {});
+ if(isGlobal)source = name;
+ for(key in source){
+ // contains in native
+ own = !(type & $def.F) && target && key in target;
+ // export native or passed
+ out = (own ? target : source)[key];
+ // bind timers to global for call from export context
+ if(type & $def.B && own)exp = ctx(out, global);
+ else exp = type & $def.P && isFunction(out) ? ctx(Function.call, out) : out;
+ // extend global
+ if(target && !own){
+ if(isGlobal)target[key] = out;
+ else delete target[key] && $.hide(target, key, out);
+ }
+ // export
+ if(exports[key] != out)$.hide(exports, key, exp);
+ }
+}
+module.exports = $def;
+},{"./$":16}],13:[function(require,module,exports){
+module.exports = function($){
+ $.FW = true;
+ $.path = $.g;
+ return $;
+};
+},{}],14:[function(require,module,exports){
+// Fast apply
+// http://jsperf.lnkit.com/fast-apply/5
+module.exports = function(fn, args, that){
+ var un = that === undefined;
+ switch(args.length){
+ case 0: return un ? fn()
+ : fn.call(that);
+ case 1: return un ? fn(args[0])
+ : fn.call(that, args[0]);
+ case 2: return un ? fn(args[0], args[1])
+ : fn.call(that, args[0], args[1]);
+ case 3: return un ? fn(args[0], args[1], args[2])
+ : fn.call(that, args[0], args[1], args[2]);
+ case 4: return un ? fn(args[0], args[1], args[2], args[3])
+ : fn.call(that, args[0], args[1], args[2], args[3]);
+ case 5: return un ? fn(args[0], args[1], args[2], args[3], args[4])
+ : fn.call(that, args[0], args[1], args[2], args[3], args[4]);
+ } return fn.apply(that, args);
+};
+},{}],15:[function(require,module,exports){
+'use strict';
+var $ = require('./$')
+ , ctx = require('./$.ctx')
+ , cof = require('./$.cof')
+ , $def = require('./$.def')
+ , assertObject = require('./$.assert').obj
+ , SYMBOL_ITERATOR = require('./$.wks')('iterator')
+ , FF_ITERATOR = '@@iterator'
+ , Iterators = {}
+ , IteratorPrototype = {};
+// Safari has byggy iterators w/o `next`
+var BUGGY = 'keys' in [] && !('next' in [].keys());
+// 25.1.2.1.1 %IteratorPrototype%[@@iterator]()
+setIterator(IteratorPrototype, $.that);
+function setIterator(O, value){
+ $.hide(O, SYMBOL_ITERATOR, value);
+ // Add iterator for FF iterator protocol
+ if(FF_ITERATOR in [])$.hide(O, FF_ITERATOR, value);
+}
+function defineIterator(Constructor, NAME, value, DEFAULT){
+ var proto = Constructor.prototype
+ , iter = proto[SYMBOL_ITERATOR] || proto[FF_ITERATOR] || DEFAULT && proto[DEFAULT] || value;
+ // Define iterator
+ if($.FW)setIterator(proto, iter);
+ if(iter !== value){
+ var iterProto = $.getProto(iter.call(new Constructor));
+ // Set @@toStringTag to native iterators
+ cof.set(iterProto, NAME + ' Iterator', true);
+ // FF fix
+ if($.FW)$.has(proto, FF_ITERATOR) && setIterator(iterProto, $.that);
+ }
+ // Plug for library
+ Iterators[NAME] = iter;
+ // FF & v8 fix
+ Iterators[NAME + ' Iterator'] = $.that;
+ return iter;
+}
+function getIterator(it){
+ var Symbol = $.g.Symbol
+ , ext = it[Symbol && Symbol.iterator || FF_ITERATOR]
+ , getIter = ext || it[SYMBOL_ITERATOR] || Iterators[cof.classof(it)];
+ return assertObject(getIter.call(it));
+}
+function closeIterator(iterator){
+ var ret = iterator['return'];
+ if(ret !== undefined)assertObject(ret.call(iterator));
+}
+function stepCall(iterator, fn, value, entries){
+ try {
+ return entries ? fn(assertObject(value)[0], value[1]) : fn(value);
+ } catch(e){
+ closeIterator(iterator);
+ throw e;
+ }
+}
+var DANGER_CLOSING = true;
+!function(){
+ try {
+ var iter = [1].keys();
+ iter['return'] = function(){ DANGER_CLOSING = false; };
+ Array.from(iter, function(){ throw 2; });
+ } catch(e){ /* empty */ }
+}();
+var $iter = module.exports = {
+ BUGGY: BUGGY,
+ DANGER_CLOSING: DANGER_CLOSING,
+ fail: function(exec){
+ var fail = true;
+ try {
+ var arr = [[{}, 1]]
+ , iter = arr[SYMBOL_ITERATOR]()
+ , next = iter.next;
+ iter.next = function(){
+ fail = false;
+ return next.call(this);
+ };
+ arr[SYMBOL_ITERATOR] = function(){
+ return iter;
+ };
+ exec(arr);
+ } catch(e){ /* empty */ }
+ return fail;
+ },
+ Iterators: Iterators,
+ prototype: IteratorPrototype,
+ step: function(done, value){
+ return {value: value, done: !!done};
+ },
+ stepCall: stepCall,
+ close: closeIterator,
+ is: function(it){
+ var O = Object(it)
+ , Symbol = $.g.Symbol
+ , SYM = Symbol && Symbol.iterator || FF_ITERATOR;
+ return SYM in O || SYMBOL_ITERATOR in O || $.has(Iterators, cof.classof(O));
+ },
+ get: getIterator,
+ set: setIterator,
+ create: function(Constructor, NAME, next, proto){
+ Constructor.prototype = $.create(proto || $iter.prototype, {next: $.desc(1, next)});
+ cof.set(Constructor, NAME + ' Iterator');
+ },
+ define: defineIterator,
+ std: function(Base, NAME, Constructor, next, DEFAULT, IS_SET, FORCE){
+ function createIter(kind){
+ return function(){
+ return new Constructor(this, kind);
+ };
+ }
+ $iter.create(Constructor, NAME, next);
+ var entries = createIter('key+value')
+ , values = createIter('value')
+ , proto = Base.prototype
+ , methods, key;
+ if(DEFAULT == 'value')values = defineIterator(Base, NAME, values, 'values');
+ else entries = defineIterator(Base, NAME, entries, 'entries');
+ if(DEFAULT){
+ methods = {
+ entries: entries,
+ keys: IS_SET ? values : createIter('key'),
+ values: values
+ };
+ $def($def.P + $def.F * BUGGY, NAME, methods);
+ if(FORCE)for(key in methods){
+ if(!(key in proto))$.hide(proto, key, methods[key]);
+ }
+ }
+ },
+ forOf: function(iterable, entries, fn, that){
+ var iterator = getIterator(iterable)
+ , f = ctx(fn, that, entries ? 2 : 1)
+ , step;
+ while(!(step = iterator.next()).done){
+ if(stepCall(iterator, f, step.value, entries) === false){
+ return closeIterator(iterator);
+ }
+ }
+ }
+};
+},{"./$":16,"./$.assert":5,"./$.cof":7,"./$.ctx":11,"./$.def":12,"./$.wks":26}],16:[function(require,module,exports){
+'use strict';
+var global = typeof self != 'undefined' ? self : Function('return this')()
+ , core = {}
+ , defineProperty = Object.defineProperty
+ , hasOwnProperty = {}.hasOwnProperty
+ , ceil = Math.ceil
+ , floor = Math.floor
+ , max = Math.max
+ , min = Math.min;
+// The engine works fine with descriptors? Thank's IE8 for his funny defineProperty.
+var DESC = !!function(){
+ try {
+ return defineProperty({}, 'a', {get: function(){ return 2; }}).a == 2;
+ } catch(e){ /* empty */ }
+}();
+var hide = createDefiner(1);
+// 7.1.4 ToInteger
+function toInteger(it){
+ return isNaN(it = +it) ? 0 : (it > 0 ? floor : ceil)(it);
+}
+function desc(bitmap, value){
+ return {
+ enumerable : !(bitmap & 1),
+ configurable: !(bitmap & 2),
+ writable : !(bitmap & 4),
+ value : value
+ };
+}
+function simpleSet(object, key, value){
+ object[key] = value;
+ return object;
+}
+function createDefiner(bitmap){
+ return DESC ? function(object, key, value){
+ return $.setDesc(object, key, desc(bitmap, value)); // eslint-disable-line no-use-before-define
+ } : simpleSet;
+}
+
+function isObject(it){
+ return it !== null && (typeof it == 'object' || typeof it == 'function');
+}
+function isFunction(it){
+ return typeof it == 'function';
+}
+function assertDefined(it){
+ if(it == undefined)throw TypeError("Can't call method on " + it);
+ return it;
+}
+
+var $ = module.exports = require('./$.fw')({
+ g: global,
+ core: core,
+ html: global.document && document.documentElement,
+ // http://jsperf.com/core-js-isobject
+ isObject: isObject,
+ isFunction: isFunction,
+ it: function(it){
+ return it;
+ },
+ that: function(){
+ return this;
+ },
+ // 7.1.4 ToInteger
+ toInteger: toInteger,
+ // 7.1.15 ToLength
+ toLength: function(it){
+ return it > 0 ? min(toInteger(it), 0x1fffffffffffff) : 0; // pow(2, 53) - 1 == 9007199254740991
+ },
+ toIndex: function(index, length){
+ index = toInteger(index);
+ return index < 0 ? max(index + length, 0) : min(index, length);
+ },
+ has: function(it, key){
+ return hasOwnProperty.call(it, key);
+ },
+ create: Object.create,
+ getProto: Object.getPrototypeOf,
+ DESC: DESC,
+ desc: desc,
+ getDesc: Object.getOwnPropertyDescriptor,
+ setDesc: defineProperty,
+ getKeys: Object.keys,
+ getNames: Object.getOwnPropertyNames,
+ getSymbols: Object.getOwnPropertySymbols,
+ // Dummy, fix for not array-like ES3 string in es5 module
+ assertDefined: assertDefined,
+ ES5Object: Object,
+ toObject: function(it){
+ return $.ES5Object(assertDefined(it));
+ },
+ hide: hide,
+ def: createDefiner(0),
+ set: global.Symbol ? simpleSet : hide,
+ mix: function(target, src){
+ for(var key in src)hide(target, key, src[key]);
+ return target;
+ },
+ each: [].forEach
+});
+if(typeof __e != 'undefined')__e = core;
+if(typeof __g != 'undefined')__g = global;
+},{"./$.fw":13}],17:[function(require,module,exports){
+var $ = require('./$');
+module.exports = function(object, el){
+ var O = $.toObject(object)
+ , keys = $.getKeys(O)
+ , length = keys.length
+ , index = 0
+ , key;
+ while(length > index)if(O[key = keys[index++]] === el)return key;
+};
+},{"./$":16}],18:[function(require,module,exports){
+var $ = require('./$')
+ , assertObject = require('./$.assert').obj;
+module.exports = function(it){
+ assertObject(it);
+ return $.getSymbols ? $.getNames(it).concat($.getSymbols(it)) : $.getNames(it);
+};
+},{"./$":16,"./$.assert":5}],19:[function(require,module,exports){
+'use strict';
+module.exports = function(regExp, replace, isStatic){
+ var replacer = replace === Object(replace) ? function(part){
+ return replace[part];
+ } : replace;
+ return function(it){
+ return String(isStatic ? it : this).replace(regExp, replacer);
+ };
+};
+},{}],20:[function(require,module,exports){
+// Works with __proto__ only. Old v8 can't works with null proto objects.
+/*eslint-disable no-proto */
+var $ = require('./$')
+ , assert = require('./$.assert');
+module.exports = Object.setPrototypeOf || ('__proto__' in {} // eslint-disable-line
+ ? function(buggy, set){
+ try {
+ set = require('./$.ctx')(Function.call, $.getDesc(Object.prototype, '__proto__').set, 2);
+ set({}, []);
+ } catch(e){ buggy = true; }
+ return function(O, proto){
+ assert.obj(O);
+ assert(proto === null || $.isObject(proto), proto, ": can't set as prototype!");
+ if(buggy)O.__proto__ = proto;
+ else set(O, proto);
+ return O;
+ };
+ }()
+ : undefined);
+},{"./$":16,"./$.assert":5,"./$.ctx":11}],21:[function(require,module,exports){
+var $ = require('./$');
+module.exports = function(C){
+ if($.DESC && $.FW)$.setDesc(C, require('./$.wks')('species'), {
+ configurable: true,
+ get: $.that
+ });
+};
+},{"./$":16,"./$.wks":26}],22:[function(require,module,exports){
+'use strict';
+// true -> String#at
+// false -> String#codePointAt
+var $ = require('./$');
+module.exports = function(TO_STRING){
+ return function(pos){
+ var s = String($.assertDefined(this))
+ , i = $.toInteger(pos)
+ , l = s.length
+ , a, b;
+ if(i < 0 || i >= l)return TO_STRING ? '' : undefined;
+ a = s.charCodeAt(i);
+ return a < 0xd800 || a > 0xdbff || i + 1 === l
+ || (b = s.charCodeAt(i + 1)) < 0xdc00 || b > 0xdfff
+ ? TO_STRING ? s.charAt(i) : a
+ : TO_STRING ? s.slice(i, i + 2) : (a - 0xd800 << 10) + (b - 0xdc00) + 0x10000;
+ };
+};
+},{"./$":16}],23:[function(require,module,exports){
+'use strict';
+var $ = require('./$')
+ , ctx = require('./$.ctx')
+ , cof = require('./$.cof')
+ , invoke = require('./$.invoke')
+ , global = $.g
+ , isFunction = $.isFunction
+ , setTask = global.setImmediate
+ , clearTask = global.clearImmediate
+ , postMessage = global.postMessage
+ , addEventListener = global.addEventListener
+ , MessageChannel = global.MessageChannel
+ , counter = 0
+ , queue = {}
+ , ONREADYSTATECHANGE = 'onreadystatechange'
+ , defer, channel, port;
+function run(){
+ var id = +this;
+ if($.has(queue, id)){
+ var fn = queue[id];
+ delete queue[id];
+ fn();
+ }
+}
+function listner(event){
+ run.call(event.data);
+}
+// Node.js 0.9+ & IE10+ has setImmediate, otherwise:
+if(!isFunction(setTask) || !isFunction(clearTask)){
+ setTask = function(fn){
+ var args = [], i = 1;
+ while(arguments.length > i)args.push(arguments[i++]);
+ queue[++counter] = function(){
+ invoke(isFunction(fn) ? fn : Function(fn), args);
+ };
+ defer(counter);
+ return counter;
+ };
+ clearTask = function(id){
+ delete queue[id];
+ };
+ // Node.js 0.8-
+ if(cof(global.process) == 'process'){
+ defer = function(id){
+ global.process.nextTick(ctx(run, id, 1));
+ };
+ // Modern browsers, skip implementation for WebWorkers
+ // IE8 has postMessage, but it's sync & typeof its postMessage is object
+ } else if(addEventListener && isFunction(postMessage) && !$.g.importScripts){
+ defer = function(id){
+ postMessage(id, '*');
+ };
+ addEventListener('message', listner, false);
+ // WebWorkers
+ } else if(isFunction(MessageChannel)){
+ channel = new MessageChannel;
+ port = channel.port2;
+ channel.port1.onmessage = listner;
+ defer = ctx(port.postMessage, port, 1);
+ // IE8-
+ } else if($.g.document && ONREADYSTATECHANGE in document.createElement('script')){
+ defer = function(id){
+ $.html.appendChild(document.createElement('script'))[ONREADYSTATECHANGE] = function(){
+ $.html.removeChild(this);
+ run.call(id);
+ };
+ };
+ // Rest old browsers
+ } else {
+ defer = function(id){
+ setTimeout(ctx(run, id, 1), 0);
+ };
+ }
+}
+module.exports = {
+ set: setTask,
+ clear: clearTask
+};
+},{"./$":16,"./$.cof":7,"./$.ctx":11,"./$.invoke":14}],24:[function(require,module,exports){
+var sid = 0;
+function uid(key){
+ return 'Symbol(' + key + ')_' + (++sid + Math.random()).toString(36);
+}
+uid.safe = require('./$').g.Symbol || uid;
+module.exports = uid;
+},{"./$":16}],25:[function(require,module,exports){
+// 22.1.3.31 Array.prototype[@@unscopables]
+var $ = require('./$')
+ , UNSCOPABLES = require('./$.wks')('unscopables');
+if($.FW && !(UNSCOPABLES in []))$.hide(Array.prototype, UNSCOPABLES, {});
+module.exports = function(key){
+ if($.FW)[][UNSCOPABLES][key] = true;
+};
+},{"./$":16,"./$.wks":26}],26:[function(require,module,exports){
+var global = require('./$').g
+ , store = {};
+module.exports = function(name){
+ return store[name] || (store[name] =
+ global.Symbol && global.Symbol[name] || require('./$.uid').safe('Symbol.' + name));
+};
+},{"./$":16,"./$.uid":24}],27:[function(require,module,exports){
+'use strict';
+var $ = require('./$')
+ , $def = require('./$.def')
+ , toIndex = $.toIndex;
+$def($def.P, 'Array', {
+ // 22.1.3.3 Array.prototype.copyWithin(target, start, end = this.length)
+ copyWithin: function(target/* = 0 */, start /* = 0, end = @length */){
+ var O = Object($.assertDefined(this))
+ , len = $.toLength(O.length)
+ , to = toIndex(target, len)
+ , from = toIndex(start, len)
+ , end = arguments[2]
+ , fin = end === undefined ? len : toIndex(end, len)
+ , count = Math.min(fin - from, len - to)
+ , inc = 1;
+ if(from < to && to < from + count){
+ inc = -1;
+ from = from + count - 1;
+ to = to + count - 1;
+ }
+ while(count-- > 0){
+ if(from in O)O[to] = O[from];
+ else delete O[to];
+ to += inc;
+ from += inc;
+ } return O;
+ }
+});
+require('./$.unscope')('copyWithin');
+},{"./$":16,"./$.def":12,"./$.unscope":25}],28:[function(require,module,exports){
+'use strict';
+var $ = require('./$')
+ , $def = require('./$.def')
+ , toIndex = $.toIndex;
+$def($def.P, 'Array', {
+ // 22.1.3.6 Array.prototype.fill(value, start = 0, end = this.length)
+ fill: function(value /*, start = 0, end = @length */){
+ var O = Object($.assertDefined(this))
+ , length = $.toLength(O.length)
+ , index = toIndex(arguments[1], length)
+ , end = arguments[2]
+ , endPos = end === undefined ? length : toIndex(end, length);
+ while(endPos > index)O[index++] = value;
+ return O;
+ }
+});
+require('./$.unscope')('fill');
+},{"./$":16,"./$.def":12,"./$.unscope":25}],29:[function(require,module,exports){
+var $def = require('./$.def');
+$def($def.P, 'Array', {
+ // 22.1.3.9 Array.prototype.findIndex(predicate, thisArg = undefined)
+ findIndex: require('./$.array-methods')(6)
+});
+require('./$.unscope')('findIndex');
+},{"./$.array-methods":4,"./$.def":12,"./$.unscope":25}],30:[function(require,module,exports){
+var $def = require('./$.def');
+$def($def.P, 'Array', {
+ // 22.1.3.8 Array.prototype.find(predicate, thisArg = undefined)
+ find: require('./$.array-methods')(5)
+});
+require('./$.unscope')('find');
+},{"./$.array-methods":4,"./$.def":12,"./$.unscope":25}],31:[function(require,module,exports){
+var $ = require('./$')
+ , ctx = require('./$.ctx')
+ , $def = require('./$.def')
+ , $iter = require('./$.iter')
+ , stepCall = $iter.stepCall;
+$def($def.S + $def.F * $iter.DANGER_CLOSING, 'Array', {
+ // 22.1.2.1 Array.from(arrayLike, mapfn = undefined, thisArg = undefined)
+ from: function(arrayLike/*, mapfn = undefined, thisArg = undefined*/){
+ var O = Object($.assertDefined(arrayLike))
+ , mapfn = arguments[1]
+ , mapping = mapfn !== undefined
+ , f = mapping ? ctx(mapfn, arguments[2], 2) : undefined
+ , index = 0
+ , length, result, step, iterator;
+ if($iter.is(O)){
+ iterator = $iter.get(O);
+ // strange IE quirks mode bug -> use typeof instead of isFunction
+ result = new (typeof this == 'function' ? this : Array);
+ for(; !(step = iterator.next()).done; index++){
+ result[index] = mapping ? stepCall(iterator, f, [step.value, index], true) : step.value;
+ }
+ } else {
+ // strange IE quirks mode bug -> use typeof instead of isFunction
+ result = new (typeof this == 'function' ? this : Array)(length = $.toLength(O.length));
+ for(; length > index; index++){
+ result[index] = mapping ? f(O[index], index) : O[index];
+ }
+ }
+ result.length = index;
+ return result;
+ }
+});
+},{"./$":16,"./$.ctx":11,"./$.def":12,"./$.iter":15}],32:[function(require,module,exports){
+var $ = require('./$')
+ , setUnscope = require('./$.unscope')
+ , ITER = require('./$.uid').safe('iter')
+ , $iter = require('./$.iter')
+ , step = $iter.step
+ , Iterators = $iter.Iterators;
+
+// 22.1.3.4 Array.prototype.entries()
+// 22.1.3.13 Array.prototype.keys()
+// 22.1.3.29 Array.prototype.values()
+// 22.1.3.30 Array.prototype[@@iterator]()
+$iter.std(Array, 'Array', function(iterated, kind){
+ $.set(this, ITER, {o: $.toObject(iterated), i: 0, k: kind});
+// 22.1.5.2.1 %ArrayIteratorPrototype%.next()
+}, function(){
+ var iter = this[ITER]
+ , O = iter.o
+ , kind = iter.k
+ , index = iter.i++;
+ if(!O || index >= O.length){
+ iter.o = undefined;
+ return step(1);
+ }
+ if(kind == 'key' )return step(0, index);
+ if(kind == 'value')return step(0, O[index]);
+ return step(0, [index, O[index]]);
+}, 'value');
+
+// argumentsList[@@iterator] is %ArrayProto_values% (9.4.4.6, 9.4.4.7)
+Iterators.Arguments = Iterators.Array;
+
+setUnscope('keys');
+setUnscope('values');
+setUnscope('entries');
+},{"./$":16,"./$.iter":15,"./$.uid":24,"./$.unscope":25}],33:[function(require,module,exports){
+var $def = require('./$.def');
+$def($def.S, 'Array', {
+ // 22.1.2.3 Array.of( ...items)
+ of: function(/* ...args */){
+ var index = 0
+ , length = arguments.length
+ // strange IE quirks mode bug -> use typeof instead of isFunction
+ , result = new (typeof this == 'function' ? this : Array)(length);
+ while(length > index)result[index] = arguments[index++];
+ result.length = length;
+ return result;
+ }
+});
+},{"./$.def":12}],34:[function(require,module,exports){
+require('./$.species')(Array);
+},{"./$.species":21}],35:[function(require,module,exports){
+'use strict';
+var $ = require('./$')
+ , NAME = 'name'
+ , setDesc = $.setDesc
+ , FunctionProto = Function.prototype;
+// 19.2.4.2 name
+NAME in FunctionProto || $.FW && $.DESC && setDesc(FunctionProto, NAME, {
+ configurable: true,
+ get: function(){
+ var match = String(this).match(/^\s*function ([^ (]*)/)
+ , name = match ? match[1] : '';
+ $.has(this, NAME) || setDesc(this, NAME, $.desc(5, name));
+ return name;
+ },
+ set: function(value){
+ $.has(this, NAME) || setDesc(this, NAME, $.desc(0, value));
+ }
+});
+},{"./$":16}],36:[function(require,module,exports){
+'use strict';
+var strong = require('./$.collection-strong');
+
+// 23.1 Map Objects
+require('./$.collection')('Map', {
+ // 23.1.3.6 Map.prototype.get(key)
+ get: function(key){
+ var entry = strong.getEntry(this, key);
+ return entry && entry.v;
+ },
+ // 23.1.3.9 Map.prototype.set(key, value)
+ set: function(key, value){
+ return strong.def(this, key === 0 ? 0 : key, value);
+ }
+}, strong, true);
+},{"./$.collection":10,"./$.collection-strong":8}],37:[function(require,module,exports){
+var Infinity = 1 / 0
+ , $def = require('./$.def')
+ , E = Math.E
+ , pow = Math.pow
+ , abs = Math.abs
+ , exp = Math.exp
+ , log = Math.log
+ , sqrt = Math.sqrt
+ , ceil = Math.ceil
+ , floor = Math.floor
+ , sign = Math.sign || function(x){
+ return (x = +x) == 0 || x != x ? x : x < 0 ? -1 : 1;
+ };
+
+// 20.2.2.5 Math.asinh(x)
+function asinh(x){
+ return !isFinite(x = +x) || x == 0 ? x : x < 0 ? -asinh(-x) : log(x + sqrt(x * x + 1));
+}
+// 20.2.2.14 Math.expm1(x)
+function expm1(x){
+ return (x = +x) == 0 ? x : x > -1e-6 && x < 1e-6 ? x + x * x / 2 : exp(x) - 1;
+}
+
+$def($def.S, 'Math', {
+ // 20.2.2.3 Math.acosh(x)
+ acosh: function(x){
+ return (x = +x) < 1 ? NaN : isFinite(x) ? log(x / E + sqrt(x + 1) * sqrt(x - 1) / E) + 1 : x;
+ },
+ // 20.2.2.5 Math.asinh(x)
+ asinh: asinh,
+ // 20.2.2.7 Math.atanh(x)
+ atanh: function(x){
+ return (x = +x) == 0 ? x : log((1 + x) / (1 - x)) / 2;
+ },
+ // 20.2.2.9 Math.cbrt(x)
+ cbrt: function(x){
+ return sign(x = +x) * pow(abs(x), 1 / 3);
+ },
+ // 20.2.2.11 Math.clz32(x)
+ clz32: function(x){
+ return (x >>>= 0) ? 32 - x.toString(2).length : 32;
+ },
+ // 20.2.2.12 Math.cosh(x)
+ cosh: function(x){
+ return (exp(x = +x) + exp(-x)) / 2;
+ },
+ // 20.2.2.14 Math.expm1(x)
+ expm1: expm1,
+ // 20.2.2.16 Math.fround(x)
+ // TODO: fallback for IE9-
+ fround: function(x){
+ return new Float32Array([x])[0];
+ },
+ // 20.2.2.17 Math.hypot([value1[, value2[, … ]]])
+ hypot: function(value1, value2){ // eslint-disable-line no-unused-vars
+ var sum = 0
+ , len1 = arguments.length
+ , len2 = len1
+ , args = Array(len1)
+ , larg = -Infinity
+ , arg;
+ while(len1--){
+ arg = args[len1] = +arguments[len1];
+ if(arg == Infinity || arg == -Infinity)return Infinity;
+ if(arg > larg)larg = arg;
+ }
+ larg = arg || 1;
+ while(len2--)sum += pow(args[len2] / larg, 2);
+ return larg * sqrt(sum);
+ },
+ // 20.2.2.18 Math.imul(x, y)
+ imul: function(x, y){
+ var UInt16 = 0xffff
+ , xn = +x
+ , yn = +y
+ , xl = UInt16 & xn
+ , yl = UInt16 & yn;
+ return 0 | xl * yl + ((UInt16 & xn >>> 16) * yl + xl * (UInt16 & yn >>> 16) << 16 >>> 0);
+ },
+ // 20.2.2.20 Math.log1p(x)
+ log1p: function(x){
+ return (x = +x) > -1e-8 && x < 1e-8 ? x - x * x / 2 : log(1 + x);
+ },
+ // 20.2.2.21 Math.log10(x)
+ log10: function(x){
+ return log(x) / Math.LN10;
+ },
+ // 20.2.2.22 Math.log2(x)
+ log2: function(x){
+ return log(x) / Math.LN2;
+ },
+ // 20.2.2.28 Math.sign(x)
+ sign: sign,
+ // 20.2.2.30 Math.sinh(x)
+ sinh: function(x){
+ return abs(x = +x) < 1 ? (expm1(x) - expm1(-x)) / 2 : (exp(x - 1) - exp(-x - 1)) * (E / 2);
+ },
+ // 20.2.2.33 Math.tanh(x)
+ tanh: function(x){
+ var a = expm1(x = +x)
+ , b = expm1(-x);
+ return a == Infinity ? 1 : b == Infinity ? -1 : (a - b) / (exp(x) + exp(-x));
+ },
+ // 20.2.2.34 Math.trunc(x)
+ trunc: function(it){
+ return (it > 0 ? floor : ceil)(it);
+ }
+});
+},{"./$.def":12}],38:[function(require,module,exports){
+'use strict';
+var $ = require('./$')
+ , isObject = $.isObject
+ , isFunction = $.isFunction
+ , NUMBER = 'Number'
+ , Number = $.g[NUMBER]
+ , Base = Number
+ , proto = Number.prototype;
+function toPrimitive(it){
+ var fn, val;
+ if(isFunction(fn = it.valueOf) && !isObject(val = fn.call(it)))return val;
+ if(isFunction(fn = it.toString) && !isObject(val = fn.call(it)))return val;
+ throw TypeError("Can't convert object to number");
+}
+function toNumber(it){
+ if(isObject(it))it = toPrimitive(it);
+ if(typeof it == 'string' && it.length > 2 && it.charCodeAt(0) == 48){
+ var binary = false;
+ switch(it.charCodeAt(1)){
+ case 66 : case 98 : binary = true;
+ case 79 : case 111 : return parseInt(it.slice(2), binary ? 2 : 8);
+ }
+ } return +it;
+}
+if($.FW && !(Number('0o1') && Number('0b1'))){
+ Number = function Number(it){
+ return this instanceof Number ? new Base(toNumber(it)) : toNumber(it);
+ };
+ $.each.call($.DESC ? $.getNames(Base) : (
+ // ES3:
+ 'MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,' +
+ // ES6 (in case, if modules with ES6 Number statics required before):
+ 'EPSILON,isFinite,isInteger,isNaN,isSafeInteger,MAX_SAFE_INTEGER,' +
+ 'MIN_SAFE_INTEGER,parseFloat,parseInt,isInteger'
+ ).split(','), function(key){
+ if($.has(Base, key) && !$.has(Number, key)){
+ $.setDesc(Number, key, $.getDesc(Base, key));
+ }
+ }
+ );
+ Number.prototype = proto;
+ proto.constructor = Number;
+ $.hide($.g, NUMBER, Number);
+}
+},{"./$":16}],39:[function(require,module,exports){
+var $ = require('./$')
+ , $def = require('./$.def')
+ , abs = Math.abs
+ , floor = Math.floor
+ , MAX_SAFE_INTEGER = 0x1fffffffffffff; // pow(2, 53) - 1 == 9007199254740991;
+function isInteger(it){
+ return !$.isObject(it) && isFinite(it) && floor(it) === it;
+}
+$def($def.S, 'Number', {
+ // 20.1.2.1 Number.EPSILON
+ EPSILON: Math.pow(2, -52),
+ // 20.1.2.2 Number.isFinite(number)
+ isFinite: function(it){
+ return typeof it == 'number' && isFinite(it);
+ },
+ // 20.1.2.3 Number.isInteger(number)
+ isInteger: isInteger,
+ // 20.1.2.4 Number.isNaN(number)
+ isNaN: function(number){
+ return number != number;
+ },
+ // 20.1.2.5 Number.isSafeInteger(number)
+ isSafeInteger: function(number){
+ return isInteger(number) && abs(number) <= MAX_SAFE_INTEGER;
+ },
+ // 20.1.2.6 Number.MAX_SAFE_INTEGER
+ MAX_SAFE_INTEGER: MAX_SAFE_INTEGER,
+ // 20.1.2.10 Number.MIN_SAFE_INTEGER
+ MIN_SAFE_INTEGER: -MAX_SAFE_INTEGER,
+ // 20.1.2.12 Number.parseFloat(string)
+ parseFloat: parseFloat,
+ // 20.1.2.13 Number.parseInt(string, radix)
+ parseInt: parseInt
+});
+},{"./$":16,"./$.def":12}],40:[function(require,module,exports){
+// 19.1.3.1 Object.assign(target, source)
+var $def = require('./$.def');
+$def($def.S, 'Object', {assign: require('./$.assign')});
+},{"./$.assign":6,"./$.def":12}],41:[function(require,module,exports){
+// 19.1.3.10 Object.is(value1, value2)
+var $def = require('./$.def');
+$def($def.S, 'Object', {
+ is: function(x, y){
+ return x === y ? x !== 0 || 1 / x === 1 / y : x != x && y != y;
+ }
+});
+},{"./$.def":12}],42:[function(require,module,exports){
+// 19.1.3.19 Object.setPrototypeOf(O, proto)
+var $def = require('./$.def');
+$def($def.S, 'Object', {setPrototypeOf: require('./$.set-proto')});
+},{"./$.def":12,"./$.set-proto":20}],43:[function(require,module,exports){
+var $ = require('./$')
+ , $def = require('./$.def')
+ , isObject = $.isObject
+ , toObject = $.toObject;
+function wrapObjectMethod(METHOD, MODE){
+ var fn = ($.core.Object || {})[METHOD] || Object[METHOD]
+ , f = 0
+ , o = {};
+ o[METHOD] = MODE == 1 ? function(it){
+ return isObject(it) ? fn(it) : it;
+ } : MODE == 2 ? function(it){
+ return isObject(it) ? fn(it) : true;
+ } : MODE == 3 ? function(it){
+ return isObject(it) ? fn(it) : false;
+ } : MODE == 4 ? function(it, key){
+ return fn(toObject(it), key);
+ } : MODE == 5 ? function(it){
+ return fn(Object($.assertDefined(it)));
+ } : function(it){
+ return fn(toObject(it));
+ };
+ try {
+ fn('z');
+ } catch(e){
+ f = 1;
+ }
+ $def($def.S + $def.F * f, 'Object', o);
+}
+wrapObjectMethod('freeze', 1);
+wrapObjectMethod('seal', 1);
+wrapObjectMethod('preventExtensions', 1);
+wrapObjectMethod('isFrozen', 2);
+wrapObjectMethod('isSealed', 2);
+wrapObjectMethod('isExtensible', 3);
+wrapObjectMethod('getOwnPropertyDescriptor', 4);
+wrapObjectMethod('getPrototypeOf', 5);
+wrapObjectMethod('keys');
+wrapObjectMethod('getOwnPropertyNames');
+},{"./$":16,"./$.def":12}],44:[function(require,module,exports){
+'use strict';
+// 19.1.3.6 Object.prototype.toString()
+var $ = require('./$')
+ , cof = require('./$.cof')
+ , tmp = {};
+tmp[require('./$.wks')('toStringTag')] = 'z';
+if($.FW && cof(tmp) != 'z')$.hide(Object.prototype, 'toString', function(){
+ return '[object ' + cof.classof(this) + ']';
+});
+},{"./$":16,"./$.cof":7,"./$.wks":26}],45:[function(require,module,exports){
+'use strict';
+var $ = require('./$')
+ , ctx = require('./$.ctx')
+ , cof = require('./$.cof')
+ , $def = require('./$.def')
+ , assert = require('./$.assert')
+ , $iter = require('./$.iter')
+ , SPECIES = require('./$.wks')('species')
+ , RECORD = require('./$.uid').safe('record')
+ , forOf = $iter.forOf
+ , PROMISE = 'Promise'
+ , global = $.g
+ , process = global.process
+ , asap = process && process.nextTick || require('./$.task').set
+ , Promise = global[PROMISE]
+ , Base = Promise
+ , isFunction = $.isFunction
+ , isObject = $.isObject
+ , assertFunction = assert.fn
+ , assertObject = assert.obj
+ , test;
+function getConstructor(C){
+ var S = assertObject(C)[SPECIES];
+ return S != undefined ? S : C;
+}
+isFunction(Promise) && isFunction(Promise.resolve)
+&& Promise.resolve(test = new Promise(function(){})) == test
+|| function(){
+ function isThenable(it){
+ var then;
+ if(isObject(it))then = it.then;
+ return isFunction(then) ? then : false;
+ }
+ function handledRejectionOrHasOnRejected(promise){
+ var record = promise[RECORD]
+ , chain = record.c
+ , i = 0
+ , react;
+ if(record.h)return true;
+ while(chain.length > i){
+ react = chain[i++];
+ if(react.fail || handledRejectionOrHasOnRejected(react.P))return true;
+ }
+ }
+ function notify(record, isReject){
+ var chain = record.c;
+ if(isReject || chain.length)asap(function(){
+ var promise = record.p
+ , value = record.v
+ , ok = record.s == 1
+ , i = 0;
+ if(isReject && !handledRejectionOrHasOnRejected(promise)){
+ setTimeout(function(){
+ if(!handledRejectionOrHasOnRejected(promise)){
+ if(cof(process) == 'process'){
+ process.emit('unhandledRejection', value, promise);
+ } else if(global.console && isFunction(console.error)){
+ console.error('Unhandled promise rejection', value);
+ }
+ }
+ }, 1e3);
+ } else while(chain.length > i)!function(react){
+ var cb = ok ? react.ok : react.fail
+ , ret, then;
+ try {
+ if(cb){
+ if(!ok)record.h = true;
+ ret = cb === true ? value : cb(value);
+ if(ret === react.P){
+ react.rej(TypeError(PROMISE + '-chain cycle'));
+ } else if(then = isThenable(ret)){
+ then.call(ret, react.res, react.rej);
+ } else react.res(ret);
+ } else react.rej(value);
+ } catch(err){
+ react.rej(err);
+ }
+ }(chain[i++]);
+ chain.length = 0;
+ });
+ }
+ function reject(value){
+ var record = this;
+ if(record.d)return;
+ record.d = true;
+ record = record.r || record; // unwrap
+ record.v = value;
+ record.s = 2;
+ notify(record, true);
+ }
+ function resolve(value){
+ var record = this
+ , then, wrapper;
+ if(record.d)return;
+ record.d = true;
+ record = record.r || record; // unwrap
+ try {
+ if(then = isThenable(value)){
+ wrapper = {r: record, d: false}; // wrap
+ then.call(value, ctx(resolve, wrapper, 1), ctx(reject, wrapper, 1));
+ } else {
+ record.v = value;
+ record.s = 1;
+ notify(record);
+ }
+ } catch(err){
+ reject.call(wrapper || {r: record, d: false}, err); // wrap
+ }
+ }
+ // 25.4.3.1 Promise(executor)
+ Promise = function(executor){
+ assertFunction(executor);
+ var record = {
+ p: assert.inst(this, Promise, PROMISE), // <- promise
+ c: [], // <- chain
+ s: 0, // <- state
+ d: false, // <- done
+ v: undefined, // <- value
+ h: false // <- handled rejection
+ };
+ $.hide(this, RECORD, record);
+ try {
+ executor(ctx(resolve, record, 1), ctx(reject, record, 1));
+ } catch(err){
+ reject.call(record, err);
+ }
+ };
+ $.mix(Promise.prototype, {
+ // 25.4.5.3 Promise.prototype.then(onFulfilled, onRejected)
+ then: function(onFulfilled, onRejected){
+ var S = assertObject(assertObject(this).constructor)[SPECIES];
+ var react = {
+ ok: isFunction(onFulfilled) ? onFulfilled : true,
+ fail: isFunction(onRejected) ? onRejected : false
+ };
+ var P = react.P = new (S != undefined ? S : Promise)(function(res, rej){
+ react.res = assertFunction(res);
+ react.rej = assertFunction(rej);
+ });
+ var record = this[RECORD];
+ record.c.push(react);
+ record.s && notify(record);
+ return P;
+ },
+ // 25.4.5.1 Promise.prototype.catch(onRejected)
+ 'catch': function(onRejected){
+ return this.then(undefined, onRejected);
+ }
+ });
+}();
+$def($def.G + $def.W + $def.F * (Promise != Base), {Promise: Promise});
+$def($def.S, PROMISE, {
+ // 25.4.4.5 Promise.reject(r)
+ reject: function(r){
+ return new (getConstructor(this))(function(res, rej){
+ rej(r);
+ });
+ },
+ // 25.4.4.6 Promise.resolve(x)
+ resolve: function(x){
+ return isObject(x) && RECORD in x && $.getProto(x) === this.prototype
+ ? x : new (getConstructor(this))(function(res){
+ res(x);
+ });
+ }
+});
+$def($def.S + $def.F * ($iter.fail(function(iter){
+ Promise.all(iter)['catch'](function(){});
+}) || $iter.DANGER_CLOSING), PROMISE, {
+ // 25.4.4.1 Promise.all(iterable)
+ all: function(iterable){
+ var C = getConstructor(this)
+ , values = [];
+ return new C(function(resolve, reject){
+ forOf(iterable, false, values.push, values);
+ var remaining = values.length
+ , results = Array(remaining);
+ if(remaining)$.each.call(values, function(promise, index){
+ C.resolve(promise).then(function(value){
+ results[index] = value;
+ --remaining || resolve(results);
+ }, reject);
+ });
+ else resolve(results);
+ });
+ },
+ // 25.4.4.4 Promise.race(iterable)
+ race: function(iterable){
+ var C = getConstructor(this);
+ return new C(function(resolve, reject){
+ forOf(iterable, false, function(promise){
+ C.resolve(promise).then(resolve, reject);
+ });
+ });
+ }
+});
+cof.set(Promise, PROMISE);
+require('./$.species')(Promise);
+},{"./$":16,"./$.assert":5,"./$.cof":7,"./$.ctx":11,"./$.def":12,"./$.iter":15,"./$.species":21,"./$.task":23,"./$.uid":24,"./$.wks":26}],46:[function(require,module,exports){
+var $ = require('./$')
+ , $def = require('./$.def')
+ , setProto = require('./$.set-proto')
+ , $iter = require('./$.iter')
+ , ITER = require('./$.uid').safe('iter')
+ , step = $iter.step
+ , assert = require('./$.assert')
+ , isObject = $.isObject
+ , getDesc = $.getDesc
+ , setDesc = $.setDesc
+ , getProto = $.getProto
+ , apply = Function.apply
+ , assertObject = assert.obj
+ , isExtensible = Object.isExtensible || $.it;
+function Enumerate(iterated){
+ var keys = [], key;
+ for(key in iterated)keys.push(key);
+ $.set(this, ITER, {o: iterated, a: keys, i: 0});
+}
+$iter.create(Enumerate, 'Object', function(){
+ var iter = this[ITER]
+ , keys = iter.a
+ , key;
+ do {
+ if(iter.i >= keys.length)return step(1);
+ } while(!((key = keys[iter.i++]) in iter.o));
+ return step(0, key);
+});
+
+function wrap(fn){
+ return function(it){
+ assertObject(it);
+ try {
+ fn.apply(undefined, arguments);
+ return true;
+ } catch(e){
+ return false;
+ }
+ };
+}
+
+function reflectGet(target, propertyKey/*, receiver*/){
+ var receiver = arguments.length < 3 ? target : arguments[2]
+ , desc = getDesc(assertObject(target), propertyKey), proto;
+ if(desc)return $.has(desc, 'value')
+ ? desc.value
+ : desc.get === undefined
+ ? undefined
+ : desc.get.call(receiver);
+ return isObject(proto = getProto(target))
+ ? reflectGet(proto, propertyKey, receiver)
+ : undefined;
+}
+function reflectSet(target, propertyKey, V/*, receiver*/){
+ var receiver = arguments.length < 4 ? target : arguments[3]
+ , ownDesc = getDesc(assertObject(target), propertyKey)
+ , existingDescriptor, proto;
+ if(!ownDesc){
+ if(isObject(proto = getProto(target))){
+ return reflectSet(proto, propertyKey, V, receiver);
+ }
+ ownDesc = $.desc(0);
+ }
+ if($.has(ownDesc, 'value')){
+ if(ownDesc.writable === false || !isObject(receiver))return false;
+ existingDescriptor = getDesc(receiver, propertyKey) || $.desc(0);
+ existingDescriptor.value = V;
+ setDesc(receiver, propertyKey, existingDescriptor);
+ return true;
+ }
+ return ownDesc.set === undefined ? false : (ownDesc.set.call(receiver, V), true);
+}
+
+var reflect = {
+ // 26.1.1 Reflect.apply(target, thisArgument, argumentsList)
+ apply: require('./$.ctx')(Function.call, apply, 3),
+ // 26.1.2 Reflect.construct(target, argumentsList [, newTarget])
+ construct: function(target, argumentsList /*, newTarget*/){
+ var proto = assert.fn(arguments.length < 3 ? target : arguments[2]).prototype
+ , instance = $.create(isObject(proto) ? proto : Object.prototype)
+ , result = apply.call(target, instance, argumentsList);
+ return isObject(result) ? result : instance;
+ },
+ // 26.1.3 Reflect.defineProperty(target, propertyKey, attributes)
+ defineProperty: wrap(setDesc),
+ // 26.1.4 Reflect.deleteProperty(target, propertyKey)
+ deleteProperty: function(target, propertyKey){
+ var desc = getDesc(assertObject(target), propertyKey);
+ return desc && !desc.configurable ? false : delete target[propertyKey];
+ },
+ // 26.1.5 Reflect.enumerate(target)
+ enumerate: function(target){
+ return new Enumerate(assertObject(target));
+ },
+ // 26.1.6 Reflect.get(target, propertyKey [, receiver])
+ get: reflectGet,
+ // 26.1.7 Reflect.getOwnPropertyDescriptor(target, propertyKey)
+ getOwnPropertyDescriptor: function(target, propertyKey){
+ return getDesc(assertObject(target), propertyKey);
+ },
+ // 26.1.8 Reflect.getPrototypeOf(target)
+ getPrototypeOf: function(target){
+ return getProto(assertObject(target));
+ },
+ // 26.1.9 Reflect.has(target, propertyKey)
+ has: function(target, propertyKey){
+ return propertyKey in target;
+ },
+ // 26.1.10 Reflect.isExtensible(target)
+ isExtensible: function(target){
+ return !!isExtensible(assertObject(target));
+ },
+ // 26.1.11 Reflect.ownKeys(target)
+ ownKeys: require('./$.own-keys'),
+ // 26.1.12 Reflect.preventExtensions(target)
+ preventExtensions: wrap(Object.preventExtensions || $.it),
+ // 26.1.13 Reflect.set(target, propertyKey, V [, receiver])
+ set: reflectSet
+};
+// 26.1.14 Reflect.setPrototypeOf(target, proto)
+if(setProto)reflect.setPrototypeOf = function(target, proto){
+ setProto(assertObject(target), proto);
+ return true;
+};
+
+$def($def.G, {Reflect: {}});
+$def($def.S, 'Reflect', reflect);
+},{"./$":16,"./$.assert":5,"./$.ctx":11,"./$.def":12,"./$.iter":15,"./$.own-keys":18,"./$.set-proto":20,"./$.uid":24}],47:[function(require,module,exports){
+var $ = require('./$')
+ , cof = require('./$.cof')
+ , RegExp = $.g.RegExp
+ , Base = RegExp
+ , proto = RegExp.prototype;
+if($.FW && $.DESC){
+ // RegExp allows a regex with flags as the pattern
+ if(!function(){try{ return RegExp(/a/g, 'i') == '/a/i'; }catch(e){ /* empty */ }}()){
+ RegExp = function RegExp(pattern, flags){
+ return new Base(cof(pattern) == 'RegExp' && flags !== undefined
+ ? pattern.source : pattern, flags);
+ };
+ $.each.call($.getNames(Base), function(key){
+ key in RegExp || $.setDesc(RegExp, key, {
+ configurable: true,
+ get: function(){ return Base[key]; },
+ set: function(it){ Base[key] = it; }
+ });
+ });
+ proto.constructor = RegExp;
+ RegExp.prototype = proto;
+ $.hide($.g, 'RegExp', RegExp);
+ }
+ // 21.2.5.3 get RegExp.prototype.flags()
+ if(/./g.flags != 'g')$.setDesc(proto, 'flags', {
+ configurable: true,
+ get: require('./$.replacer')(/^.*\/(\w*)$/, '$1')
+ });
+}
+require('./$.species')(RegExp);
+},{"./$":16,"./$.cof":7,"./$.replacer":19,"./$.species":21}],48:[function(require,module,exports){
+'use strict';
+var strong = require('./$.collection-strong');
+
+// 23.2 Set Objects
+require('./$.collection')('Set', {
+ // 23.2.3.1 Set.prototype.add(value)
+ add: function(value){
+ return strong.def(this, value = value === 0 ? 0 : value, value);
+ }
+}, strong);
+},{"./$.collection":10,"./$.collection-strong":8}],49:[function(require,module,exports){
+var $def = require('./$.def');
+$def($def.P, 'String', {
+ // 21.1.3.3 String.prototype.codePointAt(pos)
+ codePointAt: require('./$.string-at')(false)
+});
+},{"./$.def":12,"./$.string-at":22}],50:[function(require,module,exports){
+'use strict';
+var $ = require('./$')
+ , cof = require('./$.cof')
+ , $def = require('./$.def')
+ , toLength = $.toLength;
+
+$def($def.P, 'String', {
+ // 21.1.3.6 String.prototype.endsWith(searchString [, endPosition])
+ endsWith: function(searchString /*, endPosition = @length */){
+ if(cof(searchString) == 'RegExp')throw TypeError();
+ var that = String($.assertDefined(this))
+ , endPosition = arguments[1]
+ , len = toLength(that.length)
+ , end = endPosition === undefined ? len : Math.min(toLength(endPosition), len);
+ searchString += '';
+ return that.slice(end - searchString.length, end) === searchString;
+ }
+});
+},{"./$":16,"./$.cof":7,"./$.def":12}],51:[function(require,module,exports){
+var $def = require('./$.def')
+ , toIndex = require('./$').toIndex
+ , fromCharCode = String.fromCharCode;
+
+$def($def.S, 'String', {
+ // 21.1.2.2 String.fromCodePoint(...codePoints)
+ fromCodePoint: function(x){ // eslint-disable-line no-unused-vars
+ var res = []
+ , len = arguments.length
+ , i = 0
+ , code;
+ while(len > i){
+ code = +arguments[i++];
+ if(toIndex(code, 0x10ffff) !== code)throw RangeError(code + ' is not a valid code point');
+ res.push(code < 0x10000
+ ? fromCharCode(code)
+ : fromCharCode(((code -= 0x10000) >> 10) + 0xd800, code % 0x400 + 0xdc00)
+ );
+ } return res.join('');
+ }
+});
+},{"./$":16,"./$.def":12}],52:[function(require,module,exports){
+'use strict';
+var $ = require('./$')
+ , cof = require('./$.cof')
+ , $def = require('./$.def');
+
+$def($def.P, 'String', {
+ // 21.1.3.7 String.prototype.includes(searchString, position = 0)
+ includes: function(searchString /*, position = 0 */){
+ if(cof(searchString) == 'RegExp')throw TypeError();
+ return !!~String($.assertDefined(this)).indexOf(searchString, arguments[1]);
+ }
+});
+},{"./$":16,"./$.cof":7,"./$.def":12}],53:[function(require,module,exports){
+var set = require('./$').set
+ , at = require('./$.string-at')(true)
+ , ITER = require('./$.uid').safe('iter')
+ , $iter = require('./$.iter')
+ , step = $iter.step;
+
+// 21.1.3.27 String.prototype[@@iterator]()
+$iter.std(String, 'String', function(iterated){
+ set(this, ITER, {o: String(iterated), i: 0});
+// 21.1.5.2.1 %StringIteratorPrototype%.next()
+}, function(){
+ var iter = this[ITER]
+ , O = iter.o
+ , index = iter.i
+ , point;
+ if(index >= O.length)return step(1);
+ point = at.call(O, index);
+ iter.i += point.length;
+ return step(0, point);
+});
+},{"./$":16,"./$.iter":15,"./$.string-at":22,"./$.uid":24}],54:[function(require,module,exports){
+var $ = require('./$')
+ , $def = require('./$.def');
+
+$def($def.S, 'String', {
+ // 21.1.2.4 String.raw(callSite, ...substitutions)
+ raw: function(callSite){
+ var raw = $.toObject(callSite.raw)
+ , len = $.toLength(raw.length)
+ , sln = arguments.length
+ , res = []
+ , i = 0;
+ while(len > i){
+ res.push(String(raw[i++]));
+ if(i < sln)res.push(String(arguments[i]));
+ } return res.join('');
+ }
+});
+},{"./$":16,"./$.def":12}],55:[function(require,module,exports){
+'use strict';
+var $ = require('./$')
+ , $def = require('./$.def');
+
+$def($def.P, 'String', {
+ // 21.1.3.13 String.prototype.repeat(count)
+ repeat: function(count){
+ var str = String($.assertDefined(this))
+ , res = ''
+ , n = $.toInteger(count);
+ if(n < 0 || n == Infinity)throw RangeError("Count can't be negative");
+ for(;n > 0; (n >>>= 1) && (str += str))if(n & 1)res += str;
+ return res;
+ }
+});
+},{"./$":16,"./$.def":12}],56:[function(require,module,exports){
+'use strict';
+var $ = require('./$')
+ , cof = require('./$.cof')
+ , $def = require('./$.def');
+
+$def($def.P, 'String', {
+ // 21.1.3.18 String.prototype.startsWith(searchString [, position ])
+ startsWith: function(searchString /*, position = 0 */){
+ if(cof(searchString) == 'RegExp')throw TypeError();
+ var that = String($.assertDefined(this))
+ , index = $.toLength(Math.min(arguments[1], that.length));
+ searchString += '';
+ return that.slice(index, index + searchString.length) === searchString;
+ }
+});
+},{"./$":16,"./$.cof":7,"./$.def":12}],57:[function(require,module,exports){
+'use strict';
+// ECMAScript 6 symbols shim
+var $ = require('./$')
+ , setTag = require('./$.cof').set
+ , uid = require('./$.uid')
+ , $def = require('./$.def')
+ , keyOf = require('./$.keyof')
+ , has = $.has
+ , hide = $.hide
+ , getNames = $.getNames
+ , toObject = $.toObject
+ , Symbol = $.g.Symbol
+ , Base = Symbol
+ , setter = false
+ , TAG = uid.safe('tag')
+ , SymbolRegistry = {}
+ , AllSymbols = {};
+
+function wrap(tag){
+ var sym = AllSymbols[tag] = $.set($.create(Symbol.prototype), TAG, tag);
+ $.DESC && setter && $.setDesc(Object.prototype, tag, {
+ configurable: true,
+ set: function(value){
+ hide(this, tag, value);
+ }
+ });
+ return sym;
+}
+
+// 19.4.1.1 Symbol([description])
+if(!$.isFunction(Symbol)){
+ Symbol = function(description){
+ if(this instanceof Symbol)throw TypeError('Symbol is not a constructor');
+ return wrap(uid(description));
+ };
+ hide(Symbol.prototype, 'toString', function(){
+ return this[TAG];
+ });
+}
+$def($def.G + $def.W, {Symbol: Symbol});
+
+var symbolStatics = {
+ // 19.4.2.1 Symbol.for(key)
+ 'for': function(key){
+ return has(SymbolRegistry, key += '')
+ ? SymbolRegistry[key]
+ : SymbolRegistry[key] = Symbol(key);
+ },
+ // 19.4.2.5 Symbol.keyFor(sym)
+ keyFor: function(key){
+ return keyOf(SymbolRegistry, key);
+ },
+ pure: uid.safe,
+ set: $.set,
+ useSetter: function(){ setter = true; },
+ useSimple: function(){ setter = false; }
+};
+// 19.4.2.2 Symbol.hasInstance
+// 19.4.2.3 Symbol.isConcatSpreadable
+// 19.4.2.4 Symbol.iterator
+// 19.4.2.6 Symbol.match
+// 19.4.2.8 Symbol.replace
+// 19.4.2.9 Symbol.search
+// 19.4.2.10 Symbol.species
+// 19.4.2.11 Symbol.split
+// 19.4.2.12 Symbol.toPrimitive
+// 19.4.2.13 Symbol.toStringTag
+// 19.4.2.14 Symbol.unscopables
+$.each.call((
+ 'hasInstance,isConcatSpreadable,iterator,match,replace,search,' +
+ 'species,split,toPrimitive,toStringTag,unscopables'
+ ).split(','), function(it){
+ var sym = require('./$.wks')(it);
+ symbolStatics[it] = Symbol === Base ? sym : wrap(sym);
+ }
+);
+
+setter = true;
+
+$def($def.S, 'Symbol', symbolStatics);
+
+$def($def.S + $def.F * (Symbol != Base), 'Object', {
+ // 19.1.2.7 Object.getOwnPropertyNames(O)
+ getOwnPropertyNames: function(it){
+ var names = getNames(toObject(it)), result = [], key, i = 0;
+ while(names.length > i)has(AllSymbols, key = names[i++]) || result.push(key);
+ return result;
+ },
+ // 19.1.2.8 Object.getOwnPropertySymbols(O)
+ getOwnPropertySymbols: function(it){
+ var names = getNames(toObject(it)), result = [], key, i = 0;
+ while(names.length > i)has(AllSymbols, key = names[i++]) && result.push(AllSymbols[key]);
+ return result;
+ }
+});
+
+setTag(Symbol, 'Symbol');
+// 20.2.1.9 Math[@@toStringTag]
+setTag(Math, 'Math', true);
+// 24.3.3 JSON[@@toStringTag]
+setTag($.g.JSON, 'JSON', true);
+},{"./$":16,"./$.cof":7,"./$.def":12,"./$.keyof":17,"./$.uid":24,"./$.wks":26}],58:[function(require,module,exports){
+'use strict';
+var $ = require('./$')
+ , weak = require('./$.collection-weak')
+ , leakStore = weak.leakStore
+ , ID = weak.ID
+ , WEAK = weak.WEAK
+ , has = $.has
+ , isObject = $.isObject
+ , isFrozen = Object.isFrozen || $.core.Object.isFrozen
+ , tmp = {};
+
+// 23.3 WeakMap Objects
+var WeakMap = require('./$.collection')('WeakMap', {
+ // 23.3.3.3 WeakMap.prototype.get(key)
+ get: function(key){
+ if(isObject(key)){
+ if(isFrozen(key))return leakStore(this).get(key);
+ if(has(key, WEAK))return key[WEAK][this[ID]];
+ }
+ },
+ // 23.3.3.5 WeakMap.prototype.set(key, value)
+ set: function(key, value){
+ return weak.def(this, key, value);
+ }
+}, weak, true, true);
+
+// IE11 WeakMap frozen keys fix
+if($.FW && new WeakMap().set((Object.freeze || Object)(tmp), 7).get(tmp) != 7){
+ $.each.call(['delete', 'has', 'get', 'set'], function(key){
+ var method = WeakMap.prototype[key];
+ WeakMap.prototype[key] = function(a, b){
+ // store frozen objects on leaky map
+ if(isObject(a) && isFrozen(a)){
+ var result = leakStore(this)[key](a, b);
+ return key == 'set' ? this : result;
+ // store all the rest on native weakmap
+ } return method.call(this, a, b);
+ };
+ });
+}
+},{"./$":16,"./$.collection":10,"./$.collection-weak":9}],59:[function(require,module,exports){
+'use strict';
+var weak = require('./$.collection-weak');
+
+// 23.4 WeakSet Objects
+require('./$.collection')('WeakSet', {
+ // 23.4.3.1 WeakSet.prototype.add(value)
+ add: function(value){
+ return weak.def(this, value, true);
+ }
+}, weak, false, true);
+},{"./$.collection":10,"./$.collection-weak":9}],60:[function(require,module,exports){
+(function (global){
+/**
+ * Copyright (c) 2014, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * https://raw.github.com/facebook/regenerator/master/LICENSE file. An
+ * additional grant of patent rights can be found in the PATENTS file in
+ * the same directory.
+ */
+
+!(function(global) {
+ "use strict";
+
+ var hasOwn = Object.prototype.hasOwnProperty;
+ var undefined; // More compressible than void 0.
+ var iteratorSymbol =
+ typeof Symbol === "function" && Symbol.iterator || "@@iterator";
+
+ var inModule = typeof module === "object";
+ var runtime = global.regeneratorRuntime;
+ if (runtime) {
+ if (inModule) {
+ // If regeneratorRuntime is defined globally and we're in a module,
+ // make the exports object identical to regeneratorRuntime.
+ module.exports = runtime;
+ }
+ // Don't bother evaluating the rest of this file if the runtime was
+ // already defined globally.
+ return;
+ }
+
+ // Define the runtime globally (as expected by generated code) as either
+ // module.exports (if we're in a module) or a new, empty object.
+ runtime = global.regeneratorRuntime = inModule ? module.exports : {};
+
+ function wrap(innerFn, outerFn, self, tryLocsList) {
+ return new Generator(innerFn, outerFn, self || null, tryLocsList || []);
+ }
+ runtime.wrap = wrap;
+
+ // Try/catch helper to minimize deoptimizations. Returns a completion
+ // record like context.tryEntries[i].completion. This interface could
+ // have been (and was previously) designed to take a closure to be
+ // invoked without arguments, but in all the cases we care about we
+ // already have an existing method we want to call, so there's no need
+ // to create a new function object. We can even get away with assuming
+ // the method takes exactly one argument, since that happens to be true
+ // in every case, so we don't have to touch the arguments object. The
+ // only additional allocation required is the completion record, which
+ // has a stable shape and so hopefully should be cheap to allocate.
+ function tryCatch(fn, obj, arg) {
+ try {
+ return { type: "normal", arg: fn.call(obj, arg) };
+ } catch (err) {
+ return { type: "throw", arg: err };
+ }
+ }
+
+ var GenStateSuspendedStart = "suspendedStart";
+ var GenStateSuspendedYield = "suspendedYield";
+ var GenStateExecuting = "executing";
+ var GenStateCompleted = "completed";
+
+ // Returning this object from the innerFn has the same effect as
+ // breaking out of the dispatch switch statement.
+ var ContinueSentinel = {};
+
+ // Dummy constructor functions that we use as the .constructor and
+ // .constructor.prototype properties for functions that return Generator
+ // objects. For full spec compliance, you may wish to configure your
+ // minifier not to mangle the names of these two functions.
+ function GeneratorFunction() {}
+ function GeneratorFunctionPrototype() {}
+
+ var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype;
+ GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype;
+ GeneratorFunctionPrototype.constructor = GeneratorFunction;
+ GeneratorFunction.displayName = "GeneratorFunction";
+
+ runtime.isGeneratorFunction = function(genFun) {
+ var ctor = typeof genFun === "function" && genFun.constructor;
+ return ctor
+ ? ctor === GeneratorFunction ||
+ // For the native GeneratorFunction constructor, the best we can
+ // do is to check its .name property.
+ (ctor.displayName || ctor.name) === "GeneratorFunction"
+ : false;
+ };
+
+ runtime.mark = function(genFun) {
+ genFun.__proto__ = GeneratorFunctionPrototype;
+ genFun.prototype = Object.create(Gp);
+ return genFun;
+ };
+
+ runtime.async = function(innerFn, outerFn, self, tryLocsList) {
+ return new Promise(function(resolve, reject) {
+ var generator = wrap(innerFn, outerFn, self, tryLocsList);
+ var callNext = step.bind(generator.next);
+ var callThrow = step.bind(generator["throw"]);
+
+ function step(arg) {
+ var record = tryCatch(this, null, arg);
+ if (record.type === "throw") {
+ reject(record.arg);
+ return;
+ }
+
+ var info = record.arg;
+ if (info.done) {
+ resolve(info.value);
+ } else {
+ Promise.resolve(info.value).then(callNext, callThrow);
+ }
+ }
+
+ callNext();
+ });
+ };
+
+ function Generator(innerFn, outerFn, self, tryLocsList) {
+ var generator = outerFn ? Object.create(outerFn.prototype) : this;
+ var context = new Context(tryLocsList);
+ var state = GenStateSuspendedStart;
+
+ function invoke(method, arg) {
+ if (state === GenStateExecuting) {
+ throw new Error("Generator is already running");
+ }
+
+ if (state === GenStateCompleted) {
+ // Be forgiving, per 25.3.3.3.3 of the spec:
+ // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume
+ return doneResult();
+ }
+
+ while (true) {
+ var delegate = context.delegate;
+ if (delegate) {
+ var record = tryCatch(
+ delegate.iterator[method],
+ delegate.iterator,
+ arg
+ );
+
+ if (record.type === "throw") {
+ context.delegate = null;
+
+ // Like returning generator.throw(uncaught), but without the
+ // overhead of an extra function call.
+ method = "throw";
+ arg = record.arg;
+
+ continue;
+ }
+
+ // Delegate generator ran and handled its own exceptions so
+ // regardless of what the method was, we continue as if it is
+ // "next" with an undefined arg.
+ method = "next";
+ arg = undefined;
+
+ var info = record.arg;
+ if (info.done) {
+ context[delegate.resultName] = info.value;
+ context.next = delegate.nextLoc;
+ } else {
+ state = GenStateSuspendedYield;
+ return info;
+ }
+
+ context.delegate = null;
+ }
+
+ if (method === "next") {
+ if (state === GenStateSuspendedStart &&
+ typeof arg !== "undefined") {
+ // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume
+ throw new TypeError(
+ "attempt to send " + JSON.stringify(arg) + " to newborn generator"
+ );
+ }
+
+ if (state === GenStateSuspendedYield) {
+ context.sent = arg;
+ } else {
+ delete context.sent;
+ }
+
+ } else if (method === "throw") {
+ if (state === GenStateSuspendedStart) {
+ state = GenStateCompleted;
+ throw arg;
+ }
+
+ if (context.dispatchException(arg)) {
+ // If the dispatched exception was caught by a catch block,
+ // then let that catch block handle the exception normally.
+ method = "next";
+ arg = undefined;
+ }
+
+ } else if (method === "return") {
+ context.abrupt("return", arg);
+ }
+
+ state = GenStateExecuting;
+
+ var record = tryCatch(innerFn, self, context);
+ if (record.type === "normal") {
+ // If an exception is thrown from innerFn, we leave state ===
+ // GenStateExecuting and loop back for another invocation.
+ state = context.done
+ ? GenStateCompleted
+ : GenStateSuspendedYield;
+
+ var info = {
+ value: record.arg,
+ done: context.done
+ };
+
+ if (record.arg === ContinueSentinel) {
+ if (context.delegate && method === "next") {
+ // Deliberately forget the last sent value so that we don't
+ // accidentally pass it on to the delegate.
+ arg = undefined;
+ }
+ } else {
+ return info;
+ }
+
+ } else if (record.type === "throw") {
+ state = GenStateCompleted;
+
+ if (method === "next") {
+ context.dispatchException(record.arg);
+ } else {
+ arg = record.arg;
+ }
+ }
+ }
+ }
+
+ generator.next = invoke.bind(generator, "next");
+ generator["throw"] = invoke.bind(generator, "throw");
+ generator["return"] = invoke.bind(generator, "return");
+
+ return generator;
+ }
+
+ Gp[iteratorSymbol] = function() {
+ return this;
+ };
+
+ Gp.toString = function() {
+ return "[object Generator]";
+ };
+
+ function pushTryEntry(locs) {
+ var entry = { tryLoc: locs[0] };
+
+ if (1 in locs) {
+ entry.catchLoc = locs[1];
+ }
+
+ if (2 in locs) {
+ entry.finallyLoc = locs[2];
+ entry.afterLoc = locs[3];
+ }
+
+ this.tryEntries.push(entry);
+ }
+
+ function resetTryEntry(entry) {
+ var record = entry.completion || {};
+ record.type = "normal";
+ delete record.arg;
+ entry.completion = record;
+ }
+
+ function Context(tryLocsList) {
+ // The root entry object (effectively a try statement without a catch
+ // or a finally block) gives us a place to store values thrown from
+ // locations where there is no enclosing try statement.
+ this.tryEntries = [{ tryLoc: "root" }];
+ tryLocsList.forEach(pushTryEntry, this);
+ this.reset();
+ }
+
+ runtime.keys = function(object) {
+ var keys = [];
+ for (var key in object) {
+ keys.push(key);
+ }
+ keys.reverse();
+
+ // Rather than returning an object with a next method, we keep
+ // things simple and return the next function itself.
+ return function next() {
+ while (keys.length) {
+ var key = keys.pop();
+ if (key in object) {
+ next.value = key;
+ next.done = false;
+ return next;
+ }
+ }
+
+ // To avoid creating an additional object, we just hang the .value
+ // and .done properties off the next function object itself. This
+ // also ensures that the minifier will not anonymize the function.
+ next.done = true;
+ return next;
+ };
+ };
+
+ function values(iterable) {
+ if (iterable) {
+ var iteratorMethod = iterable[iteratorSymbol];
+ if (iteratorMethod) {
+ return iteratorMethod.call(iterable);
+ }
+
+ if (typeof iterable.next === "function") {
+ return iterable;
+ }
+
+ if (!isNaN(iterable.length)) {
+ var i = -1, next = function next() {
+ while (++i < iterable.length) {
+ if (hasOwn.call(iterable, i)) {
+ next.value = iterable[i];
+ next.done = false;
+ return next;
+ }
+ }
+
+ next.value = undefined;
+ next.done = true;
+
+ return next;
+ };
+
+ return next.next = next;
+ }
+ }
+
+ // Return an iterator with no values.
+ return { next: doneResult };
+ }
+ runtime.values = values;
+
+ function doneResult() {
+ return { value: undefined, done: true };
+ }
+
+ Context.prototype = {
+ constructor: Context,
+
+ reset: function() {
+ this.prev = 0;
+ this.next = 0;
+ this.sent = undefined;
+ this.done = false;
+ this.delegate = null;
+
+ this.tryEntries.forEach(resetTryEntry);
+
+ // Pre-initialize at least 20 temporary variables to enable hidden
+ // class optimizations for simple generators.
+ for (var tempIndex = 0, tempName;
+ hasOwn.call(this, tempName = "t" + tempIndex) || tempIndex < 20;
+ ++tempIndex) {
+ this[tempName] = null;
+ }
+ },
+
+ stop: function() {
+ this.done = true;
+
+ var rootEntry = this.tryEntries[0];
+ var rootRecord = rootEntry.completion;
+ if (rootRecord.type === "throw") {
+ throw rootRecord.arg;
+ }
+
+ return this.rval;
+ },
+
+ dispatchException: function(exception) {
+ if (this.done) {
+ throw exception;
+ }
+
+ var context = this;
+ function handle(loc, caught) {
+ record.type = "throw";
+ record.arg = exception;
+ context.next = loc;
+ return !!caught;
+ }
+
+ for (var i = this.tryEntries.length - 1; i >= 0; --i) {
+ var entry = this.tryEntries[i];
+ var record = entry.completion;
+
+ if (entry.tryLoc === "root") {
+ // Exception thrown outside of any try block that could handle
+ // it, so set the completion value of the entire function to
+ // throw the exception.
+ return handle("end");
+ }
+
+ if (entry.tryLoc <= this.prev) {
+ var hasCatch = hasOwn.call(entry, "catchLoc");
+ var hasFinally = hasOwn.call(entry, "finallyLoc");
+
+ if (hasCatch && hasFinally) {
+ if (this.prev < entry.catchLoc) {
+ return handle(entry.catchLoc, true);
+ } else if (this.prev < entry.finallyLoc) {
+ return handle(entry.finallyLoc);
+ }
+
+ } else if (hasCatch) {
+ if (this.prev < entry.catchLoc) {
+ return handle(entry.catchLoc, true);
+ }
+
+ } else if (hasFinally) {
+ if (this.prev < entry.finallyLoc) {
+ return handle(entry.finallyLoc);
+ }
+
+ } else {
+ throw new Error("try statement without catch or finally");
+ }
+ }
+ }
+ },
+
+ abrupt: function(type, arg) {
+ for (var i = this.tryEntries.length - 1; i >= 0; --i) {
+ var entry = this.tryEntries[i];
+ if (entry.tryLoc <= this.prev &&
+ hasOwn.call(entry, "finallyLoc") &&
+ this.prev < entry.finallyLoc) {
+ var finallyEntry = entry;
+ break;
+ }
+ }
+
+ if (finallyEntry &&
+ (type === "break" ||
+ type === "continue") &&
+ finallyEntry.tryLoc <= arg &&
+ arg < finallyEntry.finallyLoc) {
+ // Ignore the finally entry if control is not jumping to a
+ // location outside the try/catch block.
+ finallyEntry = null;
+ }
+
+ var record = finallyEntry ? finallyEntry.completion : {};
+ record.type = type;
+ record.arg = arg;
+
+ if (finallyEntry) {
+ this.next = finallyEntry.finallyLoc;
+ } else {
+ this.complete(record);
+ }
+
+ return ContinueSentinel;
+ },
+
+ complete: function(record, afterLoc) {
+ if (record.type === "throw") {
+ throw record.arg;
+ }
+
+ if (record.type === "break" ||
+ record.type === "continue") {
+ this.next = record.arg;
+ } else if (record.type === "return") {
+ this.rval = record.arg;
+ this.next = "end";
+ } else if (record.type === "normal" && afterLoc) {
+ this.next = afterLoc;
+ }
+
+ return ContinueSentinel;
+ },
+
+ finish: function(finallyLoc) {
+ for (var i = this.tryEntries.length - 1; i >= 0; --i) {
+ var entry = this.tryEntries[i];
+ if (entry.finallyLoc === finallyLoc) {
+ return this.complete(entry.completion, entry.afterLoc);
+ }
+ }
+ },
+
+ "catch": function(tryLoc) {
+ for (var i = this.tryEntries.length - 1; i >= 0; --i) {
+ var entry = this.tryEntries[i];
+ if (entry.tryLoc === tryLoc) {
+ var record = entry.completion;
+ if (record.type === "throw") {
+ var thrown = record.arg;
+ resetTryEntry(entry);
+ }
+ return thrown;
+ }
+ }
+
+ // The context.catch method must only be called with a location
+ // argument that corresponds to a known catch block.
+ throw new Error("illegal catch attempt");
+ },
+
+ delegateYield: function(iterable, resultName, nextLoc) {
+ this.delegate = {
+ iterator: values(iterable),
+ resultName: resultName,
+ nextLoc: nextLoc
+ };
+
+ return ContinueSentinel;
+ }
+ };
+})(
+ // Among the various tricks for obtaining a reference to the global
+ // object, this seems to be the most reliable technique that does not
+ // use indirect eval (which violates Content Security Policy).
+ typeof global === "object" ? global :
+ typeof window === "object" ? window : this
+);
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{}]},{},[1]);
diff --git a/web/static/js/babel-es6-polyfill.min.js b/web/static/js/babel-es6-polyfill.min.js
new file mode 100644
index 000000000..794476133
--- /dev/null
+++ b/web/static/js/babel-es6-polyfill.min.js
@@ -0,0 +1,2 @@
+!function t(e,n,r){function o(s,c){if(!n[s]){if(!e[s]){var u="function"==typeof require&&require;if(!c&&u)return u(s,!0);if(i)return i(s,!0);var a=new Error("Cannot find module '"+s+"'");throw a.code="MODULE_NOT_FOUND",a}var f=n[s]={exports:{}};e[s][0].call(f.exports,function(t){var n=e[s][1][t];return o(n?n:t)},f,f.exports,t,e,n,r)}return n[s].exports}for(var i="function"==typeof require&&require,s=0;s<r.length;s++)o(r[s]);return o}({1:[function(t){(function(e){"use strict";if(e._babelPolyfill)throw new Error("only one instance of babel/polyfill is allowed");e._babelPolyfill=!0,t("./es6-shim"),t("regenerator-babel/runtime")}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{"./es6-shim":2,"regenerator-babel/runtime":60}],2:[function(t,e){t("core-js/es6"),e.exports=t("core-js/modules/$").core},{"core-js/es6":3,"core-js/modules/$":16}],3:[function(t,e){t("../modules/es6.symbol"),t("../modules/es6.object.assign"),t("../modules/es6.object.is"),t("../modules/es6.object.set-prototype-of"),t("../modules/es6.object.to-string"),t("../modules/es6.object.statics-accept-primitives"),t("../modules/es6.function.name"),t("../modules/es6.number.constructor"),t("../modules/es6.number.statics"),t("../modules/es6.math"),t("../modules/es6.string.from-code-point"),t("../modules/es6.string.raw"),t("../modules/es6.string.iterator"),t("../modules/es6.string.code-point-at"),t("../modules/es6.string.ends-with"),t("../modules/es6.string.includes"),t("../modules/es6.string.repeat"),t("../modules/es6.string.starts-with"),t("../modules/es6.array.from"),t("../modules/es6.array.of"),t("../modules/es6.array.species"),t("../modules/es6.array.iterator"),t("../modules/es6.array.copy-within"),t("../modules/es6.array.fill"),t("../modules/es6.array.find"),t("../modules/es6.array.find-index"),t("../modules/es6.regexp"),t("../modules/es6.promise"),t("../modules/es6.map"),t("../modules/es6.set"),t("../modules/es6.weak-map"),t("../modules/es6.weak-set"),t("../modules/es6.reflect"),e.exports=t("../modules/$").core},{"../modules/$":16,"../modules/es6.array.copy-within":27,"../modules/es6.array.fill":28,"../modules/es6.array.find":30,"../modules/es6.array.find-index":29,"../modules/es6.array.from":31,"../modules/es6.array.iterator":32,"../modules/es6.array.of":33,"../modules/es6.array.species":34,"../modules/es6.function.name":35,"../modules/es6.map":36,"../modules/es6.math":37,"../modules/es6.number.constructor":38,"../modules/es6.number.statics":39,"../modules/es6.object.assign":40,"../modules/es6.object.is":41,"../modules/es6.object.set-prototype-of":42,"../modules/es6.object.statics-accept-primitives":43,"../modules/es6.object.to-string":44,"../modules/es6.promise":45,"../modules/es6.reflect":46,"../modules/es6.regexp":47,"../modules/es6.set":48,"../modules/es6.string.code-point-at":49,"../modules/es6.string.ends-with":50,"../modules/es6.string.from-code-point":51,"../modules/es6.string.includes":52,"../modules/es6.string.iterator":53,"../modules/es6.string.raw":54,"../modules/es6.string.repeat":55,"../modules/es6.string.starts-with":56,"../modules/es6.symbol":57,"../modules/es6.weak-map":58,"../modules/es6.weak-set":59}],4:[function(t,e){"use strict";var n=t("./$"),r=t("./$.ctx");e.exports=function(t){var e=1==t,o=2==t,i=3==t,s=4==t,c=6==t,u=5==t||c;return function(a){for(var f,l,h=Object(n.assertDefined(this)),d=n.ES5Object(h),p=r(a,arguments[1],3),g=n.toLength(d.length),$=0,v=e?Array(g):o?[]:void 0;g>$;$++)if((u||$ in d)&&(f=d[$],l=p(f,$,h),t))if(e)v[$]=l;else if(l)switch(t){case 3:return!0;case 5:return f;case 6:return $;case 2:v.push(f)}else if(s)return!1;return c?-1:i||s?s:v}}},{"./$":16,"./$.ctx":11}],5:[function(t,e){function n(t,e,n){if(!t)throw TypeError(n?e+n:e)}var r=t("./$");n.def=r.assertDefined,n.fn=function(t){if(!r.isFunction(t))throw TypeError(t+" is not a function!");return t},n.obj=function(t){if(!r.isObject(t))throw TypeError(t+" is not an object!");return t},n.inst=function(t,e,n){if(!(t instanceof e))throw TypeError(n+": use the 'new' operator!");return t},e.exports=n},{"./$":16}],6:[function(t,e){var n=t("./$");e.exports=Object.assign||function(t){for(var e=Object(n.assertDefined(t)),r=arguments.length,o=1;r>o;)for(var i,s=n.ES5Object(arguments[o++]),c=n.getKeys(s),u=c.length,a=0;u>a;)e[i=c[a++]]=s[i];return e}},{"./$":16}],7:[function(t,e){function n(t){return i.call(t).slice(8,-1)}var r=t("./$"),o=t("./$.wks")("toStringTag"),i={}.toString;n.classof=function(t){var e,r;return void 0==t?void 0===t?"Undefined":"Null":"string"==typeof(r=(e=Object(t))[o])?r:n(e)},n.set=function(t,e,n){t&&!r.has(t=n?t:t.prototype,o)&&r.hide(t,o,e)},e.exports=n},{"./$":16,"./$.wks":26}],8:[function(t,e){"use strict";function n(t,e){if(!l(t))return("string"==typeof t?"S":"P")+t;if(p(t))return"F";if(!a(t,g)){if(!e)return"E";h(t,g,++w)}return"O"+t[g]}function r(t,e){var r,o=n(e);if("F"!=o)return t[$][o];for(r=t[y];r;r=r.n)if(r.k==e)return r}var o=t("./$"),i=t("./$.ctx"),s=t("./$.uid").safe,c=t("./$.assert"),u=t("./$.iter"),a=o.has,f=o.set,l=o.isObject,h=o.hide,d=u.step,p=Object.isFrozen||o.core.Object.isFrozen,g=s("id"),$=s("O1"),v=s("last"),y=s("first"),m=s("iter"),b=o.DESC?s("size"):"size",w=0;e.exports={getConstructor:function(t,e,n){function s(r){var i=c.inst(this,s,t);f(i,$,o.create(null)),f(i,b,0),f(i,v,void 0),f(i,y,void 0),void 0!=r&&u.forOf(r,e,i[n],i)}return o.mix(s.prototype,{clear:function(){for(var t=this,e=t[$],n=t[y];n;n=n.n)n.r=!0,n.p&&(n.p=n.p.n=void 0),delete e[n.i];t[y]=t[v]=void 0,t[b]=0},"delete":function(t){var e=this,n=r(e,t);if(n){var o=n.n,i=n.p;delete e[$][n.i],n.r=!0,i&&(i.n=o),o&&(o.p=i),e[y]==n&&(e[y]=o),e[v]==n&&(e[v]=i),e[b]--}return!!n},forEach:function(t){for(var e,n=i(t,arguments[1],3);e=e?e.n:this[y];)for(n(e.v,e.k,this);e&&e.r;)e=e.p},has:function(t){return!!r(this,t)}}),o.DESC&&o.setDesc(s.prototype,"size",{get:function(){return c.def(this[b])}}),s},def:function(t,e,o){var i,s,c=r(t,e);return c?c.v=o:(t[v]=c={i:s=n(e,!0),k:e,v:o,p:i=t[v],n:void 0,r:!1},t[y]||(t[y]=c),i&&(i.n=c),t[b]++,"F"!=s&&(t[$][s]=c)),t},getEntry:r,getIterConstructor:function(){return function(t,e){f(this,m,{o:t,k:e})}},next:function(){for(var t=this[m],e=t.k,n=t.l;n&&n.r;)n=n.p;return t.o&&(t.l=n=n?n.n:t.o[y])?"key"==e?d(0,n.k):"value"==e?d(0,n.v):d(0,[n.k,n.v]):(t.o=void 0,d(1))}}},{"./$":16,"./$.assert":5,"./$.ctx":11,"./$.iter":15,"./$.uid":24}],9:[function(t,e){"use strict";function n(t,e){return v.call(t.array,function(t){return t[0]===e})}function r(t){return t[g]||f(t,g,{array:[],get:function(t){var e=n(this,t);return e?e[1]:void 0},has:function(t){return!!n(this,t)},set:function(t,e){var r=n(this,t);r?r[1]=e:this.array.push([t,e])},"delete":function(t){var e=y.call(this.array,function(e){return e[0]===t});return~e&&this.array.splice(e,1),!!~e}})[g]}var o=t("./$"),i=t("./$.uid").safe,s=t("./$.assert"),c=t("./$.iter").forOf,u=o.has,a=o.isObject,f=o.hide,l=Object.isFrozen||o.core.Object.isFrozen,h=0,d=i("id"),p=i("weak"),g=i("leak"),$=t("./$.array-methods"),v=$(5),y=$(6);e.exports={getConstructor:function(t,e,n){function i(r){o.set(s.inst(this,i,t),d,h++),void 0!=r&&c(r,e,this[n],this)}return o.mix(i.prototype,{"delete":function(t){return a(t)?l(t)?r(this)["delete"](t):u(t,p)&&u(t[p],this[d])&&delete t[p][this[d]]:!1},has:function(t){return a(t)?l(t)?r(this).has(t):u(t,p)&&u(t[p],this[d]):!1}}),i},def:function(t,e,n){return l(s.obj(e))?r(t).set(e,n):(u(e,p)||f(e,p,{}),e[p][t[d]]=n),t},leakStore:r,WEAK:p,ID:d}},{"./$":16,"./$.array-methods":4,"./$.assert":5,"./$.iter":15,"./$.uid":24}],10:[function(t,e){"use strict";var n=t("./$"),r=t("./$.def"),o=t("./$.iter"),i=t("./$.assert").inst;e.exports=function(e,s,c,u,a){function f(t,e){var r=p[t];n.FW&&(p[t]=function(t,n){var o=r.call(this,0===t?0:t,n);return e?this:o})}var l=n.g[e],h=l,d=u?"set":"add",p=h&&h.prototype,g={};if(n.isFunction(h)&&(a||!o.BUGGY&&p.forEach&&p.entries)){var $,v=new h,y=v[d](a?{}:-0,1);(o.fail(function(t){new h(t)})||o.DANGER_CLOSING)&&(h=function(t){i(this,h,e);var n=new l;return void 0!=t&&o.forOf(t,u,n[d],n),n},h.prototype=p,n.FW&&(p.constructor=h)),a||v.forEach(function(t,e){$=1/e===-1/0}),$&&(f("delete"),f("has"),u&&f("get")),($||y!==v)&&f(d,!0)}else h=c.getConstructor(e,u,d),n.mix(h.prototype,s);return t("./$.cof").set(h,e),t("./$.species")(h),g[e]=h,r(r.G+r.W+r.F*(h!=l),g),a||o.std(h,e,c.getIterConstructor(),c.next,u?"key+value":"value",!u,!0),h}},{"./$":16,"./$.assert":5,"./$.cof":7,"./$.def":12,"./$.iter":15,"./$.species":21}],11:[function(t,e){var n=t("./$.assert").fn;e.exports=function(t,e,r){if(n(t),~r&&void 0===e)return t;switch(r){case 1:return function(n){return t.call(e,n)};case 2:return function(n,r){return t.call(e,n,r)};case 3:return function(n,r,o){return t.call(e,n,r,o)}}return function(){return t.apply(e,arguments)}}},{"./$.assert":5}],12:[function(t,e){function n(t,e){return function(){return t.apply(e,arguments)}}function r(t,e,u){var a,f,l,h,d=t&r.G,p=d?i:t&r.S?i[e]:(i[e]||{}).prototype,g=d?s:s[e]||(s[e]={});d&&(u=e);for(a in u)f=!(t&r.F)&&p&&a in p,l=(f?p:u)[a],h=t&r.B&&f?n(l,i):t&r.P&&c(l)?n(Function.call,l):l,p&&!f&&(d?p[a]=l:delete p[a]&&o.hide(p,a,l)),g[a]!=l&&o.hide(g,a,h)}var o=t("./$"),i=o.g,s=o.core,c=o.isFunction;i.core=s,r.F=1,r.G=2,r.S=4,r.P=8,r.B=16,r.W=32,e.exports=r},{"./$":16}],13:[function(t,e){e.exports=function(t){return t.FW=!0,t.path=t.g,t}},{}],14:[function(t,e){e.exports=function(t,e,n){var r=void 0===n;switch(e.length){case 0:return r?t():t.call(n);case 1:return r?t(e[0]):t.call(n,e[0]);case 2:return r?t(e[0],e[1]):t.call(n,e[0],e[1]);case 3:return r?t(e[0],e[1],e[2]):t.call(n,e[0],e[1],e[2]);case 4:return r?t(e[0],e[1],e[2],e[3]):t.call(n,e[0],e[1],e[2],e[3]);case 5:return r?t(e[0],e[1],e[2],e[3],e[4]):t.call(n,e[0],e[1],e[2],e[3],e[4])}return t.apply(n,e)}},{}],15:[function(t,e){"use strict";function n(t,e){c.hide(t,h,e),d in[]&&c.hide(t,d,e)}function r(t,e,r,o){var i=t.prototype,s=i[h]||i[d]||o&&i[o]||r;if(c.FW&&n(i,s),s!==r){var u=c.getProto(s.call(new t));a.set(u,e+" Iterator",!0),c.FW&&c.has(i,d)&&n(u,c.that)}return p[e]=s,p[e+" Iterator"]=c.that,s}function o(t){var e=c.g.Symbol,n=t[e&&e.iterator||d],r=n||t[h]||p[a.classof(t)];return l(r.call(t))}function i(t){var e=t["return"];void 0!==e&&l(e.call(t))}function s(t,e,n,r){try{return r?e(l(n)[0],n[1]):e(n)}catch(o){throw i(t),o}}var c=t("./$"),u=t("./$.ctx"),a=t("./$.cof"),f=t("./$.def"),l=t("./$.assert").obj,h=t("./$.wks")("iterator"),d="@@iterator",p={},g={},$="keys"in[]&&!("next"in[].keys());n(g,c.that);var v=!0;!function(){try{var t=[1].keys();t["return"]=function(){v=!1},Array.from(t,function(){throw 2})}catch(e){}}();var y=e.exports={BUGGY:$,DANGER_CLOSING:v,fail:function(t){var e=!0;try{var n=[[{},1]],r=n[h](),o=r.next;r.next=function(){return e=!1,o.call(this)},n[h]=function(){return r},t(n)}catch(i){}return e},Iterators:p,prototype:g,step:function(t,e){return{value:e,done:!!t}},stepCall:s,close:i,is:function(t){var e=Object(t),n=c.g.Symbol,r=n&&n.iterator||d;return r in e||h in e||c.has(p,a.classof(e))},get:o,set:n,create:function(t,e,n,r){t.prototype=c.create(r||y.prototype,{next:c.desc(1,n)}),a.set(t,e+" Iterator")},define:r,std:function(t,e,n,o,i,s,u){function a(t){return function(){return new n(this,t)}}y.create(n,e,o);var l,h,d=a("key+value"),p=a("value"),g=t.prototype;if("value"==i?p=r(t,e,p,"values"):d=r(t,e,d,"entries"),i&&(l={entries:d,keys:s?p:a("key"),values:p},f(f.P+f.F*$,e,l),u))for(h in l)h in g||c.hide(g,h,l[h])},forOf:function(t,e,n,r){for(var c,a=o(t),f=u(n,r,e?2:1);!(c=a.next()).done;)if(s(a,f,c.value,e)===!1)return i(a)}}},{"./$":16,"./$.assert":5,"./$.cof":7,"./$.ctx":11,"./$.def":12,"./$.wks":26}],16:[function(t,e){"use strict";function n(t){return isNaN(t=+t)?0:(t>0?p:d)(t)}function r(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}function o(t,e,n){return t[e]=n,t}function i(t){return v?function(e,n,o){return m.setDesc(e,n,r(t,o))}:o}function s(t){return null!==t&&("object"==typeof t||"function"==typeof t)}function c(t){return"function"==typeof t}function u(t){if(void 0==t)throw TypeError("Can't call method on "+t);return t}var a="undefined"!=typeof self?self:Function("return this")(),f={},l=Object.defineProperty,h={}.hasOwnProperty,d=Math.ceil,p=Math.floor,g=Math.max,$=Math.min,v=!!function(){try{return 2==l({},"a",{get:function(){return 2}}).a}catch(t){}}(),y=i(1),m=e.exports=t("./$.fw")({g:a,core:f,html:a.document&&document.documentElement,isObject:s,isFunction:c,it:function(t){return t},that:function(){return this},toInteger:n,toLength:function(t){return t>0?$(n(t),9007199254740991):0},toIndex:function(t,e){return t=n(t),0>t?g(t+e,0):$(t,e)},has:function(t,e){return h.call(t,e)},create:Object.create,getProto:Object.getPrototypeOf,DESC:v,desc:r,getDesc:Object.getOwnPropertyDescriptor,setDesc:l,getKeys:Object.keys,getNames:Object.getOwnPropertyNames,getSymbols:Object.getOwnPropertySymbols,assertDefined:u,ES5Object:Object,toObject:function(t){return m.ES5Object(u(t))},hide:y,def:i(0),set:a.Symbol?o:y,mix:function(t,e){for(var n in e)y(t,n,e[n]);return t},each:[].forEach});"undefined"!=typeof __e&&(__e=f),"undefined"!=typeof __g&&(__g=a)},{"./$.fw":13}],17:[function(t,e){var n=t("./$");e.exports=function(t,e){for(var r,o=n.toObject(t),i=n.getKeys(o),s=i.length,c=0;s>c;)if(o[r=i[c++]]===e)return r}},{"./$":16}],18:[function(t,e){var n=t("./$"),r=t("./$.assert").obj;e.exports=function(t){return r(t),n.getSymbols?n.getNames(t).concat(n.getSymbols(t)):n.getNames(t)}},{"./$":16,"./$.assert":5}],19:[function(t,e){"use strict";e.exports=function(t,e,n){var r=e===Object(e)?function(t){return e[t]}:e;return function(e){return String(n?e:this).replace(t,r)}}},{}],20:[function(t,e){var n=t("./$"),r=t("./$.assert");e.exports=Object.setPrototypeOf||("__proto__"in{}?function(e,o){try{o=t("./$.ctx")(Function.call,n.getDesc(Object.prototype,"__proto__").set,2),o({},[])}catch(i){e=!0}return function(t,i){return r.obj(t),r(null===i||n.isObject(i),i,": can't set as prototype!"),e?t.__proto__=i:o(t,i),t}}():void 0)},{"./$":16,"./$.assert":5,"./$.ctx":11}],21:[function(t,e){var n=t("./$");e.exports=function(e){n.DESC&&n.FW&&n.setDesc(e,t("./$.wks")("species"),{configurable:!0,get:n.that})}},{"./$":16,"./$.wks":26}],22:[function(t,e){"use strict";var n=t("./$");e.exports=function(t){return function(e){var r,o,i=String(n.assertDefined(this)),s=n.toInteger(e),c=i.length;return 0>s||s>=c?t?"":void 0:(r=i.charCodeAt(s),55296>r||r>56319||s+1===c||(o=i.charCodeAt(s+1))<56320||o>57343?t?i.charAt(s):r:t?i.slice(s,s+2):(r-55296<<10)+(o-56320)+65536)}}},{"./$":16}],23:[function(t,e){"use strict";function n(){var t=+this;if(c.has(m,t)){var e=m[t];delete m[t],e()}}function r(t){n.call(t.data)}var o,i,s,c=t("./$"),u=t("./$.ctx"),a=t("./$.cof"),f=t("./$.invoke"),l=c.g,h=c.isFunction,d=l.setImmediate,p=l.clearImmediate,g=l.postMessage,$=l.addEventListener,v=l.MessageChannel,y=0,m={},b="onreadystatechange";h(d)&&h(p)||(d=function(t){for(var e=[],n=1;arguments.length>n;)e.push(arguments[n++]);return m[++y]=function(){f(h(t)?t:Function(t),e)},o(y),y},p=function(t){delete m[t]},"process"==a(l.process)?o=function(t){l.process.nextTick(u(n,t,1))}:$&&h(g)&&!c.g.importScripts?(o=function(t){g(t,"*")},$("message",r,!1)):h(v)?(i=new v,s=i.port2,i.port1.onmessage=r,o=u(s.postMessage,s,1)):o=c.g.document&&b in document.createElement("script")?function(t){c.html.appendChild(document.createElement("script"))[b]=function(){c.html.removeChild(this),n.call(t)}}:function(t){setTimeout(u(n,t,1),0)}),e.exports={set:d,clear:p}},{"./$":16,"./$.cof":7,"./$.ctx":11,"./$.invoke":14}],24:[function(t,e){function n(t){return"Symbol("+t+")_"+(++r+Math.random()).toString(36)}var r=0;n.safe=t("./$").g.Symbol||n,e.exports=n},{"./$":16}],25:[function(t,e){var n=t("./$"),r=t("./$.wks")("unscopables");!n.FW||r in[]||n.hide(Array.prototype,r,{}),e.exports=function(t){n.FW&&([][r][t]=!0)}},{"./$":16,"./$.wks":26}],26:[function(t,e){var n=t("./$").g,r={};e.exports=function(e){return r[e]||(r[e]=n.Symbol&&n.Symbol[e]||t("./$.uid").safe("Symbol."+e))}},{"./$":16,"./$.uid":24}],27:[function(t){"use strict";var e=t("./$"),n=t("./$.def"),r=e.toIndex;n(n.P,"Array",{copyWithin:function(t,n){var o=Object(e.assertDefined(this)),i=e.toLength(o.length),s=r(t,i),c=r(n,i),u=arguments[2],a=void 0===u?i:r(u,i),f=Math.min(a-c,i-s),l=1;for(s>c&&c+f>s&&(l=-1,c=c+f-1,s=s+f-1);f-->0;)c in o?o[s]=o[c]:delete o[s],s+=l,c+=l;return o}}),t("./$.unscope")("copyWithin")},{"./$":16,"./$.def":12,"./$.unscope":25}],28:[function(t){"use strict";var e=t("./$"),n=t("./$.def"),r=e.toIndex;n(n.P,"Array",{fill:function(t){for(var n=Object(e.assertDefined(this)),o=e.toLength(n.length),i=r(arguments[1],o),s=arguments[2],c=void 0===s?o:r(s,o);c>i;)n[i++]=t;return n}}),t("./$.unscope")("fill")},{"./$":16,"./$.def":12,"./$.unscope":25}],29:[function(t){var e=t("./$.def");e(e.P,"Array",{findIndex:t("./$.array-methods")(6)}),t("./$.unscope")("findIndex")},{"./$.array-methods":4,"./$.def":12,"./$.unscope":25}],30:[function(t){var e=t("./$.def");e(e.P,"Array",{find:t("./$.array-methods")(5)}),t("./$.unscope")("find")},{"./$.array-methods":4,"./$.def":12,"./$.unscope":25}],31:[function(t){var e=t("./$"),n=t("./$.ctx"),r=t("./$.def"),o=t("./$.iter"),i=o.stepCall;r(r.S+r.F*o.DANGER_CLOSING,"Array",{from:function(t){var r,s,c,u,a=Object(e.assertDefined(t)),f=arguments[1],l=void 0!==f,h=l?n(f,arguments[2],2):void 0,d=0;if(o.is(a))for(u=o.get(a),s=new("function"==typeof this?this:Array);!(c=u.next()).done;d++)s[d]=l?i(u,h,[c.value,d],!0):c.value;else for(s=new("function"==typeof this?this:Array)(r=e.toLength(a.length));r>d;d++)s[d]=l?h(a[d],d):a[d];return s.length=d,s}})},{"./$":16,"./$.ctx":11,"./$.def":12,"./$.iter":15}],32:[function(t){var e=t("./$"),n=t("./$.unscope"),r=t("./$.uid").safe("iter"),o=t("./$.iter"),i=o.step,s=o.Iterators;o.std(Array,"Array",function(t,n){e.set(this,r,{o:e.toObject(t),i:0,k:n})},function(){var t=this[r],e=t.o,n=t.k,o=t.i++;return!e||o>=e.length?(t.o=void 0,i(1)):"key"==n?i(0,o):"value"==n?i(0,e[o]):i(0,[o,e[o]])},"value"),s.Arguments=s.Array,n("keys"),n("values"),n("entries")},{"./$":16,"./$.iter":15,"./$.uid":24,"./$.unscope":25}],33:[function(t){var e=t("./$.def");e(e.S,"Array",{of:function(){for(var t=0,e=arguments.length,n=new("function"==typeof this?this:Array)(e);e>t;)n[t]=arguments[t++];return n.length=e,n}})},{"./$.def":12}],34:[function(t){t("./$.species")(Array)},{"./$.species":21}],35:[function(t){"use strict";var e=t("./$"),n="name",r=e.setDesc,o=Function.prototype;n in o||e.FW&&e.DESC&&r(o,n,{configurable:!0,get:function(){var t=String(this).match(/^\s*function ([^ (]*)/),o=t?t[1]:"";return e.has(this,n)||r(this,n,e.desc(5,o)),o},set:function(t){e.has(this,n)||r(this,n,e.desc(0,t))}})},{"./$":16}],36:[function(t){"use strict";var e=t("./$.collection-strong");t("./$.collection")("Map",{get:function(t){var n=e.getEntry(this,t);return n&&n.v},set:function(t,n){return e.def(this,0===t?0:t,n)}},e,!0)},{"./$.collection":10,"./$.collection-strong":8}],37:[function(t){function e(t){return isFinite(t=+t)&&0!=t?0>t?-e(-t):a(t+f(t*t+1)):t}function n(t){return 0==(t=+t)?t:t>-1e-6&&1e-6>t?t+t*t/2:u(t)-1}var r=1/0,o=t("./$.def"),i=Math.E,s=Math.pow,c=Math.abs,u=Math.exp,a=Math.log,f=Math.sqrt,l=Math.ceil,h=Math.floor,d=Math.sign||function(t){return 0==(t=+t)||t!=t?t:0>t?-1:1};o(o.S,"Math",{acosh:function(t){return(t=+t)<1?0/0:isFinite(t)?a(t/i+f(t+1)*f(t-1)/i)+1:t},asinh:e,atanh:function(t){return 0==(t=+t)?t:a((1+t)/(1-t))/2},cbrt:function(t){return d(t=+t)*s(c(t),1/3)},clz32:function(t){return(t>>>=0)?32-t.toString(2).length:32},cosh:function(t){return(u(t=+t)+u(-t))/2},expm1:n,fround:function(t){return new Float32Array([t])[0]},hypot:function(){for(var t,e=0,n=arguments.length,o=n,i=Array(n),c=-r;n--;){if(t=i[n]=+arguments[n],t==r||t==-r)return r;t>c&&(c=t)}for(c=t||1;o--;)e+=s(i[o]/c,2);return c*f(e)},imul:function(t,e){var n=65535,r=+t,o=+e,i=n&r,s=n&o;return 0|i*s+((n&r>>>16)*s+i*(n&o>>>16)<<16>>>0)},log1p:function(t){return(t=+t)>-1e-8&&1e-8>t?t-t*t/2:a(1+t)},log10:function(t){return a(t)/Math.LN10},log2:function(t){return a(t)/Math.LN2},sign:d,sinh:function(t){return c(t=+t)<1?(n(t)-n(-t))/2:(u(t-1)-u(-t-1))*(i/2)},tanh:function(t){var e=n(t=+t),o=n(-t);return e==r?1:o==r?-1:(e-o)/(u(t)+u(-t))},trunc:function(t){return(t>0?h:l)(t)}})},{"./$.def":12}],38:[function(t){"use strict";function e(t){var e,n;if(i(e=t.valueOf)&&!o(n=e.call(t)))return n;if(i(e=t.toString)&&!o(n=e.call(t)))return n;throw TypeError("Can't convert object to number")}function n(t){if(o(t)&&(t=e(t)),"string"==typeof t&&t.length>2&&48==t.charCodeAt(0)){var n=!1;switch(t.charCodeAt(1)){case 66:case 98:n=!0;case 79:case 111:return parseInt(t.slice(2),n?2:8)}}return+t}var r=t("./$"),o=r.isObject,i=r.isFunction,s="Number",c=r.g[s],u=c,a=c.prototype;!r.FW||c("0o1")&&c("0b1")||(c=function f(t){return this instanceof f?new u(n(t)):n(t)},r.each.call(r.DESC?r.getNames(u):"MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,EPSILON,isFinite,isInteger,isNaN,isSafeInteger,MAX_SAFE_INTEGER,MIN_SAFE_INTEGER,parseFloat,parseInt,isInteger".split(","),function(t){r.has(u,t)&&!r.has(c,t)&&r.setDesc(c,t,r.getDesc(u,t))}),c.prototype=a,a.constructor=c,r.hide(r.g,s,c))},{"./$":16}],39:[function(t){function e(t){return!n.isObject(t)&&isFinite(t)&&i(t)===t}var n=t("./$"),r=t("./$.def"),o=Math.abs,i=Math.floor,s=9007199254740991;r(r.S,"Number",{EPSILON:Math.pow(2,-52),isFinite:function(t){return"number"==typeof t&&isFinite(t)},isInteger:e,isNaN:function(t){return t!=t},isSafeInteger:function(t){return e(t)&&o(t)<=s},MAX_SAFE_INTEGER:s,MIN_SAFE_INTEGER:-s,parseFloat:parseFloat,parseInt:parseInt})},{"./$":16,"./$.def":12}],40:[function(t){var e=t("./$.def");e(e.S,"Object",{assign:t("./$.assign")})},{"./$.assign":6,"./$.def":12}],41:[function(t){var e=t("./$.def");e(e.S,"Object",{is:function(t,e){return t===e?0!==t||1/t===1/e:t!=t&&e!=e}})},{"./$.def":12}],42:[function(t){var e=t("./$.def");e(e.S,"Object",{setPrototypeOf:t("./$.set-proto")})},{"./$.def":12,"./$.set-proto":20}],43:[function(t){function e(t,e){var s=(n.core.Object||{})[t]||Object[t],c=0,u={};u[t]=1==e?function(t){return o(t)?s(t):t}:2==e?function(t){return o(t)?s(t):!0}:3==e?function(t){return o(t)?s(t):!1}:4==e?function(t,e){return s(i(t),e)}:5==e?function(t){return s(Object(n.assertDefined(t)))}:function(t){return s(i(t))};try{s("z")}catch(a){c=1}r(r.S+r.F*c,"Object",u)}var n=t("./$"),r=t("./$.def"),o=n.isObject,i=n.toObject;e("freeze",1),e("seal",1),e("preventExtensions",1),e("isFrozen",2),e("isSealed",2),e("isExtensible",3),e("getOwnPropertyDescriptor",4),e("getPrototypeOf",5),e("keys"),e("getOwnPropertyNames")},{"./$":16,"./$.def":12}],44:[function(t){"use strict";var e=t("./$"),n=t("./$.cof"),r={};r[t("./$.wks")("toStringTag")]="z",e.FW&&"z"!=n(r)&&e.hide(Object.prototype,"toString",function(){return"[object "+n.classof(this)+"]"})},{"./$":16,"./$.cof":7,"./$.wks":26}],45:[function(t){"use strict";function e(t){var e=w(t)[a];return void 0!=e?e:t}var n,r=t("./$"),o=t("./$.ctx"),i=t("./$.cof"),s=t("./$.def"),c=t("./$.assert"),u=t("./$.iter"),a=t("./$.wks")("species"),f=t("./$.uid").safe("record"),l=u.forOf,h="Promise",d=r.g,p=d.process,g=p&&p.nextTick||t("./$.task").set,$=d[h],v=$,y=r.isFunction,m=r.isObject,b=c.fn,w=c.obj;y($)&&y($.resolve)&&$.resolve(n=new $(function(){}))==n||function(){function t(t){var e;return m(t)&&(e=t.then),y(e)?e:!1}function e(t){var n,r=t[f],o=r.c,i=0;if(r.h)return!0;for(;o.length>i;)if(n=o[i++],n.fail||e(n.P))return!0}function n(n,r){var o=n.c;(r||o.length)&&g(function(){var s=n.p,c=n.v,u=1==n.s,a=0;if(r&&!e(s))setTimeout(function(){e(s)||("process"==i(p)?p.emit("unhandledRejection",c,s):d.console&&y(console.error)&&console.error("Unhandled promise rejection",c))},1e3);else for(;o.length>a;)!function(e){var r,o,i=u?e.ok:e.fail;try{i?(u||(n.h=!0),r=i===!0?c:i(c),r===e.P?e.rej(TypeError(h+"-chain cycle")):(o=t(r))?o.call(r,e.res,e.rej):e.res(r)):e.rej(c)}catch(s){e.rej(s)}}(o[a++]);o.length=0})}function s(t){var e=this;e.d||(e.d=!0,e=e.r||e,e.v=t,e.s=2,n(e,!0))}function u(e){var r,i,c=this;if(!c.d){c.d=!0,c=c.r||c;try{(r=t(e))?(i={r:c,d:!1},r.call(e,o(u,i,1),o(s,i,1))):(c.v=e,c.s=1,n(c))}catch(a){s.call(i||{r:c,d:!1},a)}}}$=function(t){b(t);var e={p:c.inst(this,$,h),c:[],s:0,d:!1,v:void 0,h:!1};r.hide(this,f,e);try{t(o(u,e,1),o(s,e,1))}catch(n){s.call(e,n)}},r.mix($.prototype,{then:function(t,e){var r=w(w(this).constructor)[a],o={ok:y(t)?t:!0,fail:y(e)?e:!1},i=o.P=new(void 0!=r?r:$)(function(t,e){o.res=b(t),o.rej=b(e)}),s=this[f];return s.c.push(o),s.s&&n(s),i},"catch":function(t){return this.then(void 0,t)}})}(),s(s.G+s.W+s.F*($!=v),{Promise:$}),s(s.S,h,{reject:function(t){return new(e(this))(function(e,n){n(t)})},resolve:function(t){return m(t)&&f in t&&r.getProto(t)===this.prototype?t:new(e(this))(function(e){e(t)})}}),s(s.S+s.F*(u.fail(function(t){$.all(t)["catch"](function(){})})||u.DANGER_CLOSING),h,{all:function(t){var n=e(this),o=[];return new n(function(e,i){l(t,!1,o.push,o);var s=o.length,c=Array(s);s?r.each.call(o,function(t,r){n.resolve(t).then(function(t){c[r]=t,--s||e(c)},i)}):e(c)})},race:function(t){var n=e(this);return new n(function(e,r){l(t,!1,function(t){n.resolve(t).then(e,r)})})}}),i.set($,h),t("./$.species")($)},{"./$":16,"./$.assert":5,"./$.cof":7,"./$.ctx":11,"./$.def":12,"./$.iter":15,"./$.species":21,"./$.task":23,"./$.uid":24,"./$.wks":26}],46:[function(t){function e(t){var e,n=[];for(e in t)n.push(e);i.set(this,a,{o:t,a:n,i:0})}function n(t){return function(e){v(e);try{return t.apply(void 0,arguments),!0}catch(n){return!1}}}function r(t,e){var n,o=arguments.length<3?t:arguments[2],s=d(v(t),e);return s?i.has(s,"value")?s.value:void 0===s.get?void 0:s.get.call(o):h(n=g(t))?r(n,e,o):void 0}function o(t,e,n){var r,s,c=arguments.length<4?t:arguments[3],u=d(v(t),e);if(!u){if(h(s=g(t)))return o(s,e,n,c);u=i.desc(0)}return i.has(u,"value")?u.writable!==!1&&h(c)?(r=d(c,e)||i.desc(0),r.value=n,p(c,e,r),!0):!1:void 0===u.set?!1:(u.set.call(c,n),!0)}var i=t("./$"),s=t("./$.def"),c=t("./$.set-proto"),u=t("./$.iter"),a=t("./$.uid").safe("iter"),f=u.step,l=t("./$.assert"),h=i.isObject,d=i.getDesc,p=i.setDesc,g=i.getProto,$=Function.apply,v=l.obj,y=Object.isExtensible||i.it;u.create(e,"Object",function(){var t,e=this[a],n=e.a;do if(e.i>=n.length)return f(1);while(!((t=n[e.i++])in e.o));return f(0,t)});var m={apply:t("./$.ctx")(Function.call,$,3),construct:function(t,e){var n=l.fn(arguments.length<3?t:arguments[2]).prototype,r=i.create(h(n)?n:Object.prototype),o=$.call(t,r,e);return h(o)?o:r},defineProperty:n(p),deleteProperty:function(t,e){var n=d(v(t),e);return n&&!n.configurable?!1:delete t[e]},enumerate:function(t){return new e(v(t))},get:r,getOwnPropertyDescriptor:function(t,e){return d(v(t),e)},getPrototypeOf:function(t){return g(v(t))},has:function(t,e){return e in t},isExtensible:function(t){return!!y(v(t))},ownKeys:t("./$.own-keys"),preventExtensions:n(Object.preventExtensions||i.it),set:o};c&&(m.setPrototypeOf=function(t,e){return c(v(t),e),!0}),s(s.G,{Reflect:{}}),s(s.S,"Reflect",m)},{"./$":16,"./$.assert":5,"./$.ctx":11,"./$.def":12,"./$.iter":15,"./$.own-keys":18,"./$.set-proto":20,"./$.uid":24}],47:[function(t){var e=t("./$"),n=t("./$.cof"),r=e.g.RegExp,o=r,i=r.prototype;e.FW&&e.DESC&&(function(){try{return"/a/i"==r(/a/g,"i")}catch(t){}}()||(r=function(t,e){return new o("RegExp"==n(t)&&void 0!==e?t.source:t,e)},e.each.call(e.getNames(o),function(t){t in r||e.setDesc(r,t,{configurable:!0,get:function(){return o[t]},set:function(e){o[t]=e}})}),i.constructor=r,r.prototype=i,e.hide(e.g,"RegExp",r)),"g"!=/./g.flags&&e.setDesc(i,"flags",{configurable:!0,get:t("./$.replacer")(/^.*\/(\w*)$/,"$1")})),t("./$.species")(r)},{"./$":16,"./$.cof":7,"./$.replacer":19,"./$.species":21}],48:[function(t){"use strict";var e=t("./$.collection-strong");t("./$.collection")("Set",{add:function(t){return e.def(this,t=0===t?0:t,t)}},e)},{"./$.collection":10,"./$.collection-strong":8}],49:[function(t){var e=t("./$.def");e(e.P,"String",{codePointAt:t("./$.string-at")(!1)})},{"./$.def":12,"./$.string-at":22}],50:[function(t){"use strict";var e=t("./$"),n=t("./$.cof"),r=t("./$.def"),o=e.toLength;r(r.P,"String",{endsWith:function(t){if("RegExp"==n(t))throw TypeError();var r=String(e.assertDefined(this)),i=arguments[1],s=o(r.length),c=void 0===i?s:Math.min(o(i),s);return t+="",r.slice(c-t.length,c)===t}})},{"./$":16,"./$.cof":7,"./$.def":12}],51:[function(t){var e=t("./$.def"),n=t("./$").toIndex,r=String.fromCharCode;e(e.S,"String",{fromCodePoint:function(){for(var t,e=[],o=arguments.length,i=0;o>i;){if(t=+arguments[i++],n(t,1114111)!==t)throw RangeError(t+" is not a valid code point");e.push(65536>t?r(t):r(((t-=65536)>>10)+55296,t%1024+56320))}return e.join("")}})},{"./$":16,"./$.def":12}],52:[function(t){"use strict";var e=t("./$"),n=t("./$.cof"),r=t("./$.def");r(r.P,"String",{includes:function(t){if("RegExp"==n(t))throw TypeError();return!!~String(e.assertDefined(this)).indexOf(t,arguments[1])}})},{"./$":16,"./$.cof":7,"./$.def":12}],53:[function(t){var e=t("./$").set,n=t("./$.string-at")(!0),r=t("./$.uid").safe("iter"),o=t("./$.iter"),i=o.step;o.std(String,"String",function(t){e(this,r,{o:String(t),i:0})},function(){var t,e=this[r],o=e.o,s=e.i;return s>=o.length?i(1):(t=n.call(o,s),e.i+=t.length,i(0,t))})},{"./$":16,"./$.iter":15,"./$.string-at":22,"./$.uid":24}],54:[function(t){var e=t("./$"),n=t("./$.def");n(n.S,"String",{raw:function(t){for(var n=e.toObject(t.raw),r=e.toLength(n.length),o=arguments.length,i=[],s=0;r>s;)i.push(String(n[s++])),o>s&&i.push(String(arguments[s]));return i.join("")}})},{"./$":16,"./$.def":12}],55:[function(t){"use strict";var e=t("./$"),n=t("./$.def");n(n.P,"String",{repeat:function(t){var n=String(e.assertDefined(this)),r="",o=e.toInteger(t);if(0>o||1/0==o)throw RangeError("Count can't be negative");for(;o>0;(o>>>=1)&&(n+=n))1&o&&(r+=n);return r}})},{"./$":16,"./$.def":12}],56:[function(t){"use strict";var e=t("./$"),n=t("./$.cof"),r=t("./$.def");r(r.P,"String",{startsWith:function(t){if("RegExp"==n(t))throw TypeError();var r=String(e.assertDefined(this)),o=e.toLength(Math.min(arguments[1],r.length));return t+="",r.slice(o,o+t.length)===t}})},{"./$":16,"./$.cof":7,"./$.def":12}],57:[function(t){"use strict";function e(t){var e=$[t]=n.set(n.create(l.prototype),p,t);return n.DESC&&d&&n.setDesc(Object.prototype,t,{configurable:!0,set:function(e){u(this,t,e)}}),e}var n=t("./$"),r=t("./$.cof").set,o=t("./$.uid"),i=t("./$.def"),s=t("./$.keyof"),c=n.has,u=n.hide,a=n.getNames,f=n.toObject,l=n.g.Symbol,h=l,d=!1,p=o.safe("tag"),g={},$={};n.isFunction(l)||(l=function(t){if(this instanceof l)throw TypeError("Symbol is not a constructor");return e(o(t))},u(l.prototype,"toString",function(){return this[p]})),i(i.G+i.W,{Symbol:l});var v={"for":function(t){return c(g,t+="")?g[t]:g[t]=l(t)},keyFor:function(t){return s(g,t)},pure:o.safe,set:n.set,useSetter:function(){d=!0},useSimple:function(){d=!1}};n.each.call("hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables".split(","),function(n){var r=t("./$.wks")(n);v[n]=l===h?r:e(r)}),d=!0,i(i.S,"Symbol",v),i(i.S+i.F*(l!=h),"Object",{getOwnPropertyNames:function(t){for(var e,n=a(f(t)),r=[],o=0;n.length>o;)c($,e=n[o++])||r.push(e);return r},getOwnPropertySymbols:function(t){for(var e,n=a(f(t)),r=[],o=0;n.length>o;)c($,e=n[o++])&&r.push($[e]);return r}}),r(l,"Symbol"),r(Math,"Math",!0),r(n.g.JSON,"JSON",!0)},{"./$":16,"./$.cof":7,"./$.def":12,"./$.keyof":17,"./$.uid":24,"./$.wks":26}],58:[function(t){"use strict";var e=t("./$"),n=t("./$.collection-weak"),r=n.leakStore,o=n.ID,i=n.WEAK,s=e.has,c=e.isObject,u=Object.isFrozen||e.core.Object.isFrozen,a={},f=t("./$.collection")("WeakMap",{get:function(t){if(c(t)){if(u(t))return r(this).get(t);if(s(t,i))return t[i][this[o]]}},set:function(t,e){return n.def(this,t,e)}},n,!0,!0);e.FW&&7!=(new f).set((Object.freeze||Object)(a),7).get(a)&&e.each.call(["delete","has","get","set"],function(t){var e=f.prototype[t];
+f.prototype[t]=function(n,o){if(c(n)&&u(n)){var i=r(this)[t](n,o);return"set"==t?this:i}return e.call(this,n,o)}})},{"./$":16,"./$.collection":10,"./$.collection-weak":9}],59:[function(t){"use strict";var e=t("./$.collection-weak");t("./$.collection")("WeakSet",{add:function(t){return e.def(this,t,!0)}},e,!1,!0)},{"./$.collection":10,"./$.collection-weak":9}],60:[function(t,e){(function(t){!function(t){"use strict";function n(t,e,n,r){return new s(t,e,n||null,r||[])}function r(t,e,n){try{return{type:"normal",arg:t.call(e,n)}}catch(r){return{type:"throw",arg:r}}}function o(){}function i(){}function s(t,e,n,o){function i(e,o){if(u===m)throw new Error("Generator is already running");if(u===b)return l();for(;;){var i=c.delegate;if(i){var s=r(i.iterator[e],i.iterator,o);if("throw"===s.type){c.delegate=null,e="throw",o=s.arg;continue}e="next",o=h;var a=s.arg;if(!a.done)return u=y,a;c[i.resultName]=a.value,c.next=i.nextLoc,c.delegate=null}if("next"===e){if(u===v&&"undefined"!=typeof o)throw new TypeError("attempt to send "+JSON.stringify(o)+" to newborn generator");u===y?c.sent=o:delete c.sent}else if("throw"===e){if(u===v)throw u=b,o;c.dispatchException(o)&&(e="next",o=h)}else"return"===e&&c.abrupt("return",o);u=m;var s=r(t,n,c);if("normal"===s.type){u=c.done?b:y;var a={value:s.arg,done:c.done};if(s.arg!==w)return a;c.delegate&&"next"===e&&(o=h)}else"throw"===s.type&&(u=b,"next"===e?c.dispatchException(s.arg):o=s.arg)}}var s=e?Object.create(e.prototype):this,c=new a(o),u=v;return s.next=i.bind(s,"next"),s["throw"]=i.bind(s,"throw"),s["return"]=i.bind(s,"return"),s}function c(t){var e={tryLoc:t[0]};1 in t&&(e.catchLoc=t[1]),2 in t&&(e.finallyLoc=t[2],e.afterLoc=t[3]),this.tryEntries.push(e)}function u(t){var e=t.completion||{};e.type="normal",delete e.arg,t.completion=e}function a(t){this.tryEntries=[{tryLoc:"root"}],t.forEach(c,this),this.reset()}function f(t){if(t){var e=t[p];if(e)return e.call(t);if("function"==typeof t.next)return t;if(!isNaN(t.length)){var n=-1,r=function o(){for(;++n<t.length;)if(d.call(t,n))return o.value=t[n],o.done=!1,o;return o.value=h,o.done=!0,o};return r.next=r}}return{next:l}}function l(){return{value:h,done:!0}}var h,d=Object.prototype.hasOwnProperty,p="function"==typeof Symbol&&Symbol.iterator||"@@iterator",g="object"==typeof e,$=t.regeneratorRuntime;if($)return void(g&&(e.exports=$));$=t.regeneratorRuntime=g?e.exports:{},$.wrap=n;var v="suspendedStart",y="suspendedYield",m="executing",b="completed",w={},x=i.prototype=s.prototype;o.prototype=x.constructor=i,i.constructor=o,o.displayName="GeneratorFunction",$.isGeneratorFunction=function(t){var e="function"==typeof t&&t.constructor;return e?e===o||"GeneratorFunction"===(e.displayName||e.name):!1},$.mark=function(t){return t.__proto__=i,t.prototype=Object.create(x),t},$.async=function(t,e,o,i){return new Promise(function(s,c){function u(t){var e=r(this,null,t);if("throw"===e.type)return void c(e.arg);var n=e.arg;n.done?s(n.value):Promise.resolve(n.value).then(f,l)}var a=n(t,e,o,i),f=u.bind(a.next),l=u.bind(a["throw"]);f()})},x[p]=function(){return this},x.toString=function(){return"[object Generator]"},$.keys=function(t){var e=[];for(var n in t)e.push(n);return e.reverse(),function r(){for(;e.length;){var n=e.pop();if(n in t)return r.value=n,r.done=!1,r}return r.done=!0,r}},$.values=f,a.prototype={constructor:a,reset:function(){this.prev=0,this.next=0,this.sent=h,this.done=!1,this.delegate=null,this.tryEntries.forEach(u);for(var t,e=0;d.call(this,t="t"+e)||20>e;++e)this[t]=null},stop:function(){this.done=!0;var t=this.tryEntries[0],e=t.completion;if("throw"===e.type)throw e.arg;return this.rval},dispatchException:function(t){function e(e,r){return i.type="throw",i.arg=t,n.next=e,!!r}if(this.done)throw t;for(var n=this,r=this.tryEntries.length-1;r>=0;--r){var o=this.tryEntries[r],i=o.completion;if("root"===o.tryLoc)return e("end");if(o.tryLoc<=this.prev){var s=d.call(o,"catchLoc"),c=d.call(o,"finallyLoc");if(s&&c){if(this.prev<o.catchLoc)return e(o.catchLoc,!0);if(this.prev<o.finallyLoc)return e(o.finallyLoc)}else if(s){if(this.prev<o.catchLoc)return e(o.catchLoc,!0)}else{if(!c)throw new Error("try statement without catch or finally");if(this.prev<o.finallyLoc)return e(o.finallyLoc)}}}},abrupt:function(t,e){for(var n=this.tryEntries.length-1;n>=0;--n){var r=this.tryEntries[n];if(r.tryLoc<=this.prev&&d.call(r,"finallyLoc")&&this.prev<r.finallyLoc){var o=r;break}}o&&("break"===t||"continue"===t)&&o.tryLoc<=e&&e<o.finallyLoc&&(o=null);var i=o?o.completion:{};return i.type=t,i.arg=e,o?this.next=o.finallyLoc:this.complete(i),w},complete:function(t,e){if("throw"===t.type)throw t.arg;return"break"===t.type||"continue"===t.type?this.next=t.arg:"return"===t.type?(this.rval=t.arg,this.next="end"):"normal"===t.type&&e&&(this.next=e),w},finish:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var n=this.tryEntries[e];if(n.finallyLoc===t)return this.complete(n.completion,n.afterLoc)}},"catch":function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var n=this.tryEntries[e];if(n.tryLoc===t){var r=n.completion;if("throw"===r.type){var o=r.arg;u(n)}return o}}throw new Error("illegal catch attempt")},delegateYield:function(t,e,n){return this.delegate={iterator:f(t),resultName:e,nextLoc:n},w}}}("object"==typeof t?t:"object"==typeof window?window:this)}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}]},{},[1]); \ No newline at end of file
diff --git a/web/templates/channel.html b/web/templates/channel.html
index 4b8318d43..63fe38587 100644
--- a/web/templates/channel.html
+++ b/web/templates/channel.html
@@ -5,24 +5,7 @@
{{template "head" . }}
<body>
<div id="error_bar"></div>
- <div class="container-fluid">
- <div class="sidebar--right" id="sidebar-right"></div>
- <div class="sidebar--menu" id="sidebar-menu"></div>
- <div class="sidebar--left" id="sidebar-left"></div>
- <div class="inner__wrap channel__wrap">
- <div class="row header">
- <div id="navbar"></div>
- </div>
- <div class="row main">
- <div id="file_upload_overlay"></div>
- <div id="app-content" class="app__content">
- <div id="channel-header"></div>
- <div id="post-list"></div>
- <div class="post-create__container" id="post-create"></div>
- </div>
- </div>
- </div>
- </div>
+ <div id="channel_view" class="channel-view"></div>
<div id="channel_loader"></div>
<div id="post_mention_tab"></div>
<div id="reply_mention_tab"></div>
diff --git a/web/templates/head.html b/web/templates/head.html
index fdc371af4..24f9862c0 100644
--- a/web/templates/head.html
+++ b/web/templates/head.html
@@ -37,6 +37,7 @@
<script src="/static/js/react-bootstrap-0.27.1.js"></script>
<script src="/static/js/perfect-scrollbar-0.6.7.jquery.min.js"></script>
<script src="/static/js/jquery-dragster/jquery.dragster.js"></script>
+ <script src="/static/js/babel-es6-polyfill.min.js"></script>
<style id="antiClickjack">body{display:none !important;}</style>
@@ -83,15 +84,14 @@
if (window.mm_config.SegmentDeveloperKey != null && window.mm_config.SegmentDeveloperKey !== "") {
!function(){var analytics=window.analytics=window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Segment snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","group","track","ready","alias","page","once","off","on"];analytics.factory=function(t){return function(){var e=Array.prototype.slice.call(arguments);e.unshift(t);analytics.push(e);return analytics}};for(var t=0;t<analytics.methods.length;t++){var e=analytics.methods[t];analytics[e]=analytics.factory(e)}analytics.load=function(t){var e=document.createElement("script");e.type="text/javascript";e.async=!0;e.src=("https:"===document.location.protocol?"https://":"http://")+"cdn.segment.com/analytics.js/v1/"+t+"/analytics.min.js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(e,n)};analytics.SNIPPET_VERSION="3.0.1";
analytics.load(window.mm_config.SegmentDeveloperKey);
- var user = window.UserStore.getCurrentUser(true);
- if (user) {
+ if (window.mm_user) {
analytics.identify(user.id, {
- name: user.nickname,
- email: user.email,
- createdAt: user.create_at,
- username: user.username,
- team_id: user.team_id,
- id: user.id
+ name: window.mm_user.nickname,
+ email: window.mm_user.email,
+ createdAt: window.mm_user.create_at,
+ username: window.mm_user.username,
+ team_id: window.mm_user.team_id,
+ id: window.mm_user.id
});
}
analytics.page();
diff --git a/web/web.go b/web/web.go
index 34f4df08f..51f6664b6 100644
--- a/web/web.go
+++ b/web/web.go
@@ -170,7 +170,7 @@ func root(c *api.Context, w http.ResponseWriter, r *http.Request) {
page.Props[team.Name] = team.DisplayName
}
- if len(teams) == 1 && *utils.Cfg.TeamSettings.EnableTeamListing {
+ if len(teams) == 1 && *utils.Cfg.TeamSettings.EnableTeamListing && !utils.Cfg.TeamSettings.EnableTeamCreation {
http.Redirect(w, r, c.GetSiteURL()+"/"+teams[0].Name, http.StatusTemporaryRedirect)
return
}