diff options
-rw-r--r-- | Godeps/Godeps.json | 2 | ||||
-rw-r--r-- | Godeps/_workspace/src/github.com/nfnt/resize/converter.go | 6 | ||||
-rw-r--r-- | Godeps/_workspace/src/github.com/nfnt/resize/resize.go | 4 | ||||
-rw-r--r-- | Godeps/_workspace/src/github.com/nfnt/resize/resize_test.go | 96 | ||||
-rw-r--r-- | Makefile | 54 | ||||
-rw-r--r-- | api/file.go | 4 | ||||
-rw-r--r-- | store/sql_channel_store.go | 2 | ||||
-rw-r--r-- | store/sql_post_store.go | 12 | ||||
-rw-r--r-- | store/sql_user_store.go | 2 | ||||
-rw-r--r-- | web/react/components/create_post.jsx | 2 | ||||
-rw-r--r-- | web/react/components/textbox.jsx | 37 | ||||
-rw-r--r-- | web/react/components/user_settings.jsx | 8 |
12 files changed, 147 insertions, 82 deletions
diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 8b30d0478..86ed9e0cf 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -100,7 +100,7 @@ }, { "ImportPath": "github.com/nfnt/resize", - "Rev": "f2d1b73023c55bdfb4fa2ed385d2bec95bce3692" + "Rev": "dc93e1b98c579d90ee2fa15c1fd6dac34f6e7899" }, { "ImportPath": "github.com/stretchr/objx", diff --git a/Godeps/_workspace/src/github.com/nfnt/resize/converter.go b/Godeps/_workspace/src/github.com/nfnt/resize/converter.go index 84bd284ba..b3dd06b8d 100644 --- a/Godeps/_workspace/src/github.com/nfnt/resize/converter.go +++ b/Godeps/_workspace/src/github.com/nfnt/resize/converter.go @@ -131,11 +131,11 @@ func resizeRGBA(in *image.RGBA, out *image.NRGBA, scale float64, coeffs []int16, // reverse alpha-premultiplication. if a != 0 { - r *= 0xffff + r *= 0xff r /= a - g *= 0xffff + g *= 0xff g /= a - b *= 0xffff + b *= 0xff b /= a } diff --git a/Godeps/_workspace/src/github.com/nfnt/resize/resize.go b/Godeps/_workspace/src/github.com/nfnt/resize/resize.go index 4d4ff6e3e..c1672432e 100644 --- a/Godeps/_workspace/src/github.com/nfnt/resize/resize.go +++ b/Godeps/_workspace/src/github.com/nfnt/resize/resize.go @@ -167,7 +167,7 @@ func Resize(width, height uint, img image.Image, interp InterpolationFunction) i // accessing the YCbCr arrays in a tight loop is slow. // converting the image to ycc increases performance by 2x. temp := newYCC(image.Rect(0, 0, input.Bounds().Dy(), int(width)), input.SubsampleRatio) - result := newYCC(image.Rect(0, 0, int(width), int(height)), input.SubsampleRatio) + result := newYCC(image.Rect(0, 0, int(width), int(height)), image.YCbCrSubsampleRatio444) coeffs, offset, filterLength := createWeights8(temp.Bounds().Dy(), taps, blur, scaleX, kernel) in := imageYCbCrToYCC(input) @@ -409,7 +409,7 @@ func resizeNearest(width, height uint, scaleX, scaleY float64, img image.Image, // accessing the YCbCr arrays in a tight loop is slow. // converting the image to ycc increases performance by 2x. temp := newYCC(image.Rect(0, 0, input.Bounds().Dy(), int(width)), input.SubsampleRatio) - result := newYCC(image.Rect(0, 0, int(width), int(height)), input.SubsampleRatio) + result := newYCC(image.Rect(0, 0, int(width), int(height)), image.YCbCrSubsampleRatio444) coeffs, offset, filterLength := createWeightsNearest(temp.Bounds().Dy(), taps, blur, scaleX) in := imageYCbCrToYCC(input) diff --git a/Godeps/_workspace/src/github.com/nfnt/resize/resize_test.go b/Godeps/_workspace/src/github.com/nfnt/resize/resize_test.go index ee31ac494..6f6911316 100644 --- a/Godeps/_workspace/src/github.com/nfnt/resize/resize_test.go +++ b/Godeps/_workspace/src/github.com/nfnt/resize/resize_test.go @@ -46,7 +46,7 @@ func Test_CorrectResize(t *testing.T) { } } -func Test_SameColor(t *testing.T) { +func Test_SameColorWithRGBA(t *testing.T) { img := image.NewRGBA(image.Rect(0, 0, 20, 20)) for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ { for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ { @@ -56,9 +56,99 @@ func Test_SameColor(t *testing.T) { out := Resize(10, 10, img, Lanczos3) for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ { for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ { - color := img.At(x, y).(color.RGBA) + color := out.At(x, y).(color.NRGBA) if color.R != 0x80 || color.G != 0x80 || color.B != 0x80 || color.A != 0xFF { - t.Fail() + t.Errorf("%+v", color) + } + } + } +} + +func Test_SameColorWithNRGBA(t *testing.T) { + img := image.NewNRGBA(image.Rect(0, 0, 20, 20)) + for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ { + for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ { + img.SetNRGBA(x, y, color.NRGBA{0x80, 0x80, 0x80, 0xFF}) + } + } + out := Resize(10, 10, img, Lanczos3) + for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ { + for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ { + color := out.At(x, y).(color.NRGBA) + if color.R != 0x80 || color.G != 0x80 || color.B != 0x80 || color.A != 0xFF { + t.Errorf("%+v", color) + } + } + } +} + +func Test_SameColorWithRGBA64(t *testing.T) { + img := image.NewRGBA64(image.Rect(0, 0, 20, 20)) + for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ { + for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ { + img.SetRGBA64(x, y, color.RGBA64{0x8000, 0x8000, 0x8000, 0xFFFF}) + } + } + out := Resize(10, 10, img, Lanczos3) + for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ { + for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ { + color := out.At(x, y).(color.NRGBA64) + if color.R != 0x8000 || color.G != 0x8000 || color.B != 0x8000 || color.A != 0xFFFF { + t.Errorf("%+v", color) + } + } + } +} + +func Test_SameColorWithNRGBA64(t *testing.T) { + img := image.NewNRGBA64(image.Rect(0, 0, 20, 20)) + for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ { + for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ { + img.SetNRGBA64(x, y, color.NRGBA64{0x8000, 0x8000, 0x8000, 0xFFFF}) + } + } + out := Resize(10, 10, img, Lanczos3) + for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ { + for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ { + color := out.At(x, y).(color.NRGBA64) + if color.R != 0x8000 || color.G != 0x8000 || color.B != 0x8000 || color.A != 0xFFFF { + t.Errorf("%+v", color) + } + } + } +} + +func Test_SameColorWithGray(t *testing.T) { + img := image.NewGray(image.Rect(0, 0, 20, 20)) + for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ { + for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ { + img.SetGray(x, y, color.Gray{0x80}) + } + } + out := Resize(10, 10, img, Lanczos3) + for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ { + for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ { + color := out.At(x, y).(color.Gray) + if color.Y != 0x80 { + t.Errorf("%+v", color) + } + } + } +} + +func Test_SameColorWithGray16(t *testing.T) { + img := image.NewGray16(image.Rect(0, 0, 20, 20)) + for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ { + for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ { + img.SetGray16(x, y, color.Gray16{0x8000}) + } + } + out := Resize(10, 10, img, Lanczos3) + for y := out.Bounds().Min.Y; y < out.Bounds().Max.Y; y++ { + for x := out.Bounds().Min.X; x < out.Bounds().Max.X; x++ { + color := out.At(x, y).(color.Gray16) + if color.Y != 0x8000 { + t.Errorf("%+v", color) } } } @@ -4,6 +4,8 @@ GOPATH ?= $(GOPATH:) GOFLAGS ?= $(GOFLAGS:) BUILD_NUMBER ?= $(BUILD_NUMBER:) +GO=$(GOPATH)/bin/godep go + ifeq ($(BUILD_NUMBER),) BUILD_NUMBER := dev endif @@ -21,29 +23,27 @@ travis: @echo building for travis rm -Rf $(DIST_ROOT) - @go clean $(GOFLAGS) -i ./... - + @$(GO) clean $(GOFLAGS) -i ./... + @cd web/react/ && npm install - @go build $(GOFLAGS) ./... + @$(GO) build $(GOFLAGS) ./... @mkdir -p logs - @go test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=180s ./api || exit 1 - @go test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=12s ./model || exit 1 - @go test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./store || exit 1 - @go test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./utils || exit 1 - @go test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./web || exit 1 + @$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=180s ./api || exit 1 + @$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=12s ./model || exit 1 + @$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./store || exit 1 + @$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./utils || exit 1 + @$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./web || exit 1 build: - @go build $(GOFLAGS) ./... + @$(GO) build $(GOFLAGS) ./... install: @go get $(GOFLAGS) github.com/tools/godep @if [ $(shell docker ps -a | grep -ci mattermost-mysql) -eq 0 ]; then \ - echo restoring go libs using godep; \ - $(GOPATH)/bin/godep restore; \ echo starting mattermost-mysql; \ docker run --name mattermost-mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=mostest \ -e MYSQL_USER=mmuser -e MYSQL_PASSWORD=mostest -e MYSQL_DATABASE=mattermost_test -d mysql > /dev/null; \ @@ -64,37 +64,37 @@ install: test: install @mkdir -p logs - @go test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=180s ./api || exit 1 - @go test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=12s ./model || exit 1 - @go test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./store || exit 1 - @go test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./utils || exit 1 - @go test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./web || exit 1 + @$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=180s ./api || exit 1 + @$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=12s ./model || exit 1 + @$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./store || exit 1 + @$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./utils || exit 1 + @$(GO) test $(GOFLAGS) -run=$(TESTS) -test.v -test.timeout=120s ./web || exit 1 benchmark: install @mkdir -p logs - @go test $(GOFLAGS) -test.v -run=NO_TESTS -bench=$(BENCH) ./api || exit 1 + @$(GO) test $(GOFLAGS) -test.v -run=NO_TESTS -bench=$(BENCH) ./api || exit 1 cover: install rm -Rf $(DIST_RESULTS) mkdir -p $(DIST_RESULTS) - @go test $(GOFLAGS) -coverprofile=$(DIST_RESULTS)/api.cover.out github.com/mattermost/platform/api - @go test $(GOFLAGS) -coverprofile=$(DIST_RESULTS)/model.cover.out github.com/mattermost/platform/model - @go test $(GOFLAGS) -coverprofile=$(DIST_RESULTS)/store.cover.out github.com/mattermost/platform/store - @go test $(GOFLAGS) -coverprofile=$(DIST_RESULTS)/utils.cover.out github.com/mattermost/platform/utils - @go test $(GOFLAGS) -coverprofile=$(DIST_RESULTS)/web.cover.out github.com/mattermost/platform/web + @$(GO) test $(GOFLAGS) -coverprofile=$(DIST_RESULTS)/api.cover.out github.com/mattermost/platform/api + @$(GO) test $(GOFLAGS) -coverprofile=$(DIST_RESULTS)/model.cover.out github.com/mattermost/platform/model + @$(GO) test $(GOFLAGS) -coverprofile=$(DIST_RESULTS)/store.cover.out github.com/mattermost/platform/store + @$(GO) test $(GOFLAGS) -coverprofile=$(DIST_RESULTS)/utils.cover.out github.com/mattermost/platform/utils + @$(GO) test $(GOFLAGS) -coverprofile=$(DIST_RESULTS)/web.cover.out github.com/mattermost/platform/web cd $(DIST_RESULTS) && \ echo "mode: set" > coverage.out && cat *.cover.out | grep -v mode: | sort -r | \ awk '{if($$1 != last) {print $$0;last=$$1}}' >> coverage.out - cd $(DIST_RESULTS) && go tool cover -html=coverage.out -o=coverage.html + cd $(DIST_RESULTS) && $(GO) tool cover -html=coverage.out -o=coverage.html rm -f $(DIST_RESULTS)/*.cover.out clean: rm -Rf $(DIST_ROOT) - @go clean $(GOFLAGS) -i ./... + @$(GO) clean $(GOFLAGS) -i ./... @if [ $(shell docker ps -a | grep -ci mattermost-mysql) -eq 1 ]; then \ echo stopping mattermost-mysql; \ @@ -124,7 +124,7 @@ run: install @cd web/react && npm start & @echo starting go web server - @go run $(GOFLAGS) mattermost.go -config=config.json & + @$(GO) run $(GOFLAGS) mattermost.go -config=config.json & @echo starting compass watch @cd web/sass-files && compass watch & @@ -161,8 +161,8 @@ cleandb: dist: install - @go build $(GOFLAGS) -i -a ./... - @go install $(GOFLAGS) -a ./... + @$(GO) build $(GOFLAGS) -i -a ./... + @$(GO) install $(GOFLAGS) -a ./... mkdir -p $(DIST_PATH)/bin cp $(GOPATH)/bin/platform $(DIST_PATH)/bin diff --git a/api/file.go b/api/file.go index 82cee9d1e..889c9dd1b 100644 --- a/api/file.go +++ b/api/file.go @@ -142,7 +142,7 @@ func fireAndForgetHandleImages(filenames []string, fileData [][]byte, teamId, ch go func() { var thumbnail image.Image if imgConfig.Width > int(utils.Cfg.ImageSettings.ThumbnailWidth) { - thumbnail = resize.Resize(utils.Cfg.ImageSettings.ThumbnailWidth, utils.Cfg.ImageSettings.ThumbnailHeight, img, resize.NearestNeighbor) + thumbnail = resize.Resize(utils.Cfg.ImageSettings.ThumbnailWidth, utils.Cfg.ImageSettings.ThumbnailHeight, img, resize.Lanczos3) } else { thumbnail = img } @@ -164,7 +164,7 @@ func fireAndForgetHandleImages(filenames []string, fileData [][]byte, teamId, ch go func() { var preview image.Image if imgConfig.Width > int(utils.Cfg.ImageSettings.PreviewWidth) { - preview = resize.Resize(utils.Cfg.ImageSettings.PreviewWidth, utils.Cfg.ImageSettings.PreviewHeight, img, resize.NearestNeighbor) + preview = resize.Resize(utils.Cfg.ImageSettings.PreviewWidth, utils.Cfg.ImageSettings.PreviewHeight, img, resize.Lanczos3) } else { preview = img } diff --git a/store/sql_channel_store.go b/store/sql_channel_store.go index 8961d5d97..f64558a92 100644 --- a/store/sql_channel_store.go +++ b/store/sql_channel_store.go @@ -120,7 +120,7 @@ func (s SqlChannelStore) Update(channel *model.Channel) StoreChannel { if count, err := s.GetMaster().Update(channel); err != nil { if IsUniqueConstraintError(err.Error(), "Name", "channels_name_teamid_key") { dupChannel := model.Channel{} - s.GetReplica().SelectOne(&dupChannel, "SELECT * FROM Channels WHERE TeamId=? AND Name=? AND DeleteAt > 0", channel.TeamId, channel.Name) + s.GetReplica().SelectOne(&dupChannel, "SELECT * FROM Channels WHERE TeamId = :TeamId AND Name= :Name AND DeleteAt > 0", map[string]interface{}{"TeamId": channel.TeamId, "Name": channel.Name}) if dupChannel.DeleteAt > 0 { result.Err = model.NewAppError("SqlChannelStore.Update", "A channel with that name was previously created", "id="+channel.Id+", "+err.Error()) } else { diff --git a/store/sql_post_store.go b/store/sql_post_store.go index 56c174e4c..ede69d125 100644 --- a/store/sql_post_store.go +++ b/store/sql_post_store.go @@ -38,7 +38,7 @@ func NewSqlPostStore(sqlStore *SqlStore) PostStore { func (s SqlPostStore) UpgradeSchemaIfNeeded() { // These execs are for upgrading currently created databases to full utf8mb4 compliance - // Will be removed as seen fit for upgrading + // Will be removed as seen fit for upgrading s.GetMaster().Exec("ALTER TABLE Posts charset=utf8mb4") s.GetMaster().Exec("ALTER TABLE Posts MODIFY COLUMN Message varchar(4000) CHARACTER SET utf8mb4") } @@ -80,14 +80,14 @@ func (s SqlPostStore) Save(post *model.Post) StoreChannel { time := model.GetMillis() if post.Type != model.POST_JOIN_LEAVE { - s.GetMaster().Exec("UPDATE Channels SET LastPostAt = ?, TotalMsgCount = TotalMsgCount + 1 WHERE Id = ?", time, post.ChannelId) + s.GetMaster().Exec("UPDATE Channels SET LastPostAt = :LastPostAt, TotalMsgCount = TotalMsgCount + 1 WHERE Id = :ChannelId", map[string]interface{}{"LastPostAt": time, "ChannelId": post.ChannelId}) } else { // don't update TotalMsgCount for unimportant messages so that the channel isn't marked as unread - s.GetMaster().Exec("UPDATE Channels SET LastPostAt = ? WHERE Id = ?", time, post.ChannelId) + s.GetMaster().Exec("UPDATE Channels SET LastPostAt = :LastPostAt WHERE Id = :ChannelId", map[string]interface{}{"LastPostAt": time, "ChannelId": post.ChannelId}) } if len(post.RootId) > 0 { - s.GetMaster().Exec("UPDATE Posts SET UpdateAt = ? WHERE Id = ?", time, post.RootId) + s.GetMaster().Exec("UPDATE Posts SET UpdateAt = :UpdateAt WHERE Id = :RootId", map[string]interface{}{"UpdateAt": time, "RootId": post.RootId}) } result.Data = post @@ -126,10 +126,10 @@ func (s SqlPostStore) Update(oldPost *model.Post, newMessage string, newHashtags result.Err = model.NewAppError("SqlPostStore.Update", "We couldn't update the Post", "id="+editPost.Id+", "+err.Error()) } else { time := model.GetMillis() - s.GetMaster().Exec("UPDATE Channels SET LastPostAt = ? WHERE Id = ?", time, editPost.ChannelId) + s.GetMaster().Exec("UPDATE Channels SET LastPostAt = :LastPostAt WHERE Id = :ChannelId", map[string]interface{}{"LastPostAt": time, "ChannelId": editPost.ChannelId}) if len(editPost.RootId) > 0 { - s.GetMaster().Exec("UPDATE Posts SET UpdateAt = ? WHERE Id = ?", time, editPost.RootId) + s.GetMaster().Exec("UPDATE Posts SET UpdateAt = :UpdateAt WHERE Id = :RootId", map[string]interface{}{"UpdateAt": time, "RootId": editPost.RootId}) } // mark the old post as deleted diff --git a/store/sql_user_store.go b/store/sql_user_store.go index 0228fa308..cd63e95b8 100644 --- a/store/sql_user_store.go +++ b/store/sql_user_store.go @@ -184,7 +184,7 @@ func (us SqlUserStore) UpdateLastPictureUpdate(userId string) StoreChannel { curTime := model.GetMillis() - if _, err := us.GetMaster().Exec("UPDATE Users SET LastPictureUpdate = ?, UpdateAt = ? WHERE Id = ?", curTime, curTime, userId); err != nil { + if _, err := us.GetMaster().Exec("UPDATE Users SET LastPictureUpdate = :Time, UpdateAt = :Time WHERE Id = :UserId", map[string]interface{}{"Time": curTime, "UserId": userId}); err != nil { result.Err = model.NewAppError("SqlUserStore.UpdateUpdateAt", "We couldn't update the update_at", "user_id="+userId) } else { result.Data = userId diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx index 87895588e..327520210 100644 --- a/web/react/components/create_post.jsx +++ b/web/react/components/create_post.jsx @@ -32,7 +32,7 @@ module.exports = React.createClass({ post.message = this.state.messageText; // if this is a reply, trim off any carets from the beginning of a message - if (this.state.rootId && post.message.startsWith("^")) { + if (this.state.rootId && post.message[0] === "^") { post.message = post.message.replace(/^\^+\s*/g, ""); } diff --git a/web/react/components/textbox.jsx b/web/react/components/textbox.jsx index ad50b7920..bbd1f84b6 100644 --- a/web/react/components/textbox.jsx +++ b/web/react/components/textbox.jsx @@ -36,7 +36,6 @@ module.exports = React.createClass({ this.resize(); this.processMentions(); - this.updateTextdiv(); }, componentWillUnmount: function() { PostStore.removeAddMentionListener(this._onChange); @@ -87,7 +86,6 @@ module.exports = React.createClass({ this.processMentions(); this.doProcessMentions = false; } - this.updateTextdiv(); this.resize(); }, componentWillReceiveProps: function(nextProps) { @@ -117,17 +115,6 @@ module.exports = React.createClass({ }); }, 1); }, - updateTextdiv: function() { - var html = utils.insertHtmlEntities(this.refs.message.getDOMNode().value); - for (var k in this.mentions) { - var m = this.mentions[k]; - var re = new RegExp('( |^)@' + m + '( |$|\n)', 'm'); - html = html.replace(re, '$1<span class="mention">@'+m+'</span>$2'); - } - var re2 = new RegExp('(^$)(?![.\n])', 'gm'); - html = html.replace(re2, '<br/><br/>'); - $(this.refs.textdiv.getDOMNode()).html(html); - }, handleChange: function() { this.props.onUserInput(this.refs.message.getDOMNode().value); this.resize(); @@ -181,7 +168,7 @@ module.exports = React.createClass({ } }, processMentions: function() { - /* First, find all the possible mentions, highlight them in the HTML and add + /* First, find all the possible mentions and add them all to a list of mentions */ var text = utils.insertHtmlEntities(this.refs.message.getDOMNode().value); @@ -192,9 +179,7 @@ module.exports = React.createClass({ var matches = text.match(re1); if (!matches) { - $(this.refs.textdiv.getDOMNode()).text(text); this.updateMentionTab(null, []); - this.mentions = []; return; } @@ -207,7 +192,7 @@ module.exports = React.createClass({ } /* Figure out what the user is currently typing. If it's a mention then we don't - want to highlight it and add it to the mention list yet, so we remove it if + want to add it to the mention list yet, so we remove it if there is only one occurence of that mention so far. */ var caret = utils.getCaretPosition(this.refs.message.getDOMNode()); @@ -225,14 +210,13 @@ module.exports = React.createClass({ typingMention = text.substring(atIndex+1, caret); } - var re3 = new RegExp('@' + typingMention + '( |$|\n)', 'g'); + var re2 = new RegExp('@' + typingMention + '( |$|\n)', 'g'); - if ((text.match(re3) || []).length === 1 && mentions.indexOf(typingMention) !== -1) { + if ((text.match(re2) || []).length === 1 && mentions.indexOf(typingMention) !== -1) { mentions.splice(mentions.indexOf(typingMention), 1); } this.updateMentionTab(null, mentions); - this.mentions = mentions; }, checkForNewMention: function(text) { var caret = utils.getCaretPosition(this.refs.message.getDOMNode()); @@ -287,15 +271,9 @@ module.exports = React.createClass({ elm.value = cmd; this.handleChange(); }, - scroll: function() { - var e = this.refs.message.getDOMNode(); - var d = this.refs.textdiv.getDOMNode(); - $(d).scrollTop($(e).scrollTop()); - }, resize: function() { var e = this.refs.message.getDOMNode(); var w = this.refs.wrapper.getDOMNode(); - var d = this.refs.textdiv.getDOMNode(); var lht = parseInt($(e).css('lineHeight'),10); var lines = e.scrollHeight / lht; @@ -303,15 +281,11 @@ module.exports = React.createClass({ if (e.scrollHeight - mod < 167) { $(e).css({'height':'auto','overflow-y':'hidden'}).height(e.scrollHeight - mod); - $(d).css({'height':'auto','overflow-y':'hidden'}).height(e.scrollHeight - mod); $(w).css({'height':'auto'}).height(e.scrollHeight+2); } else { $(e).css({'height':'auto','overflow-y':'scroll'}).height(167); - $(d).css({'height':'auto','overflow-y':'scroll'}).height(167); $(w).css({'height':'auto'}).height(167); } - - $(d).scrollTop($(e).scrollTop()); }, handleFocus: function() { var elm = this.refs.message.getDOMNode(); @@ -332,8 +306,7 @@ module.exports = React.createClass({ return ( <div ref="wrapper" className="textarea-wrapper"> <CommandList ref='commands' addCommand={this.addCommand} channelId={this.props.channelId} /> - <div className="form-control textarea-div" ref="textdiv"/> - <textarea id={this.props.id} ref="message" className={"form-control custom-textarea " + this.state.connection} spellCheck="true" autoComplete="off" autoCorrect="off" rows="1" placeholder={this.props.createMessage} value={this.props.messageText} onInput={this.handleChange} onChange={this.handleChange} onKeyPress={this.handleKeyPress} onKeyDown={this.handleKeyDown} onScroll={this.scroll} onFocus={this.handleFocus} onBlur={this.handleBlur} onPaste={this.handlePaste} /> + <textarea id={this.props.id} ref="message" className={"form-control custom-textarea " + this.state.connection} spellCheck="true" autoComplete="off" autoCorrect="off" rows="1" placeholder={this.props.createMessage} value={this.props.messageText} onInput={this.handleChange} onChange={this.handleChange} onKeyPress={this.handleKeyPress} onKeyDown={this.handleKeyDown} onFocus={this.handleFocus} onBlur={this.handleBlur} onPaste={this.handlePaste} /> </div> ); } diff --git a/web/react/components/user_settings.jsx b/web/react/components/user_settings.jsx index b940a3c6f..2ac9a2371 100644 --- a/web/react/components/user_settings.jsx +++ b/web/react/components/user_settings.jsx @@ -721,13 +721,15 @@ var GeneralTab = React.createClass({ if(!this.submitActive) return; - if(this.state.picture.type !== "image/jpeg") { - this.setState({client_error: "Only JPG images may be used for profile pictures"}); + var picture = this.state.picture; + + if(picture.type !== "image/jpeg" && picture.type !== "image/png") { + this.setState({client_error: "Only JPG or PNG images may be used for profile pictures"}); return; } formData = new FormData(); - formData.append('image', this.state.picture, this.state.picture.name); + formData.append('image', picture, picture.name); client.uploadProfileImage(formData, function(data) { |