commit
57b54ea26d
@ -1,219 +0,0 @@ |
||||
@RoomHistoryManager = new class |
||||
defaultLimit = 50 |
||||
|
||||
histories = {} |
||||
|
||||
getRoom = (rid) -> |
||||
if not histories[rid]? |
||||
histories[rid] = |
||||
hasMore: new ReactiveVar true |
||||
hasMoreNext: new ReactiveVar false |
||||
isLoading: new ReactiveVar false |
||||
unreadNotLoaded: new ReactiveVar 0 |
||||
firstUnread: new ReactiveVar |
||||
loaded: undefined |
||||
|
||||
return histories[rid] |
||||
|
||||
getMore = (rid, limit=defaultLimit) -> |
||||
room = getRoom rid |
||||
if room.hasMore.curValue isnt true |
||||
return |
||||
|
||||
room.isLoading.set true |
||||
|
||||
# ScrollListener.setLoader true |
||||
lastMessage = ChatMessage.findOne({rid: rid}, {sort: {ts: 1}}) |
||||
# lastMessage ?= ChatMessage.findOne({rid: rid}, {sort: {ts: 1}}) |
||||
|
||||
if lastMessage? |
||||
ts = lastMessage.ts |
||||
else |
||||
ts = undefined |
||||
|
||||
ls = undefined |
||||
typeName = undefined |
||||
|
||||
subscription = ChatSubscription.findOne rid: rid |
||||
if subscription? |
||||
ls = subscription.ls |
||||
typeName = subscription.t + subscription.name |
||||
else |
||||
curRoomDoc = ChatRoom.findOne(_id: rid) |
||||
typeName = curRoomDoc?.t + curRoomDoc?.name |
||||
|
||||
Meteor.call 'loadHistory', rid, ts, limit, ls, (err, result) -> |
||||
room.unreadNotLoaded.set result?.unreadNotLoaded |
||||
room.firstUnread.set result?.firstUnread |
||||
|
||||
wrapper = $('.messages-box .wrapper').get(0) |
||||
if wrapper? |
||||
previousHeight = wrapper.scrollHeight |
||||
|
||||
for item in result?.messages or [] when item.t isnt 'command' |
||||
item.roles = _.union(UserRoles.findOne(item.u?._id)?.roles, RoomRoles.findOne({rid: item.rid, 'u._id': item.u?._id})?.roles) |
||||
ChatMessage.upsert {_id: item._id}, item |
||||
|
||||
if wrapper? |
||||
heightDiff = wrapper.scrollHeight - previousHeight |
||||
wrapper.scrollTop += heightDiff |
||||
|
||||
Meteor.defer -> |
||||
readMessage.refreshUnreadMark(rid, true) |
||||
RoomManager.updateMentionsMarksOfRoom typeName |
||||
|
||||
room.isLoading.set false |
||||
room.loaded ?= 0 |
||||
if result?.messages?.length? |
||||
room.loaded += result.messages.length |
||||
if result?.messages?.length < limit |
||||
room.hasMore.set false |
||||
|
||||
getMoreNext = (rid, limit=defaultLimit) -> |
||||
room = getRoom rid |
||||
if room.hasMoreNext.curValue isnt true |
||||
return |
||||
|
||||
instance = Blaze.getView($('.messages-box .wrapper')[0]).templateInstance() |
||||
instance.atBottom = false |
||||
|
||||
room.isLoading.set true |
||||
|
||||
lastMessage = ChatMessage.findOne({rid: rid}, {sort: {ts: -1}}) |
||||
|
||||
typeName = undefined |
||||
|
||||
subscription = ChatSubscription.findOne rid: rid |
||||
if subscription? |
||||
ls = subscription.ls |
||||
typeName = subscription.t + subscription.name |
||||
else |
||||
curRoomDoc = ChatRoom.findOne(_id: rid) |
||||
typeName = curRoomDoc?.t + curRoomDoc?.name |
||||
|
||||
ts = lastMessage.ts |
||||
|
||||
if ts |
||||
Meteor.call 'loadNextMessages', rid, ts, limit, (err, result) -> |
||||
for item in result?.messages or [] |
||||
if item.t isnt 'command' |
||||
item.roles = _.union(UserRoles.findOne(item.u?._id)?.roles, RoomRoles.findOne({rid: item.rid, 'u._id': item.u?._id})?.roles) |
||||
ChatMessage.upsert {_id: item._id}, item |
||||
|
||||
Meteor.defer -> |
||||
RoomManager.updateMentionsMarksOfRoom typeName |
||||
|
||||
room.isLoading.set false |
||||
room.loaded ?= 0 |
||||
if result.messages.length? |
||||
room.loaded += result.messages.length |
||||
if result.messages.length < limit |
||||
room.hasMoreNext.set false |
||||
|
||||
getSurroundingMessages = (message, limit=defaultLimit) -> |
||||
unless message?.rid |
||||
return |
||||
|
||||
instance = Blaze.getView($('.messages-box .wrapper')[0]).templateInstance() |
||||
|
||||
if ChatMessage.findOne message._id |
||||
wrapper = $('.messages-box .wrapper') |
||||
msgElement = $("##{message._id}", wrapper) |
||||
pos = wrapper.scrollTop() + msgElement.offset().top - wrapper.height()/2 |
||||
wrapper.animate({ |
||||
scrollTop: pos |
||||
}, 500) |
||||
msgElement.addClass('highlight') |
||||
|
||||
setTimeout -> |
||||
messages = wrapper[0] |
||||
instance.atBottom = messages.scrollTop >= messages.scrollHeight - messages.clientHeight; |
||||
|
||||
setTimeout -> |
||||
msgElement.removeClass('highlight') |
||||
, 500 |
||||
else |
||||
room = getRoom message.rid |
||||
room.isLoading.set true |
||||
ChatMessage.remove { rid: message.rid } |
||||
|
||||
typeName = undefined |
||||
|
||||
subscription = ChatSubscription.findOne rid: message.rid |
||||
if subscription? |
||||
ls = subscription.ls |
||||
typeName = subscription.t + subscription.name |
||||
else |
||||
curRoomDoc = ChatRoom.findOne(_id: message.rid) |
||||
typeName = curRoomDoc?.t + curRoomDoc?.name |
||||
|
||||
Meteor.call 'loadSurroundingMessages', message, limit, (err, result) -> |
||||
for item in result?.messages or [] |
||||
if item.t isnt 'command' |
||||
item.roles = _.union(UserRoles.findOne(item.u?._id)?.roles, RoomRoles.findOne({rid: item.rid, 'u._id': item.u?._id})?.roles) |
||||
ChatMessage.upsert {_id: item._id}, item |
||||
|
||||
Meteor.defer -> |
||||
readMessage.refreshUnreadMark(message.rid, true) |
||||
RoomManager.updateMentionsMarksOfRoom typeName |
||||
wrapper = $('.messages-box .wrapper') |
||||
msgElement = $("##{message._id}", wrapper) |
||||
pos = wrapper.scrollTop() + msgElement.offset().top - wrapper.height()/2 |
||||
wrapper.animate({ |
||||
scrollTop: pos |
||||
}, 500) |
||||
|
||||
msgElement.addClass('highlight') |
||||
|
||||
setTimeout -> |
||||
room.isLoading.set false |
||||
messages = wrapper[0] |
||||
instance.atBottom = !result.moreAfter && messages.scrollTop >= messages.scrollHeight - messages.clientHeight; |
||||
, 500 |
||||
|
||||
setTimeout -> |
||||
msgElement.removeClass('highlight') |
||||
, 500 |
||||
room.loaded ?= 0 |
||||
if result.messages.length? |
||||
room.loaded += result.messages.length |
||||
room.hasMore.set result.moreBefore |
||||
room.hasMoreNext.set result.moreAfter |
||||
|
||||
hasMore = (rid) -> |
||||
room = getRoom rid |
||||
|
||||
return room.hasMore.get() |
||||
|
||||
hasMoreNext = (rid) -> |
||||
room = getRoom rid |
||||
return room.hasMoreNext.get() |
||||
|
||||
|
||||
getMoreIfIsEmpty = (rid) -> |
||||
room = getRoom rid |
||||
|
||||
if room.loaded is undefined |
||||
getMore rid |
||||
|
||||
|
||||
isLoading = (rid) -> |
||||
room = getRoom rid |
||||
return room.isLoading.get() |
||||
|
||||
clear = (rid) -> |
||||
ChatMessage.remove({ rid: rid }) |
||||
if histories[rid]? |
||||
histories[rid].hasMore.set true |
||||
histories[rid].isLoading.set false |
||||
histories[rid].loaded = undefined |
||||
|
||||
getRoom: getRoom |
||||
getMore: getMore |
||||
getMoreNext: getMoreNext |
||||
getMoreIfIsEmpty: getMoreIfIsEmpty |
||||
hasMore: hasMore |
||||
hasMoreNext: hasMoreNext |
||||
isLoading: isLoading |
||||
clear: clear |
||||
getSurroundingMessages: getSurroundingMessages |
||||
@ -0,0 +1,266 @@ |
||||
/* globals readMessage UserRoles RoomRoles*/ |
||||
export const RoomHistoryManager = new class { |
||||
constructor() { |
||||
this.defaultLimit = 50; |
||||
this.histories = {}; |
||||
} |
||||
getRoom(rid) { |
||||
if ((this.histories[rid] == null)) { |
||||
this.histories[rid] = { |
||||
hasMore: new ReactiveVar(true), |
||||
hasMoreNext: new ReactiveVar(false), |
||||
isLoading: new ReactiveVar(false), |
||||
unreadNotLoaded: new ReactiveVar(0), |
||||
firstUnread: new ReactiveVar, |
||||
loaded: undefined |
||||
}; |
||||
} |
||||
|
||||
return this.histories[rid]; |
||||
} |
||||
|
||||
getMore(rid, limit) { |
||||
let ts; |
||||
if (limit == null) { limit = this.defaultLimit; } |
||||
const room = this.getRoom(rid); |
||||
if (room.hasMore.curValue !== true) { |
||||
return; |
||||
} |
||||
|
||||
room.isLoading.set(true); |
||||
|
||||
// ScrollListener.setLoader true
|
||||
const lastMessage = ChatMessage.findOne({rid}, {sort: {ts: 1}}); |
||||
// lastMessage ?= ChatMessage.findOne({rid: rid}, {sort: {ts: 1}})
|
||||
|
||||
if (lastMessage != null) { |
||||
({ ts } = lastMessage); |
||||
} else { |
||||
ts = undefined; |
||||
} |
||||
|
||||
let ls = undefined; |
||||
let typeName = undefined; |
||||
|
||||
const subscription = ChatSubscription.findOne({rid}); |
||||
if (subscription != null) { |
||||
({ ls } = subscription); |
||||
typeName = subscription.t + subscription.name; |
||||
} else { |
||||
const curRoomDoc = ChatRoom.findOne({_id: rid}); |
||||
typeName = (curRoomDoc != null ? curRoomDoc.t : undefined) + (curRoomDoc != null ? curRoomDoc.name : undefined); |
||||
} |
||||
|
||||
return Meteor.call('loadHistory', rid, ts, limit, ls, function(err, result) { |
||||
if (err) { |
||||
return; |
||||
} |
||||
let previousHeight; |
||||
room.unreadNotLoaded.set(result.unreadNotLoaded); |
||||
room.firstUnread.set(result.firstUnread); |
||||
|
||||
const wrapper = $('.messages-box .wrapper').get(0); |
||||
if (wrapper != null) { |
||||
previousHeight = wrapper.scrollHeight; |
||||
} |
||||
|
||||
result.messages.forEach(item => { |
||||
if (item.t !== 'command') { |
||||
const roles = [ |
||||
(item.u && item.u._id && UserRoles.findOne(item.u._id, { fields: { roles: 1 }})) || {}, |
||||
(item.u && item.u._id && RoomRoles.findOne({rid: item.rid, 'u._id': item.u._id})) || {} |
||||
].map(e => e.roles); |
||||
item.roles = _.union.apply(_.union, roles); |
||||
ChatMessage.upsert({_id: item._id}, item); |
||||
} |
||||
}); |
||||
|
||||
if (wrapper) { |
||||
const heightDiff = wrapper.scrollHeight - previousHeight; |
||||
wrapper.scrollTop += heightDiff; |
||||
} |
||||
|
||||
Meteor.defer(() => { |
||||
readMessage.refreshUnreadMark(rid, true); |
||||
return RoomManager.updateMentionsMarksOfRoom(typeName); |
||||
}); |
||||
|
||||
room.isLoading.set(false); |
||||
if (room.loaded == null) { room.loaded = 0; } |
||||
room.loaded += result.messages.length; |
||||
if (result.messages.length < limit) { |
||||
return room.hasMore.set(false); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
getMoreNext(rid, limit) { |
||||
if (limit == null) { limit = this.defaultLimit; } |
||||
const room = this.getRoom(rid); |
||||
if (room.hasMoreNext.curValue !== true) { |
||||
return; |
||||
} |
||||
|
||||
const instance = Blaze.getView($('.messages-box .wrapper')[0]).templateInstance(); |
||||
instance.atBottom = false; |
||||
|
||||
room.isLoading.set(true); |
||||
|
||||
const lastMessage = ChatMessage.findOne({rid}, {sort: {ts: -1}}); |
||||
|
||||
let typeName = undefined; |
||||
|
||||
const subscription = ChatSubscription.findOne({rid}); |
||||
if (subscription != null) { |
||||
// const { ls } = subscription;
|
||||
typeName = subscription.t + subscription.name; |
||||
} else { |
||||
const curRoomDoc = ChatRoom.findOne({_id: rid}); |
||||
typeName = (curRoomDoc != null ? curRoomDoc.t : undefined) + (curRoomDoc != null ? curRoomDoc.name : undefined); |
||||
} |
||||
|
||||
const { ts } = lastMessage; |
||||
|
||||
if (ts) { |
||||
return Meteor.call('loadNextMessages', rid, ts, limit, function(err, result) { |
||||
for (const item of Array.from((result != null ? result.messages : undefined) || [])) { |
||||
if (item.t !== 'command') { |
||||
const roles = [ |
||||
(item.u && item.u._id && UserRoles.findOne(item.u._id, { fields: { roles: 1 }})) || {}, |
||||
(item.u && item.u._id && RoomRoles.findOne({rid: item.rid, 'u._id': item.u._id})) || {} |
||||
].map(e => e.roles); |
||||
item.roles = _.union.apply(_.union, roles); |
||||
ChatMessage.upsert({_id: item._id}, item); |
||||
} |
||||
} |
||||
|
||||
Meteor.defer(() => RoomManager.updateMentionsMarksOfRoom(typeName)); |
||||
|
||||
room.isLoading.set(false); |
||||
if (room.loaded == null) { room.loaded = 0; } |
||||
|
||||
room.loaded += result.messages.length; |
||||
if (result.messages.length < limit) { |
||||
room.hasMoreNext.set(false); |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
|
||||
getSurroundingMessages(message, limit) { |
||||
if (limit == null) { limit = this.defaultLimit; } |
||||
if (!(message != null ? message.rid : undefined)) { |
||||
return; |
||||
} |
||||
|
||||
const instance = Blaze.getView($('.messages-box .wrapper')[0]).templateInstance(); |
||||
|
||||
if (ChatMessage.findOne(message._id)) { |
||||
const wrapper = $('.messages-box .wrapper'); |
||||
const msgElement = $(`#${ message._id }`, wrapper); |
||||
const pos = (wrapper.scrollTop() + msgElement.offset().top) - (wrapper.height()/2); |
||||
wrapper.animate({ |
||||
scrollTop: pos |
||||
}, 500); |
||||
msgElement.addClass('highlight'); |
||||
|
||||
setTimeout(function() { |
||||
const messages = wrapper[0]; |
||||
return instance.atBottom = messages.scrollTop >= (messages.scrollHeight - messages.clientHeight); |
||||
}); |
||||
|
||||
return setTimeout(() => msgElement.removeClass('highlight') |
||||
, 500); |
||||
} else { |
||||
const room = this.getRoom(message.rid); |
||||
room.isLoading.set(true); |
||||
ChatMessage.remove({ rid: message.rid }); |
||||
|
||||
let typeName = undefined; |
||||
|
||||
const subscription = ChatSubscription.findOne({rid: message.rid}); |
||||
if (subscription) { |
||||
// const { ls } = subscription;
|
||||
typeName = subscription.t + subscription.name; |
||||
} else { |
||||
const curRoomDoc = ChatRoom.findOne({_id: message.rid}); |
||||
typeName = (curRoomDoc != null ? curRoomDoc.t : undefined) + (curRoomDoc != null ? curRoomDoc.name : undefined); |
||||
} |
||||
|
||||
return Meteor.call('loadSurroundingMessages', message, limit, function(err, result) { |
||||
for (const item of Array.from((result != null ? result.messages : undefined) || [])) { |
||||
if (item.t !== 'command') { |
||||
const roles = [ |
||||
(item.u && item.u._id && UserRoles.findOne(item.u._id, { fields: { roles: 1 }})) || {}, |
||||
(item.u && item.u._id && RoomRoles.findOne({rid: item.rid, 'u._id': item.u._id})) || {} |
||||
].map(e => e.roles); |
||||
item.roles = _.union.apply(_.union, roles); |
||||
ChatMessage.upsert({_id: item._id}, item); |
||||
} |
||||
} |
||||
|
||||
Meteor.defer(function() { |
||||
readMessage.refreshUnreadMark(message.rid, true); |
||||
RoomManager.updateMentionsMarksOfRoom(typeName); |
||||
const wrapper = $('.messages-box .wrapper'); |
||||
const msgElement = $(`#${ message._id }`, wrapper); |
||||
const pos = (wrapper.scrollTop() + msgElement.offset().top) - (wrapper.height()/2); |
||||
wrapper.animate({ |
||||
scrollTop: pos |
||||
}, 500); |
||||
|
||||
msgElement.addClass('highlight'); |
||||
|
||||
setTimeout(function() { |
||||
room.isLoading.set(false); |
||||
const messages = wrapper[0]; |
||||
instance.atBottom = !result.moreAfter && (messages.scrollTop >= (messages.scrollHeight - messages.clientHeight)); |
||||
return 500; |
||||
}); |
||||
|
||||
return setTimeout(() => msgElement.removeClass('highlight') |
||||
, 500); |
||||
}); |
||||
if (room.loaded == null) { room.loaded = 0; } |
||||
room.loaded += result.messages.length; |
||||
room.hasMore.set(result.moreBefore); |
||||
return room.hasMoreNext.set(result.moreAfter); |
||||
}); |
||||
} |
||||
} |
||||
|
||||
hasMore(rid) { |
||||
const room = this.getRoom(rid); |
||||
return room.hasMore.get(); |
||||
} |
||||
|
||||
hasMoreNext(rid) { |
||||
const room = this.getRoom(rid); |
||||
return room.hasMoreNext.get(); |
||||
} |
||||
|
||||
|
||||
getMoreIfIsEmpty(rid) { |
||||
const room = this.getRoom(rid); |
||||
|
||||
if (room.loaded === undefined) { |
||||
return this.getMore(rid); |
||||
} |
||||
} |
||||
|
||||
|
||||
isLoading(rid) { |
||||
const room = this.getRoom(rid); |
||||
return room.isLoading.get(); |
||||
} |
||||
|
||||
clear(rid) { |
||||
ChatMessage.remove({ rid }); |
||||
if (this.histories[rid] != null) { |
||||
this.histories[rid].hasMore.set(true); |
||||
this.histories[rid].isLoading.set(false); |
||||
return this.histories[rid].loaded = undefined; |
||||
} |
||||
} |
||||
}; |
||||
this.RoomHistoryManager = RoomHistoryManager; |
||||
@ -1,253 +0,0 @@ |
||||
loadMissedMessages = (rid) -> |
||||
lastMessage = ChatMessage.findOne({rid: rid}, {sort: {ts: -1}, limit: 1}) |
||||
if not lastMessage? |
||||
return |
||||
|
||||
Meteor.call 'loadMissedMessages', rid, lastMessage.ts, (err, result) -> |
||||
for item in result |
||||
RocketChat.promises.run('onClientMessageReceived', item).then (item) -> |
||||
item.roles = _.union(UserRoles.findOne(item.u?._id)?.roles, RoomRoles.findOne({rid: item.rid, 'u._id': item.u?._id})?.roles) |
||||
ChatMessage.upsert {_id: item._id}, item |
||||
|
||||
connectionWasOnline = true |
||||
Tracker.autorun -> |
||||
connected = Meteor.connection.status().connected |
||||
|
||||
if connected is true and connectionWasOnline is false and RoomManager.openedRooms? |
||||
for key, value of RoomManager.openedRooms |
||||
if value.rid? |
||||
loadMissedMessages(value.rid) |
||||
|
||||
connectionWasOnline = connected |
||||
|
||||
# Reload rooms after login |
||||
currentUsername = undefined |
||||
Tracker.autorun (c) -> |
||||
user = Meteor.user() |
||||
if currentUsername is undefined and user?.username? |
||||
currentUsername = user.username |
||||
RoomManager.closeAllRooms() |
||||
FlowRouter._current.route.callAction(FlowRouter._current) |
||||
|
||||
Meteor.startup -> |
||||
ChatMessage.find().observe |
||||
removed: (record) -> |
||||
if RoomManager.getOpenedRoomByRid(record.rid)? |
||||
recordBefore = ChatMessage.findOne {ts: {$lt: record.ts}}, {sort: {ts: -1}} |
||||
if recordBefore? |
||||
ChatMessage.update {_id: recordBefore._id}, {$set: {tick: new Date}} |
||||
|
||||
recordAfter = ChatMessage.findOne {ts: {$gt: record.ts}}, {sort: {ts: 1}} |
||||
if recordAfter? |
||||
ChatMessage.update {_id: recordAfter._id}, {$set: {tick: new Date}} |
||||
|
||||
|
||||
onDeleteMessageStream = (msg) -> |
||||
ChatMessage.remove _id: msg._id |
||||
|
||||
|
||||
Tracker.autorun -> |
||||
if Meteor.userId() |
||||
RocketChat.Notifications.onUser 'message', (msg) -> |
||||
msg.u = |
||||
username: 'rocket.cat' |
||||
msg.private = true |
||||
|
||||
ChatMessage.upsert { _id: msg._id }, msg |
||||
|
||||
|
||||
@RoomManager = new class |
||||
openedRooms = {} |
||||
msgStream = new Meteor.Streamer 'room-messages' |
||||
onlineUsers = new ReactiveVar {} |
||||
|
||||
Dep = new Tracker.Dependency |
||||
|
||||
close = (typeName) -> |
||||
if openedRooms[typeName] |
||||
if openedRooms[typeName].rid? |
||||
msgStream.removeAllListeners openedRooms[typeName].rid |
||||
RocketChat.Notifications.unRoom openedRooms[typeName].rid, 'deleteMessage', onDeleteMessageStream |
||||
|
||||
openedRooms[typeName].ready = false |
||||
openedRooms[typeName].active = false |
||||
if openedRooms[typeName].template? |
||||
Blaze.remove openedRooms[typeName].template |
||||
delete openedRooms[typeName].dom |
||||
delete openedRooms[typeName].template |
||||
|
||||
rid = openedRooms[typeName].rid |
||||
delete openedRooms[typeName] |
||||
|
||||
if rid? |
||||
RoomHistoryManager.clear rid |
||||
|
||||
|
||||
computation = Tracker.autorun -> |
||||
for typeName, record of openedRooms when record.active is true |
||||
do (typeName, record) -> |
||||
|
||||
user = Meteor.user() |
||||
|
||||
if record.ready is true |
||||
return |
||||
|
||||
ready = CachedChatRoom.ready.get() and CachedChatSubscription.ready.get() is true |
||||
|
||||
if ready is true |
||||
type = typeName.substr(0, 1) |
||||
name = typeName.substr(1) |
||||
|
||||
room = Tracker.nonreactive => |
||||
return RocketChat.roomTypes.findRoom(type, name, user) |
||||
|
||||
if not room? |
||||
record.ready = true |
||||
else |
||||
openedRooms[typeName].rid = room._id |
||||
|
||||
RoomHistoryManager.getMoreIfIsEmpty room._id |
||||
record.ready = RoomHistoryManager.isLoading(room._id) is false |
||||
Dep.changed() |
||||
|
||||
if openedRooms[typeName].streamActive isnt true |
||||
openedRooms[typeName].streamActive = true |
||||
msgStream.on openedRooms[typeName].rid, (msg) -> |
||||
|
||||
RocketChat.promises.run('onClientMessageReceived', msg).then (msg) -> |
||||
|
||||
# Should not send message to room if room has not loaded all the current messages |
||||
if RoomHistoryManager.hasMoreNext(openedRooms[typeName].rid) is false |
||||
|
||||
# Do not load command messages into channel |
||||
if msg.t isnt 'command' |
||||
msg.roles = _.union(UserRoles.findOne(msg.u?._id)?.roles, RoomRoles.findOne({rid: msg.rid, 'u._id': msg.u?._id})?.roles) |
||||
ChatMessage.upsert { _id: msg._id }, msg |
||||
|
||||
Meteor.defer -> |
||||
RoomManager.updateMentionsMarksOfRoom typeName |
||||
|
||||
RocketChat.callbacks.run 'streamMessage', msg |
||||
|
||||
window.fireGlobalEvent('new-message', msg); |
||||
|
||||
RocketChat.Notifications.onRoom openedRooms[typeName].rid, 'deleteMessage', onDeleteMessageStream |
||||
|
||||
Dep.changed() |
||||
|
||||
|
||||
closeOlderRooms = -> |
||||
maxRoomsOpen = 10 |
||||
if Object.keys(openedRooms).length <= maxRoomsOpen |
||||
return |
||||
|
||||
roomsToClose = _.sortBy(_.values(openedRooms), 'lastSeen').reverse().slice(maxRoomsOpen) |
||||
for roomToClose in roomsToClose |
||||
close roomToClose.typeName |
||||
|
||||
|
||||
closeAllRooms = -> |
||||
for key, openedRoom of openedRooms |
||||
close openedRoom.typeName |
||||
|
||||
|
||||
open = (typeName) -> |
||||
if not openedRooms[typeName]? |
||||
openedRooms[typeName] = |
||||
typeName: typeName |
||||
active: false |
||||
ready: false |
||||
unreadSince: new ReactiveVar undefined |
||||
|
||||
openedRooms[typeName].lastSeen = new Date |
||||
|
||||
if openedRooms[typeName].ready |
||||
closeOlderRooms() |
||||
|
||||
if CachedChatSubscription.ready.get() is true |
||||
|
||||
if openedRooms[typeName].active isnt true |
||||
openedRooms[typeName].active = true |
||||
|
||||
computation?.invalidate() |
||||
|
||||
return { |
||||
ready: -> |
||||
Dep.depend() |
||||
return openedRooms[typeName].ready |
||||
} |
||||
|
||||
getOpenedRoomByRid = (rid) -> |
||||
for typeName, openedRoom of openedRooms |
||||
if openedRoom.rid is rid |
||||
return openedRoom |
||||
|
||||
getDomOfRoom = (typeName, rid) -> |
||||
room = openedRooms[typeName] |
||||
if not room? |
||||
return |
||||
|
||||
if not room.dom? and rid? |
||||
room.dom = document.createElement 'div' |
||||
room.dom.classList.add 'room-container' |
||||
contentAsFunc = (content) -> |
||||
return -> content |
||||
|
||||
room.template = Blaze._TemplateWith { _id: rid }, contentAsFunc(Template.room) |
||||
Blaze.render room.template, room.dom #, nextNode, parentView |
||||
|
||||
return room.dom |
||||
|
||||
existsDomOfRoom = (typeName) -> |
||||
room = openedRooms[typeName] |
||||
return room?.dom? |
||||
|
||||
updateUserStatus = (user, status, utcOffset) -> |
||||
onlineUsersValue = onlineUsers.curValue |
||||
|
||||
if status is 'offline' |
||||
delete onlineUsersValue[user.username] |
||||
else |
||||
onlineUsersValue[user.username] = |
||||
_id: user._id |
||||
status: status |
||||
utcOffset: utcOffset |
||||
|
||||
onlineUsers.set onlineUsersValue |
||||
|
||||
updateMentionsMarksOfRoom = (typeName) -> |
||||
dom = getDomOfRoom typeName |
||||
if not dom? |
||||
return |
||||
|
||||
ticksBar = $(dom).find('.ticks-bar') |
||||
$(dom).find('.ticks-bar > .tick').remove() |
||||
|
||||
scrollTop = $(dom).find('.messages-box > .wrapper').scrollTop() - 50 |
||||
totalHeight = $(dom).find('.messages-box > .wrapper > ul').height() + 40 |
||||
|
||||
$('.messages-box .mention-link-me').each (index, item) -> |
||||
topOffset = $(item).offset().top + scrollTop |
||||
percent = 100 / totalHeight * topOffset |
||||
if $(item).hasClass('mention-link-all') |
||||
ticksBar.append('<div class="tick background-attention-color" style="top: '+percent+'%;"></div>') |
||||
else |
||||
ticksBar.append('<div class="tick background-primary-action-color" style="top: '+percent+'%;"></div>') |
||||
|
||||
open: open |
||||
close: close |
||||
closeAllRooms: closeAllRooms |
||||
getDomOfRoom: getDomOfRoom |
||||
existsDomOfRoom: existsDomOfRoom |
||||
msgStream: msgStream |
||||
openedRooms: openedRooms |
||||
updateUserStatus: updateUserStatus |
||||
onlineUsers: onlineUsers |
||||
updateMentionsMarksOfRoom: updateMentionsMarksOfRoom |
||||
getOpenedRoomByRid: getOpenedRoomByRid |
||||
computation: computation |
||||
|
||||
|
||||
RocketChat.callbacks.add 'afterLogoutCleanUp', -> |
||||
RoomManager.closeAllRooms() |
||||
, RocketChat.callbacks.priority.MEDIUM, 'roommanager-after-logout-cleanup' |
||||
@ -0,0 +1,302 @@ |
||||
const RoomManager = new function() { |
||||
const openedRooms = {}; |
||||
const msgStream = new Meteor.Streamer('room-messages'); |
||||
const onlineUsers = new ReactiveVar({}); |
||||
const Dep = new Tracker.Dependency(); |
||||
const Cls = class { |
||||
static initClass() { |
||||
/* globals CachedChatRoom CachedChatSubscription */ |
||||
this.prototype.openedRooms = openedRooms; |
||||
this.prototype.onlineUsers = onlineUsers; |
||||
this.prototype.computation = Tracker.autorun(() => { |
||||
Object.keys(openedRooms).forEach(typeName => { |
||||
const record = openedRooms[typeName]; |
||||
if (record.active !== true || record.ready === true) { return; } |
||||
const ready = CachedChatRoom.ready.get() && CachedChatSubscription.ready.get() === true; |
||||
if (ready !== true) { return; } |
||||
const user = Meteor.user(); |
||||
|
||||
const type = typeName.substr(0, 1); |
||||
const name = typeName.substr(1); |
||||
|
||||
const room = Tracker.nonreactive(() => { |
||||
return RocketChat.roomTypes.findRoom(type, name, user); |
||||
}); |
||||
|
||||
if (room == null) { |
||||
record.ready = true; |
||||
} else { |
||||
openedRooms[typeName].rid = room._id; |
||||
|
||||
RoomHistoryManager.getMoreIfIsEmpty(room._id); |
||||
record.ready = RoomHistoryManager.isLoading(room._id) === false; |
||||
Dep.changed(); |
||||
|
||||
if (openedRooms[typeName].streamActive !== true) { |
||||
openedRooms[typeName].streamActive = true; |
||||
msgStream.on(openedRooms[typeName].rid, msg => |
||||
|
||||
RocketChat.promises.run('onClientMessageReceived', msg).then(function(msg) { |
||||
|
||||
// Should not send message to room if room has not loaded all the current messages
|
||||
if (RoomHistoryManager.hasMoreNext(openedRooms[typeName].rid) === false) { |
||||
|
||||
// Do not load command messages into channel
|
||||
if (msg.t !== 'command') { |
||||
const roles = [ |
||||
(msg.u && msg.u._id && UserRoles.findOne(msg.u._id, { fields: { roles: 1 }})) || {}, |
||||
(msg.u && msg.u._id && RoomRoles.findOne({rid: msg.rid, 'u._id': msg.u._id})) || {} |
||||
].map(e => e.roles); |
||||
msg.roles = _.union.apply(_.union, roles); |
||||
ChatMessage.upsert({ _id: msg._id }, msg); |
||||
} |
||||
|
||||
Meteor.defer(() => RoomManager.updateMentionsMarksOfRoom(typeName)); |
||||
|
||||
RocketChat.callbacks.run('streamMessage', msg); |
||||
|
||||
return window.fireGlobalEvent('new-message', msg); |
||||
} |
||||
}) |
||||
); |
||||
|
||||
RocketChat.Notifications.onRoom(openedRooms[typeName].rid, 'deleteMessage', onDeleteMessageStream); // eslint-disable-line no-use-before-define
|
||||
} |
||||
} |
||||
return Dep.changed(); |
||||
}); |
||||
}); |
||||
} |
||||
|
||||
getOpenedRoomByRid(rid) { |
||||
return Object.keys(openedRooms).map(typeName => openedRooms[typeName]).find(openedRoom => openedRoom.rid === rid); |
||||
} |
||||
|
||||
getDomOfRoom(typeName, rid) { |
||||
const room = openedRooms[typeName]; |
||||
if ((room == null)) { |
||||
return; |
||||
} |
||||
|
||||
if ((room.dom == null) && (rid != null)) { |
||||
room.dom = document.createElement('div'); |
||||
room.dom.classList.add('room-container'); |
||||
const contentAsFunc = content => () => content; |
||||
|
||||
room.template = Blaze._TemplateWith({ _id: rid }, contentAsFunc(Template.room)); |
||||
Blaze.render(room.template, room.dom); //, nextNode, parentView
|
||||
} |
||||
|
||||
return room.dom; |
||||
} |
||||
|
||||
close(typeName) { |
||||
if (openedRooms[typeName]) { |
||||
if (openedRooms[typeName].rid != null) { |
||||
msgStream.removeAllListeners(openedRooms[typeName].rid); |
||||
RocketChat.Notifications.unRoom(openedRooms[typeName].rid, 'deleteMessage', onDeleteMessageStream); // eslint-disable-line no-use-before-define
|
||||
} |
||||
|
||||
openedRooms[typeName].ready = false; |
||||
openedRooms[typeName].active = false; |
||||
if (openedRooms[typeName].template != null) { |
||||
Blaze.remove(openedRooms[typeName].template); |
||||
} |
||||
delete openedRooms[typeName].dom; |
||||
delete openedRooms[typeName].template; |
||||
|
||||
const { rid } = openedRooms[typeName]; |
||||
delete openedRooms[typeName]; |
||||
|
||||
if (rid != null) { |
||||
return RoomHistoryManager.clear(rid); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
closeOlderRooms() { |
||||
const maxRoomsOpen = 10; |
||||
if (Object.keys(openedRooms).length <= maxRoomsOpen) { |
||||
return; |
||||
} |
||||
|
||||
const roomsToClose = _.sortBy(_.values(openedRooms), 'lastSeen').reverse().slice(maxRoomsOpen); |
||||
return Array.from(roomsToClose).map((roomToClose) => |
||||
this.close(roomToClose.typeName)); |
||||
} |
||||
|
||||
|
||||
closeAllRooms() { |
||||
Object.keys(openedRooms).forEach(key => { |
||||
const openedRoom = openedRooms[key]; |
||||
this.close(openedRoom.typeName); |
||||
}); |
||||
} |
||||
|
||||
|
||||
open(typeName) { |
||||
if ((openedRooms[typeName] == null)) { |
||||
openedRooms[typeName] = { |
||||
typeName, |
||||
active: false, |
||||
ready: false, |
||||
unreadSince: new ReactiveVar(undefined) |
||||
}; |
||||
} |
||||
|
||||
openedRooms[typeName].lastSeen = new Date; |
||||
|
||||
if (openedRooms[typeName].ready) { |
||||
this.closeOlderRooms(); |
||||
} |
||||
|
||||
if (CachedChatSubscription.ready.get() === true) { |
||||
|
||||
if (openedRooms[typeName].active !== true) { |
||||
openedRooms[typeName].active = true; |
||||
if (this.computation) { |
||||
this.computation.invalidate(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
return { |
||||
ready() { |
||||
Dep.depend(); |
||||
return openedRooms[typeName].ready; |
||||
} |
||||
}; |
||||
} |
||||
|
||||
existsDomOfRoom(typeName) { |
||||
const room = openedRooms[typeName]; |
||||
return ((room != null ? room.dom : undefined) != null); |
||||
} |
||||
|
||||
updateUserStatus(user, status, utcOffset) { |
||||
const onlineUsersValue = onlineUsers.curValue; |
||||
|
||||
if (status === 'offline') { |
||||
delete onlineUsersValue[user.username]; |
||||
} else { |
||||
onlineUsersValue[user.username] = { |
||||
_id: user._id, |
||||
status, |
||||
utcOffset |
||||
}; |
||||
} |
||||
|
||||
return onlineUsers.set(onlineUsersValue); |
||||
} |
||||
|
||||
updateMentionsMarksOfRoom(typeName) { |
||||
const dom = this.getDomOfRoom(typeName); |
||||
if ((dom == null)) { |
||||
return; |
||||
} |
||||
|
||||
const ticksBar = $(dom).find('.ticks-bar'); |
||||
$(dom).find('.ticks-bar > .tick').remove(); |
||||
|
||||
const scrollTop = $(dom).find('.messages-box > .wrapper').scrollTop() - 50; |
||||
const totalHeight = $(dom).find('.messages-box > .wrapper > ul').height() + 40; |
||||
|
||||
return $('.messages-box .mention-link-me').each(function(index, item) { |
||||
const topOffset = $(item).offset().top + scrollTop; |
||||
const percent = (100 / totalHeight) * topOffset; |
||||
if ($(item).hasClass('mention-link-all')) { |
||||
return ticksBar.append(`<div class="tick background-attention-color" style="top: ${ percent }%;"></div>`); |
||||
} else { |
||||
return ticksBar.append(`<div class="tick background-primary-action-color" style="top: ${ percent }%;"></div>`); |
||||
} |
||||
}); |
||||
} |
||||
}; |
||||
Cls.initClass(); |
||||
return new Cls; |
||||
}; |
||||
|
||||
const loadMissedMessages = function(rid) { |
||||
const lastMessage = ChatMessage.findOne({rid}, {sort: {ts: -1}, limit: 1}); |
||||
if (lastMessage == null) { |
||||
return; |
||||
} |
||||
|
||||
return Meteor.call('loadMissedMessages', rid, lastMessage.ts, (err, result) => |
||||
Array.from(result).map((item) => |
||||
RocketChat.promises.run('onClientMessageReceived', item).then(function(item) { |
||||
/* globals UserRoles RoomRoles*/ |
||||
const roles = [ |
||||
(item.u && item.u._id && UserRoles.findOne(item.u._id)) || {}, |
||||
(item.u && item.u._id && RoomRoles.findOne({rid: item.rid, 'u._id': item.u._id})) || {} |
||||
].map(({roles}) => roles); |
||||
item.roles = _.union.apply(_, roles); |
||||
return ChatMessage.upsert({_id: item._id}, item); |
||||
})) |
||||
); |
||||
}; |
||||
|
||||
let connectionWasOnline = true; |
||||
Tracker.autorun(function() { |
||||
const { connected } = Meteor.connection.status(); |
||||
|
||||
if (connected === true && connectionWasOnline === false && RoomManager.openedRooms != null) { |
||||
Object.keys(RoomManager.openedRooms).forEach(key => { |
||||
const value = RoomManager.openedRooms[key]; |
||||
if (value.rid != null) { |
||||
loadMissedMessages(value.rid); |
||||
} |
||||
}); |
||||
} |
||||
return connectionWasOnline = connected; |
||||
}); |
||||
|
||||
// Reload rooms after login
|
||||
let currentUsername = undefined; |
||||
Tracker.autorun(() => { |
||||
const user = Meteor.user(); |
||||
if ((currentUsername === undefined) && ((user != null ? user.username : undefined) != null)) { |
||||
currentUsername = user.username; |
||||
RoomManager.closeAllRooms(); |
||||
return FlowRouter._current.route.callAction(FlowRouter._current); |
||||
} |
||||
}); |
||||
|
||||
Meteor.startup(() => |
||||
ChatMessage.find().observe({ |
||||
removed(record) { |
||||
if (RoomManager.getOpenedRoomByRid(record.rid) != null) { |
||||
const recordBefore = ChatMessage.findOne({ts: {$lt: record.ts}}, {sort: {ts: -1}}); |
||||
if (recordBefore != null) { |
||||
ChatMessage.update({_id: recordBefore._id}, {$set: {tick: new Date}}); |
||||
} |
||||
|
||||
const recordAfter = ChatMessage.findOne({ts: {$gt: record.ts}}, {sort: {ts: 1}}); |
||||
if (recordAfter != null) { |
||||
return ChatMessage.update({_id: recordAfter._id}, {$set: {tick: new Date}}); |
||||
} |
||||
} |
||||
} |
||||
}) |
||||
); |
||||
|
||||
|
||||
const onDeleteMessageStream = msg => ChatMessage.remove({_id: msg._id}); |
||||
|
||||
|
||||
Tracker.autorun(function() { |
||||
if (Meteor.userId()) { |
||||
return RocketChat.Notifications.onUser('message', function(msg) { |
||||
msg.u = |
||||
{username: 'rocket.cat'}; |
||||
msg.private = true; |
||||
|
||||
return ChatMessage.upsert({ _id: msg._id }, msg); |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
export { RoomManager }; |
||||
this.RoomManager = RoomManager; |
||||
RocketChat.callbacks.add('afterLogoutCleanUp', () => RoomManager.closeAllRooms(), RocketChat.callbacks.priority.MEDIUM, 'roommanager-after-logout-cleanup'); |
||||
@ -1,112 +0,0 @@ |
||||
RocketChat.Login = (-> |
||||
onClick = (el) -> |
||||
$el = $(el) |
||||
if $el.length |
||||
$el.addClass "active" |
||||
$el.find("input").focus() |
||||
onBlur = (input) -> |
||||
$input = $(input) |
||||
if $input.length |
||||
if input.value == "" |
||||
$input.parents(".input-text").removeClass "active" |
||||
check = (form) -> |
||||
$form = $(form) |
||||
if $form.length |
||||
inputs = $form.find("input") |
||||
inputs.each -> |
||||
if @.value != "" |
||||
console.log @.value |
||||
$(@).parents(".input-text").addClass "active" |
||||
check: check |
||||
onClick: onClick |
||||
onBlur: onBlur |
||||
)() |
||||
|
||||
RocketChat.Button = (-> |
||||
time = undefined |
||||
loading = (el) -> |
||||
$el = $(el) |
||||
next = el.attr("data-loading-text") |
||||
html = el.find("span").html() |
||||
el.addClass("-progress").attr("data-def-text",html).find("span").html(next) |
||||
time = setTimeout -> |
||||
el.addClass("going") |
||||
, 1 |
||||
done = (el) -> |
||||
$el = $(el) |
||||
el.addClass("done") |
||||
reset = (el) -> |
||||
clearTimeout(time) if time |
||||
$el = $(el) |
||||
html= $el.attr("data-def-text") |
||||
$el.find("span").html(html) if html |
||||
$el.removeClass("-progress going done") |
||||
done: done |
||||
loading: loading |
||||
reset: reset |
||||
)() |
||||
|
||||
RocketChat.animationSupport = -> |
||||
animeEnd = |
||||
WebkitAnimation: "webkitAnimationEnd" |
||||
OAnimation: "oAnimationEnd" |
||||
msAnimation: "MSAnimationEnd" |
||||
animation: "animationend" |
||||
|
||||
transEndEventNames = |
||||
WebkitTransition: "webkitTransitionEnd" |
||||
MozTransition: "transitionend" |
||||
OTransition: "oTransitionEnd otransitionend" |
||||
msTransition: "MSTransitionEnd" |
||||
transition: "transitionend" |
||||
prefixB = transEndEventNames[Modernizr.prefixed("transition")] |
||||
prefixA = animeEnd[Modernizr.prefixed("animation")] |
||||
support = Modernizr.cssanimations |
||||
support: support |
||||
animation: prefixA |
||||
transition: prefixB |
||||
|
||||
RocketChat.animeBack = (el, callback, type) -> |
||||
el = $(el) |
||||
if not el.length > 0 |
||||
callback el if callback |
||||
return |
||||
s = animationSupport() |
||||
p = ((if type then s.animation else s.transition)) |
||||
el.one p, (e) -> |
||||
|
||||
#el.off(p); |
||||
callback e |
||||
return |
||||
|
||||
return |
||||
|
||||
RocketChat.preLoadImgs = (urls, callback) -> |
||||
L_ = (x) -> |
||||
if x.width > 0 |
||||
$(x).addClass("loaded").removeClass "loading" |
||||
loaded = $(".loaded", preLoader) |
||||
if loaded.length is urls.length and not ended |
||||
ended = 1 |
||||
imgs = preLoader.children() |
||||
callback imgs |
||||
preLoader.remove() |
||||
return |
||||
im = new Array() |
||||
preLoader = $("<div/>").attr(id: "perverter-preloader") |
||||
loaded = undefined |
||||
ended = undefined |
||||
i = 0 |
||||
|
||||
while i < urls.length |
||||
im[i] = new Image() |
||||
im[i].onload = -> |
||||
L_ this |
||||
return |
||||
|
||||
$(im[i]).appendTo(preLoader).addClass "loading" |
||||
im[i].src = urls[i] |
||||
L_ im[i] if im[i].width > 0 |
||||
i++ |
||||
|
||||
return |
||||
@ -0,0 +1,121 @@ |
||||
/* globals Modernizr */ |
||||
RocketChat.Login = (function() { |
||||
function onClick(el) { |
||||
const $el = $(el); |
||||
if ($el.length) { |
||||
$el.addClass('active'); |
||||
return $el.find('input').focus(); |
||||
} |
||||
} |
||||
function onBlur(input) { |
||||
const $input = $(input); |
||||
if ($input.length) { |
||||
if (input.value === '') { |
||||
return $input.parents('.input-text').removeClass('active'); |
||||
} |
||||
} |
||||
} |
||||
function check(form) { |
||||
const $form = $(form); |
||||
if ($form.length) { |
||||
const inputs = $form.find('input'); |
||||
return inputs.each(function() { |
||||
if (this.value !== '') { |
||||
console.log(this.value); |
||||
return $(this).parents('.input-text').addClass('active'); |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
return { check, onClick, onBlur }; |
||||
}()); |
||||
|
||||
RocketChat.Button = (function() { |
||||
let time = undefined; |
||||
const loading = function(el) { |
||||
const next = el.attr('data-loading-text'); |
||||
const html = el.find('span').html(); |
||||
el.addClass('-progress').attr('data-def-text', html).find('span').html(next); |
||||
return time = setTimeout(() => el.addClass('going'), 1); |
||||
}; |
||||
const done = function(el) { |
||||
return el.addClass('done'); |
||||
}; |
||||
const reset = function(el) { |
||||
if (time) { clearTimeout(time); } |
||||
const $el = $(el); |
||||
const html= $el.attr('data-def-text'); |
||||
if (html) { $el.find('span').html(html); } |
||||
return $el.removeClass('-progress going done'); |
||||
}; |
||||
return { done, loading, reset }; |
||||
}()); |
||||
|
||||
RocketChat.animationSupport = function() { |
||||
const animeEnd = { |
||||
WebkitAnimation: 'webkitAnimationEnd', |
||||
OAnimation: 'oAnimationEnd', |
||||
msAnimation: 'MSAnimationEnd', |
||||
animation: 'animationend' |
||||
}; |
||||
|
||||
const transEndEventNames = { |
||||
WebkitTransition: 'webkitTransitionEnd', |
||||
MozTransition: 'transitionend', |
||||
OTransition: 'oTransitionEnd otransitionend', |
||||
msTransition: 'MSTransitionEnd', |
||||
transition: 'transitionend' |
||||
}; |
||||
const prefixB = transEndEventNames[Modernizr.prefixed('transition')]; |
||||
const prefixA = animeEnd[Modernizr.prefixed('animation')]; |
||||
const support = Modernizr.cssanimations; |
||||
return { |
||||
support, |
||||
animation: prefixA, |
||||
transition: prefixB |
||||
}; |
||||
}; |
||||
|
||||
RocketChat.animeBack = function(e, callback, type) { |
||||
const el = $(e); |
||||
if (!el.length > 0) { |
||||
if (callback) { callback(el); } |
||||
return; |
||||
} |
||||
const s = RocketChat.animationSupport(); |
||||
const p = ((type ? s.animation : s.transition)); |
||||
el.one(p, function(e) { |
||||
|
||||
//el.off(p);
|
||||
callback(e); |
||||
}); |
||||
|
||||
}; |
||||
|
||||
RocketChat.preLoadImgs = function(urls, callback) { |
||||
const preLoader = $('<div/>').attr({id: 'perverter-preloader'}); |
||||
let ended = undefined; |
||||
const l_ = function(x) { |
||||
if (x.width > 0) { |
||||
$(x).addClass('loaded').removeClass('loading'); |
||||
const loaded = $('.loaded', preLoader); |
||||
if ((loaded.length === urls.length) && !ended) { |
||||
ended = 1; |
||||
const imgs = preLoader.children(); |
||||
callback(imgs); |
||||
preLoader.remove(); |
||||
} |
||||
} |
||||
}; |
||||
return urls.map(url => { |
||||
const im = new Image(); |
||||
im.onload = function() { |
||||
l_(this); |
||||
}; |
||||
$(im).appendTo(preLoader).addClass('loading'); |
||||
im.src = url; |
||||
if (im.width > 0) { l_(im); } |
||||
return im; |
||||
}); |
||||
|
||||
}; |
||||
Loading…
Reference in new issue