<commit_before>/**
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * 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.
 */

goog.provide('org.apache.flex.utils.Language');

/**
 * @constructor
 */
org.apache.flex.utils.Language = function() {
};

/**
 * Metadata
 *
 * @type {Object.<string, Array.<Object>>}
 */
org.apache.flex.utils.Language.prototype.FLEXJS_CLASS_INFO =
    { names: [{ name: 'Language',
                qName: 'org.apache.flex.utils.Language'}] };

/**
 * as()
 *
 * @expose
 * @param {?} leftOperand The lefthand operand of the
 *                        binary as operator in AS3.
 * @param {?} rightOperand The righthand operand of the
 *                         binary operator in AS3.
 * @param {?=} opt_coercion The cast is a coercion,
 *                          throw expception if it fails.
 * @return {?} Returns the lefthand operand if it is of the
 *             type of the righthand operand, otherwise null.
 */
org.apache.flex.utils.Language.as = function(leftOperand, rightOperand, opt_coercion) {
  var error, itIs, message;

  opt_coercion = (opt_coercion !== undefined) ? opt_coercion : false;

  itIs = org.apache.flex.utils.Language.is(leftOperand, rightOperand);

  if (!itIs && opt_coercion) {
    message = 'Type Coercion failed';
    if (TypeError) {
      error = new TypeError(message);
    } else {
      error = new Error(message);
    }
    throw error;
  }

  return (itIs) ? leftOperand : null;
};

/**
 * int()
 *
 * @expose
 * @param {?} value The value to be cast.
 * @return {number}
 */
org.apache.flex.utils.Language._int = function(value) {
  return value >> 0;
};

/**
 * is()
 *
 * @expose
 * @param {?} leftOperand The lefthand operand of the
 *     binary as operator in AS3.
 * @param {?} rightOperand The righthand operand of the
 *     binary operator in AS3.
 * @return {boolean}
 */
org.apache.flex.utils.Language.is = function(leftOperand, rightOperand) {
  var checkInterfaces, superClass;

  // (erikdebruin) we intentionally DON'T do null checks on the
  //               [class].FLEXJS_CLASS_INFO property, as it MUST be
  //               declared for every FLEXJS JS (framework) class

  if (leftOperand && !rightOperand) {
    return false;
  }

  checkInterfaces = function(left) {
    var i, interfaces;

    interfaces = left.FLEXJS_CLASS_INFO.interfaces;
    for (i = interfaces.length - 1; i > -1; i--) {
      if (interfaces[i] === rightOperand) {
        return true;
      }

      if (interfaces[i].prototype.FLEXJS_CLASS_INFO.interfaces) {
        return checkInterfaces(new interfaces[i]());
      }
    }

    return false;
  };

  if ((rightOperand === String && typeof leftOperand === 'string') ||
      (leftOperand instanceof /** @type {Object} */(rightOperand))) {
    return true;
  }

  if (leftOperand.FLEXJS_CLASS_INFO.interfaces) {
    if (checkInterfaces(leftOperand)) {
      return true;
    }
  }

  superClass = leftOperand.constructor.superClass_;
  if (superClass) {
    while (superClass && superClass.FLEXJS_CLASS_INFO) {
      if (superClass.FLEXJS_CLASS_INFO.interfaces) {
        if (checkInterfaces(superClass)) {
          return true;
        }
      }
      superClass = superClass.constructor.superClass_;
    }
  }

  return false;
};

/**
 * trace()
 *
 * @expose
 * @param {string=} opt_value The message to be written to the console.
 */
org.apache.flex.utils.Language.trace = function(opt_value) {
  var theConsole;

  opt_value = (opt_value !== undefined) ? opt_value : '';

  theConsole = goog.global.console;

  if (theConsole === undefined && window.console !== undefined)
    theConsole = window.console;

  try {
    if (theConsole && theConsole.log) {
      theConsole.log(opt_value);
    }
  } catch (e) {
    // ignore; at least we tried ;-)
  }
};

/**
 * uint()
 *
 * @expose
 * @param {?} value The value to be cast.
 * @return {number}
 */
org.apache.flex.utils.Language.uint = function(value) {
  return value >>> 0;
};
<commit_msg>allow Number in is/as checks<commit_after>/**
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * 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.
 */

goog.provide('org.apache.flex.utils.Language');

/**
 * @constructor
 */
org.apache.flex.utils.Language = function() {
};

/**
 * Metadata
 *
 * @type {Object.<string, Array.<Object>>}
 */
org.apache.flex.utils.Language.prototype.FLEXJS_CLASS_INFO =
    { names: [{ name: 'Language',
                qName: 'org.apache.flex.utils.Language'}] };

/**
 * as()
 *
 * @expose
 * @param {?} leftOperand The lefthand operand of the
 *                        binary as operator in AS3.
 * @param {?} rightOperand The righthand operand of the
 *                         binary operator in AS3.
 * @param {?=} opt_coercion The cast is a coercion,
 *                          throw expception if it fails.
 * @return {?} Returns the lefthand operand if it is of the
 *             type of the righthand operand, otherwise null.
 */
org.apache.flex.utils.Language.as = function(leftOperand, rightOperand, opt_coercion) {
  var error, itIs, message;

  opt_coercion = (opt_coercion !== undefined) ? opt_coercion : false;

  itIs = org.apache.flex.utils.Language.is(leftOperand, rightOperand);

  if (!itIs && opt_coercion) {
    message = 'Type Coercion failed';
    if (TypeError) {
      error = new TypeError(message);
    } else {
      error = new Error(message);
    }
    throw error;
  }

  return (itIs) ? leftOperand : null;
};

/**
 * int()
 *
 * @expose
 * @param {?} value The value to be cast.
 * @return {number}
 */
org.apache.flex.utils.Language._int = function(value) {
  return value >> 0;
};

/**
 * is()
 *
 * @expose
 * @param {?} leftOperand The lefthand operand of the
 *     binary as operator in AS3.
 * @param {?} rightOperand The righthand operand of the
 *     binary operator in AS3.
 * @return {boolean}
 */
org.apache.flex.utils.Language.is = function(leftOperand, rightOperand) {
  var checkInterfaces, superClass;

  // (erikdebruin) we intentionally DON'T do null checks on the
  //               [class].FLEXJS_CLASS_INFO property, as it MUST be
  //               declared for every FLEXJS JS (framework) class

  if (leftOperand && !rightOperand) {
    return false;
  }

  checkInterfaces = function(left) {
    var i, interfaces;

    interfaces = left.FLEXJS_CLASS_INFO.interfaces;
    for (i = interfaces.length - 1; i > -1; i--) {
      if (interfaces[i] === rightOperand) {
        return true;
      }

      if (interfaces[i].prototype.FLEXJS_CLASS_INFO.interfaces) {
        return checkInterfaces(new interfaces[i]());
      }
    }

    return false;
  };

  if ((rightOperand === String && typeof leftOperand === 'string') ||
      (leftOperand instanceof /** @type {Object} */(rightOperand))) {
    return true;
  }
  if ((rightOperand === Number && typeof leftOperand === 'number') ||
    (leftOperand instanceof /** @type {Object} */(rightOperand))) {
    return true;
  }

  if (leftOperand.FLEXJS_CLASS_INFO.interfaces) {
    if (checkInterfaces(leftOperand)) {
      return true;
    }
  }

  superClass = leftOperand.constructor.superClass_;
  if (superClass) {
    while (superClass && superClass.FLEXJS_CLASS_INFO) {
      if (superClass.FLEXJS_CLASS_INFO.interfaces) {
        if (checkInterfaces(superClass)) {
          return true;
        }
      }
      superClass = superClass.constructor.superClass_;
    }
  }

  return false;
};

/**
 * trace()
 *
 * @expose
 * @param {string=} opt_value The message to be written to the console.
 */
org.apache.flex.utils.Language.trace = function(opt_value) {
  var theConsole;

  opt_value = (opt_value !== undefined) ? opt_value : '';

  theConsole = goog.global.console;

  if (theConsole === undefined && window.console !== undefined)
    theConsole = window.console;

  try {
    if (theConsole && theConsole.log) {
      theConsole.log(opt_value);
    }
  } catch (e) {
    // ignore; at least we tried ;-)
  }
};

/**
 * uint()
 *
 * @expose
 * @param {?} value The value to be cast.
 * @return {number}
 */
org.apache.flex.utils.Language.uint = function(value) {
  return value >>> 0;
};
<|endoftext|>


<commit_before>var ChatServerActionCreators = require('../actions/ChatServerActionCreators');
var XMPP = require('./xmpp.js');
var ChatMessageUtils = require('./ChatMessageUtils');
var urlUtils = require('./urlUtils');

var _connection, // raw connection
    _connectResolve,
    _connectionStatus, _domain, _me, _myJID, _nick;
var _pendingFriends = [];

// TODO I think this state is maybe in the wrong place
// E.g. if we have a ContactStore, it would request history and profile
// to be fetched when a contact is first added?
var historyLoadedFor = [];
var userProfileLoadedFor = [];
//

var sentIsTyping = {};

// Strophe.log = function (level, msg) {
//     console.log(msg);
// };

var connect = new Promise((resolve, reject) => {
    _connectResolve = resolve;
});

function onConnectFactory (resolve) {
    return function (status_code) {
        _connectionStatus = status_code;
        if (status_code == Strophe.Status.CONNECTED) {
            sendInitialPresence();
	    addPrivateChatHandlers();
            _connection.mam.init(_connection);
            _connection.addHandler(
                receiveArchivedPrivateMessage, Strophe.NS.MAM, "message", null);
            getContacts();
            _connection.vcard.init(_connection);
            loadUserProfile(Strophe.getNodeFromJid(_myJID));
            _connection.chatstates.init(_connection);
            if (chatConf.hasChatroom) {
                _connection.muc.init(_connection);
	      addGroupChatHandlers();
            }
            if (_pendingFriends.length) {
                _pendingFriends.forEach(f => addFriend(f));
            }
            resolve(_connection);
            ChatServerActionCreators.connect(_connection);
        } else if (status_code == Strophe.Status.DISCONNECTED) {
            ChatServerActionCreators.disconnect();
        }
    }
};

function sendInitialPresence () {
    _connection.send($pres().tree());
}

var getChatrooms = new Promise((resolve, reject) => {
    // TODO not sure here about using a promise inside a promise
    // TODO fix this hack to workaround problems joining the main chatroom
    // when viewing private messages. For now we don't join the chatroom
    // as private and group threads are intermingled in the thread store
    // and that breaks thinsgs.
    if (window.location.href.indexOf('messages') === -1 &&
        window.location.href.indexOf('sessions') === -1) {
        connect.then(connection => {
            var chatDomain = 'muc.' + _domain; // TODO pass in from config
            connection.muc.listRooms(
                chatDomain,
                receiveChatrooms(resolve),
                reject
            )
        });
    }
});

// TODO is there a better pattern to use here?
// Want getChatrooms to be a promise but want .then funcs to receive the parsed roomList
// not the raw XMPP result. Seems I'm going to be writing a lot of factory functions to
// return functions that call `resolve`...
function receiveChatrooms (resolve) {
    return function (result) {
        console.log('got chatrooms');
        var roomList = XMPP.parse.roomList(result);
        resolve(roomList);
	ChatServerActionCreators.receiveChatrooms(roomList);
    }
};

getChatrooms.then(roomList => {
    if (roomList.length) {
	// TODO something better than these path inspection hacks, pass some explicit option?
	if (window.location.href.indexOf('chatroom') === -1) {
            // Anywhere outside of /chatrooms/ we just want to join the 'main' site chatroom
            // TODO should be explicit about main chatroom instead of relying on roomList[0]
            joinChatroom(roomList[0]);
	}
    }
});

function joinChatroom (roomJID) {
    if (!chatConf.hasChatroom) {
        return;
    }
    Promise.all([
        connect,  // TODO not sure this is need as getChatrooms already depends on it
        getChatrooms
    ]).then(result => {
        var [connection, roomList] = result;
        if (roomList.indexOf(roomJID) === -1) {
            console.log('No such room', roomList);
            return;
        }
        // TODO this isn't quite the right place, should probably do
        // when get presence back confirming room join
        ChatServerActionCreators.joinChatroom(roomJID);
        connection.muc.join(
            roomJID,
            _nick
        );
    });
}

function receiveGroupMessage (msg) {
    var message = XMPP.parse.groupMessage(msg);
    // TODO not sure about treating a group message as just another message. Makes some sense, as it's just another thread
    message.threadID = message.to;
    message.authorName = message.from;
    if (message.from) {
	// TODO always get an 'empty' message from the room
	// itself, not sure why
        ChatServerActionCreators.receivePrivateMessage(message);
    }
    return true;
};

function receiveGroupPresence (pres) {
    var presence = XMPP.parse.groupPresence(pres);
    if (presence.added) {
      ChatServerActionCreators.receiveOnline(presence.user, presence.room);
      loadUserProfile(presence.user);
    } else if (presence.removed) {
        ChatServerActionCreators.receiveOffline(presence.user, presence.room);
        if (presence.destroyed) {
            ChatServerActionCreators.leaveChatroom(presence.room);
        }
    }
    // TODO ChatServerActionCreators.joinChatroom
    return true;
}

function addPrivateChatHandlers () {
    _connection.addHandler(receivePrivateMessage, null, 'message', 'chat',  null);
    _connection.addHandler(receivePresence, null, 'presence', null,  null);
}

function addGroupChatHandlers() {
  _connection.addHandler(receiveGroupMessage, null, 'message', 'groupchat', null);
  _connection.addHandler(receiveGroupPresence, null, 'presence', null, null);
}

function getContacts () {
    _connection.roster.init(_connection);
    _connection.roster.registerRequestCallback(acceptFriendRequest);
    _connection.roster.registerCallback(handleContacts);
    _connection.roster.get();
}

function addFriend(friend) {
    if (_connectionStatus === Strophe.Status.CONNECTED) {
        _connection.roster.subscribe(getBareJIDForNode(friend));
    } else {
        _pendingFriends.push(friend);
    }
}

function acceptFriendRequest (from) {
    _connection.roster.authorize(from);
    return true;
}

function handleContacts (roster, item) {
    // var friends = [];
    roster.forEach((friend, i) => {
	var username = Strophe.getNodeFromJid(friend.jid);
	if (friend.subscription !== 'none') {
          loadPrivateChatHistory(friend.jid, "");
	    //     friends.push(username);
	    loadUserProfile(username);
	}
    });
    return true;
}

function loadPrivateChatHistory (contact, before) {
    if (historyLoadedFor.indexOf(contact) === -1 || before) {
        historyLoadedFor.push(contact);
      _connection.mam.query(
            Strophe.getBareJidFromJid(_myJID),
            {
                'with': contact,
              'before': before,
                'max': 20,
                // TODO strophe.mam is a bit broken.
                // It adds onMessage as handler for all received messages so
                // if you send off multiple queries in parallel the handler is
                // called several times. To workaround we add receiveArchivedPrivateMessage
                // once above and pass a noop here.
                // TODO remove the noop otherwise we're adding one each time we do a query
              onMessage: function () { },
              onComplete: msg => {
                let first = XMPP.parse.RSM(msg);
                if (first) {
                  loadPrivateChatHistory(contact, first);
                }
              }
            }
      );
    }
}

function loadUserProfile (user) {
    var jid = getBareJIDForNode(user);
    if (userProfileLoadedFor.indexOf(user) === -1) {
	userProfileLoadedFor.push(user);
        _connection.vcard.get(
	  vcard => {
                var userProfile = XMPP.parse.vCard(vcard);
                userProfile.user = user;
                ChatServerActionCreators.receiveUserProfile(userProfile);
	    },
	  jid,
          () => {
            changeAvatar('sunshine');
            // Assume vcard.set will work and update the UI
            ChatServerActionCreators.receiveUserProfile({
              role: chatConf.role,
              avatar: 'sunshine'
            });
          }
        );
    }
}

function receivePrivateMessage (msg) {
    var message = XMPP.parse.privateMessage(msg);
    if (message.composing.length) {
	ChatServerActionCreators.receiveStartTyping(message.from, message.threadID);
    } else {
	if (message.active.length) {
	    ChatServerActionCreators.receiveStopTyping(message.from, message.threadID);
	}
	if (message.ended.length) {
	    ChatServerActionCreators.receiveEndThread(message.threadID, null);
	}
	if (message.text) {
            ChatServerActionCreators.receivePrivateMessage(message);
	}
    }
    return true;
}

var receiveArchivedPrivateMessage = function (msg) {
    var message = XMPP.parse.archivedPrivateMessage(msg);
    if (message) { // TODO don't need if when not querying group messages
	message.isRead = true;
      ChatServerActionCreators.receivePrivateMessage(message, true);
	if (message.ended.length) {
	    ChatServerActionCreators.receiveEndThread(message.threadID);
            // TODO not sure where this call belongs
            getSessionRating(
                message.threadID,
                // TODO use Promise here?
                rating => ChatServerActionCreators.receiveSessionRating(
                    message.threadID,
                    // TODO prob getSession should do this unpacking?
                    rating.rating
                )
            )
	}
    }
    return true;
};

function receivePresence (pres) {
    var status = XMPP.parse.presence(pres);
    ChatServerActionCreators.receiveChatStatus(status);
    return true;
};

function getSessionRating (threadID, callback) {
    $.get(urlUtils.getSessionRating(threadID)).done(callback);
}

function setupLogging () {
    _connection.rawInput = function (data) { console.log('RECV: ', data); };
    _connection.rawOutput = function (data) { console.log('SENT: ', data); };
}

function getBareJIDForNode (node) {
    return node + '@' + _domain;
};

function changeAvatar(avatarName) {
  // TODO no convenience function provided for making vcards?
  var role = Strophe.xmlElement('ROLE');
  role.appendChild(Strophe.xmlTextNode(chatConf.role));

  var photo = Strophe.xmlElement('PHOTO');
  photo.appendChild(Strophe.xmlTextNode(avatarName));  // TODO prob make this full URI of avatar?
  // TODO looks like strophe.vcard doesn't allow setting multiple elements?
  // (sort of doesn't matter cos the data you set isn't validated, which is ok
  // while we assume no other clients will connect)
  var vcard = Strophe.xmlElement('XXX');
  vcard.appendChild(role);
  vcard.appendChild(photo);

  // TODO handle error
  _connection.vcard.set(
    function (r) { },  // TODO handle something here?
    vcard
  );
}

module.exports = {

    connect: function (server, jid, password, nick, log=false) {
	_myJID = jid;
        _nick = nick;
	_domain = Strophe.getDomainFromJid(jid);
	_me = Strophe.getNodeFromJid(jid);

        _connection = new Strophe.Connection('ws://' + server + ':5280/ws-xmpp');
        if (log) {
            setupLogging();
        }
        _connection.connect(
            jid,
	    password,
            onConnectFactory(_connectResolve)
        );
    },

    getAllMessages: function() {
        // simulate retrieving data from a database
        var rawMessages = JSON.parse(localStorage.getItem('messages'));

        // simulate success callback
        ChatServerActionCreators.receiveAll(rawMessages);
    },

    createMessage: function(message, threadID) {
        var roomJID;
        if (message.threadID.indexOf(':') === -1) {
            roomJID = message.threadID + '@muc.' + _domain; // TODO fix hardcoded chat domain
            _connection.muc.groupchat(
                roomJID,
                message.text
            )
        } else {
	    var to = ChatMessageUtils.getMessageOther(message.threadID);
	    var payload = XMPP.create.privateMessage(
	        message.text,
	        _myJID,
	        getBareJIDForNode(to)
	    );
	    _connection.chatstates.addActive(payload);

            payload.c('thread').t(message.threadID);

	    delete sentIsTyping[threadID];
	    _connection.send(payload.tree()); // TODO handle error on message submit
        }
    },

    setStatus: function (code, message) {
	var pres = $pres();
	if (code) {
	    pres.c('show').t(code).up();
	}
	if (message) {
	    pres.c('status').t(message);
	}
	_connection.send(pres.tree());
    },

    // TODO only send this if other person is online? what's spec say?
    startTyping: function (threadID) {
	var to = ChatMessageUtils.getMessageOther(threadID);
	if (!sentIsTyping[threadID]) {
	    sentIsTyping[threadID] = true;
	    _connection.chatstates.sendComposing(
	        getBareJIDForNode(to)
	    );
	}
    },

    stopTyping: function (threadID) {
	var to = ChatMessageUtils.getMessageOther(threadID);
	delete sentIsTyping[threadID];
	_connection.chatstates.sendActive(
	    getBareJIDForNode(to)
	);
    },

  changeAvatar: changeAvatar,

    joinChatroom: joinChatroom,

    addFriend: addFriend,

    startSession: function (threadID, participants) {
	var url = urlUtils.startSession();
        $.ajax({
            url: url,
            type: "POST",
            data: JSON.stringify({
		session_id: threadID,
		ratings: [for (p of participants) {user: p}]
	    }),
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            error: () => console.log('startsession failed')
        })
    },

    rateThread (threadID, rating) {
	var url = urlUtils.rateSession(threadID);
        $.ajax({
            url: url,
            type: "PUT",
            data: JSON.stringify({
		rating: rating
	    }),
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            error: () => console.log('rate session failed')
        })
    },

    endThread: function (threadID) {
	var to = ChatMessageUtils.getMessageOther(threadID);
	var payload = $msg({
	    from: _myJID,
	    to: getBareJIDForNode(to),
	    type: 'chat'
	});
        payload.c('ended').up();
        payload.c('thread').t(threadID).up();
	// Add empty body so message gets archived
        payload.c('body').up();
	_connection.send(payload.tree()); // TODO handle error on message submit
    },

};
<commit_msg>Abuse 'archived' flag so we don't 'ping' for new chatroom messages<commit_after>var ChatServerActionCreators = require('../actions/ChatServerActionCreators');
var XMPP = require('./xmpp.js');
var ChatMessageUtils = require('./ChatMessageUtils');
var urlUtils = require('./urlUtils');

var _connection, // raw connection
    _connectResolve,
    _connectionStatus, _domain, _me, _myJID, _nick;
var _pendingFriends = [];

// TODO I think this state is maybe in the wrong place
// E.g. if we have a ContactStore, it would request history and profile
// to be fetched when a contact is first added?
var historyLoadedFor = [];
var userProfileLoadedFor = [];
//

var sentIsTyping = {};

// Strophe.log = function (level, msg) {
//     console.log(msg);
// };

var connect = new Promise((resolve, reject) => {
    _connectResolve = resolve;
});

function onConnectFactory (resolve) {
    return function (status_code) {
        _connectionStatus = status_code;
        if (status_code == Strophe.Status.CONNECTED) {
            sendInitialPresence();
	    addPrivateChatHandlers();
            _connection.mam.init(_connection);
            _connection.addHandler(
                receiveArchivedPrivateMessage, Strophe.NS.MAM, "message", null);
            getContacts();
            _connection.vcard.init(_connection);
            loadUserProfile(Strophe.getNodeFromJid(_myJID));
            _connection.chatstates.init(_connection);
            if (chatConf.hasChatroom) {
                _connection.muc.init(_connection);
	      addGroupChatHandlers();
            }
            if (_pendingFriends.length) {
                _pendingFriends.forEach(f => addFriend(f));
            }
            resolve(_connection);
            ChatServerActionCreators.connect(_connection);
        } else if (status_code == Strophe.Status.DISCONNECTED) {
            ChatServerActionCreators.disconnect();
        }
    }
};

function sendInitialPresence () {
    _connection.send($pres().tree());
}

var getChatrooms = new Promise((resolve, reject) => {
    // TODO not sure here about using a promise inside a promise
    // TODO fix this hack to workaround problems joining the main chatroom
    // when viewing private messages. For now we don't join the chatroom
    // as private and group threads are intermingled in the thread store
    // and that breaks thinsgs.
    if (window.location.href.indexOf('messages') === -1 &&
        window.location.href.indexOf('sessions') === -1) {
        connect.then(connection => {
            var chatDomain = 'muc.' + _domain; // TODO pass in from config
            connection.muc.listRooms(
                chatDomain,
                receiveChatrooms(resolve),
                reject
            )
        });
    }
});

// TODO is there a better pattern to use here?
// Want getChatrooms to be a promise but want .then funcs to receive the parsed roomList
// not the raw XMPP result. Seems I'm going to be writing a lot of factory functions to
// return functions that call `resolve`...
function receiveChatrooms (resolve) {
    return function (result) {
        console.log('got chatrooms');
        var roomList = XMPP.parse.roomList(result);
        resolve(roomList);
	ChatServerActionCreators.receiveChatrooms(roomList);
    }
};

getChatrooms.then(roomList => {
    if (roomList.length) {
	// TODO something better than these path inspection hacks, pass some explicit option?
	if (window.location.href.indexOf('chatroom') === -1) {
            // Anywhere outside of /chatrooms/ we just want to join the 'main' site chatroom
            // TODO should be explicit about main chatroom instead of relying on roomList[0]
            joinChatroom(roomList[0]);
	}
    }
});

function joinChatroom (roomJID) {
    if (!chatConf.hasChatroom) {
        return;
    }
    Promise.all([
        connect,  // TODO not sure this is need as getChatrooms already depends on it
        getChatrooms
    ]).then(result => {
        var [connection, roomList] = result;
        if (roomList.indexOf(roomJID) === -1) {
            console.log('No such room', roomList);
            return;
        }
        // TODO this isn't quite the right place, should probably do
        // when get presence back confirming room join
        ChatServerActionCreators.joinChatroom(roomJID);
        connection.muc.join(
            roomJID,
            _nick
        );
    });
}

function receiveGroupMessage (msg) {
    var message = XMPP.parse.groupMessage(msg);
    // TODO not sure about treating a group message as just another message. Makes some sense, as it's just another thread
    message.threadID = message.to;
    message.authorName = message.from;
    if (message.from) {
	// TODO always get an 'empty' message from the room
	// itself, not sure why
      ChatServerActionCreators.receivePrivateMessage(message, true);
    }
    return true;
};

function receiveGroupPresence (pres) {
    var presence = XMPP.parse.groupPresence(pres);
    if (presence.added) {
      ChatServerActionCreators.receiveOnline(presence.user, presence.room);
      loadUserProfile(presence.user);
    } else if (presence.removed) {
        ChatServerActionCreators.receiveOffline(presence.user, presence.room);
        if (presence.destroyed) {
            ChatServerActionCreators.leaveChatroom(presence.room);
        }
    }
    // TODO ChatServerActionCreators.joinChatroom
    return true;
}

function addPrivateChatHandlers () {
    _connection.addHandler(receivePrivateMessage, null, 'message', 'chat',  null);
    _connection.addHandler(receivePresence, null, 'presence', null,  null);
}

function addGroupChatHandlers() {
  _connection.addHandler(receiveGroupMessage, null, 'message', 'groupchat', null);
  _connection.addHandler(receiveGroupPresence, null, 'presence', null, null);
}

function getContacts () {
    _connection.roster.init(_connection);
    _connection.roster.registerRequestCallback(acceptFriendRequest);
    _connection.roster.registerCallback(handleContacts);
    _connection.roster.get();
}

function addFriend(friend) {
    if (_connectionStatus === Strophe.Status.CONNECTED) {
        _connection.roster.subscribe(getBareJIDForNode(friend));
    } else {
        _pendingFriends.push(friend);
    }
}

function acceptFriendRequest (from) {
    _connection.roster.authorize(from);
    return true;
}

function handleContacts (roster, item) {
    // var friends = [];
    roster.forEach((friend, i) => {
	var username = Strophe.getNodeFromJid(friend.jid);
	if (friend.subscription !== 'none') {
          loadPrivateChatHistory(friend.jid, "");
	    //     friends.push(username);
	    loadUserProfile(username);
	}
    });
    return true;
}

function loadPrivateChatHistory (contact, before) {
    if (historyLoadedFor.indexOf(contact) === -1 || before) {
        historyLoadedFor.push(contact);
      _connection.mam.query(
            Strophe.getBareJidFromJid(_myJID),
            {
                'with': contact,
              'before': before,
                'max': 20,
                // TODO strophe.mam is a bit broken.
                // It adds onMessage as handler for all received messages so
                // if you send off multiple queries in parallel the handler is
                // called several times. To workaround we add receiveArchivedPrivateMessage
                // once above and pass a noop here.
                // TODO remove the noop otherwise we're adding one each time we do a query
              onMessage: function () { },
              onComplete: msg => {
                let first = XMPP.parse.RSM(msg);
                if (first) {
                  loadPrivateChatHistory(contact, first);
                }
              }
            }
      );
    }
}

function loadUserProfile (user) {
    var jid = getBareJIDForNode(user);
    if (userProfileLoadedFor.indexOf(user) === -1) {
	userProfileLoadedFor.push(user);
        _connection.vcard.get(
	  vcard => {
                var userProfile = XMPP.parse.vCard(vcard);
                userProfile.user = user;
                ChatServerActionCreators.receiveUserProfile(userProfile);
	    },
	  jid,
          () => {
            changeAvatar('sunshine');
            // Assume vcard.set will work and update the UI
            ChatServerActionCreators.receiveUserProfile({
              role: chatConf.role,
              avatar: 'sunshine'
            });
          }
        );
    }
}

function receivePrivateMessage (msg) {
    var message = XMPP.parse.privateMessage(msg);
    if (message.composing.length) {
	ChatServerActionCreators.receiveStartTyping(message.from, message.threadID);
    } else {
	if (message.active.length) {
	    ChatServerActionCreators.receiveStopTyping(message.from, message.threadID);
	}
	if (message.ended.length) {
	    ChatServerActionCreators.receiveEndThread(message.threadID, null);
	}
	if (message.text) {
            ChatServerActionCreators.receivePrivateMessage(message);
	}
    }
    return true;
}

var receiveArchivedPrivateMessage = function (msg) {
    var message = XMPP.parse.archivedPrivateMessage(msg);
    if (message) { // TODO don't need if when not querying group messages
	message.isRead = true;
      ChatServerActionCreators.receivePrivateMessage(message, true);
	if (message.ended.length) {
	    ChatServerActionCreators.receiveEndThread(message.threadID);
            // TODO not sure where this call belongs
            getSessionRating(
                message.threadID,
                // TODO use Promise here?
                rating => ChatServerActionCreators.receiveSessionRating(
                    message.threadID,
                    // TODO prob getSession should do this unpacking?
                    rating.rating
                )
            )
	}
    }
    return true;
};

function receivePresence (pres) {
    var status = XMPP.parse.presence(pres);
    ChatServerActionCreators.receiveChatStatus(status);
    return true;
};

function getSessionRating (threadID, callback) {
    $.get(urlUtils.getSessionRating(threadID)).done(callback);
}

function setupLogging () {
    _connection.rawInput = function (data) { console.log('RECV: ', data); };
    _connection.rawOutput = function (data) { console.log('SENT: ', data); };
}

function getBareJIDForNode (node) {
    return node + '@' + _domain;
};

function changeAvatar(avatarName) {
  // TODO no convenience function provided for making vcards?
  var role = Strophe.xmlElement('ROLE');
  role.appendChild(Strophe.xmlTextNode(chatConf.role));

  var photo = Strophe.xmlElement('PHOTO');
  photo.appendChild(Strophe.xmlTextNode(avatarName));  // TODO prob make this full URI of avatar?
  // TODO looks like strophe.vcard doesn't allow setting multiple elements?
  // (sort of doesn't matter cos the data you set isn't validated, which is ok
  // while we assume no other clients will connect)
  var vcard = Strophe.xmlElement('XXX');
  vcard.appendChild(role);
  vcard.appendChild(photo);

  // TODO handle error
  _connection.vcard.set(
    function (r) { },  // TODO handle something here?
    vcard
  );
}

module.exports = {

    connect: function (server, jid, password, nick, log=false) {
	_myJID = jid;
        _nick = nick;
	_domain = Strophe.getDomainFromJid(jid);
	_me = Strophe.getNodeFromJid(jid);

        _connection = new Strophe.Connection('ws://' + server + ':5280/ws-xmpp');
        if (log) {
            setupLogging();
        }
        _connection.connect(
            jid,
	    password,
            onConnectFactory(_connectResolve)
        );
    },

    getAllMessages: function() {
        // simulate retrieving data from a database
        var rawMessages = JSON.parse(localStorage.getItem('messages'));

        // simulate success callback
        ChatServerActionCreators.receiveAll(rawMessages);
    },

    createMessage: function(message, threadID) {
        var roomJID;
        if (message.threadID.indexOf(':') === -1) {
            roomJID = message.threadID + '@muc.' + _domain; // TODO fix hardcoded chat domain
            _connection.muc.groupchat(
                roomJID,
                message.text
            )
        } else {
	    var to = ChatMessageUtils.getMessageOther(message.threadID);
	    var payload = XMPP.create.privateMessage(
	        message.text,
	        _myJID,
	        getBareJIDForNode(to)
	    );
	    _connection.chatstates.addActive(payload);

            payload.c('thread').t(message.threadID);

	    delete sentIsTyping[threadID];
	    _connection.send(payload.tree()); // TODO handle error on message submit
        }
    },

    setStatus: function (code, message) {
	var pres = $pres();
	if (code) {
	    pres.c('show').t(code).up();
	}
	if (message) {
	    pres.c('status').t(message);
	}
	_connection.send(pres.tree());
    },

    // TODO only send this if other person is online? what's spec say?
    startTyping: function (threadID) {
	var to = ChatMessageUtils.getMessageOther(threadID);
	if (!sentIsTyping[threadID]) {
	    sentIsTyping[threadID] = true;
	    _connection.chatstates.sendComposing(
	        getBareJIDForNode(to)
	    );
	}
    },

    stopTyping: function (threadID) {
	var to = ChatMessageUtils.getMessageOther(threadID);
	delete sentIsTyping[threadID];
	_connection.chatstates.sendActive(
	    getBareJIDForNode(to)
	);
    },

  changeAvatar: changeAvatar,

    joinChatroom: joinChatroom,

    addFriend: addFriend,

    startSession: function (threadID, participants) {
	var url = urlUtils.startSession();
        $.ajax({
            url: url,
            type: "POST",
            data: JSON.stringify({
		session_id: threadID,
		ratings: [for (p of participants) {user: p}]
	    }),
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            error: () => console.log('startsession failed')
        })
    },

    rateThread (threadID, rating) {
	var url = urlUtils.rateSession(threadID);
        $.ajax({
            url: url,
            type: "PUT",
            data: JSON.stringify({
		rating: rating
	    }),
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            error: () => console.log('rate session failed')
        })
    },

    endThread: function (threadID) {
	var to = ChatMessageUtils.getMessageOther(threadID);
	var payload = $msg({
	    from: _myJID,
	    to: getBareJIDForNode(to),
	    type: 'chat'
	});
        payload.c('ended').up();
        payload.c('thread').t(threadID).up();
	// Add empty body so message gets archived
        payload.c('body').up();
	_connection.send(payload.tree()); // TODO handle error on message submit
    },

};
<|endoftext|>


<commit_before>var results           = [];
var paginationResults;

var previousResults = document.getElementById("previousResults");
var nextResults     = document.getElementById("nextResults");

var draggableMarker;

previousResults.addEventListener('click', function() {
    paginationResults -= 1;
    renderResults();
})
nextResults.addEventListener('click', function() {
    paginationResults += 1;
    renderResults();
})

function renderResults() {
    var $proposal = $('#proposal');
    var $proposalMessage = $('#proposal-message');

    var $searchStreet = $('#input-search');
    var $searchMunicipality = $('#input-ward');

    $proposal.empty();
    var features = [];

    var iconIdx = 0;
    for(var i=paginationResults*5, length=results.length; i<length && features.length<5; i++) {
        var address = results[i].address;
        var pos = results[i].point;

        var newAddress = new AddressResult(pos.x, pos.y, address, iconIdx++);
        $proposal.append(newAddress.render());

        // Create feature on vectore layer
        var feature = new OpenLayers.Feature.Vector(
                new OpenLayers.Geometry.Point(pos.x, pos.y),
                { 'additionalInfo' :
                    {
                        'streetName'   : address.street.name,
                        'number'       : address.number,
                        'postCode'     : address.street.postCode,
                        'municipality' : address.street.municipality
                    }
                },
                {
                    externalGraphic: newAddress.iconSrc(),
                    graphicWidth: 25,
                    graphicHeight: 32,
                    graphicYOffset: -32
                }
            );
        features.push(feature);

        // Define message if needed
        if ($searchMunicipality.val()) {
            $proposalMessage.html("Cette rue n'est pas répertoriée dans cette commune");
        } else if ($searchStreet.val().toLowerCase() == address.street.name.toLowerCase()) {
            $proposalMessage.html("Cette rue existe dans plusieurs communes, merci de préciser");
        }
    }

    // Mask/display pagination buttons
    if (paginationResults == 0) {
        previousResults.hidden = true;
    } else {
        previousResults.hidden = false;
    }

    if (results.length >= paginationResults * 5 + 6) {
        nextResults.hidden = false;
    } else {
        nextResults.hidden = true;
    }

    // Add features to layer
    cleanMap();
    fms.currentMap.homepageMarkersLayer.addFeatures(features);

    // Zoom to markers
    var markersBound = fms.currentMap.homepageMarkersLayer.getDataExtent();
    fms.currentMap.map.zoomToExtent(markersBound);

    // Show/hide proposal and message
    $proposalContainer = $('#proposal-container');
    $proposalContainer.slideDown();

    if ($proposalMessage.html()) {
        map.classList.add("map-big-message");
        $proposalMessage.slideDown();
    } else {
        map.classList.remove("map-big-message");
        map.classList.add("map-big");
        $proposalMessage.slideUp();
    }

}
function AddressResult(x, y, address, idx)
{
    var self = this;

    this.x = x;
    this.y = y;
    this.el = document.createElement('li');

    this.address = address;

    this.render = function() {
        this.el.innerHTML = '
' + this.address.street.name +
                            '
' +
                            '' + this.address.street.postCode + ' ' + this.address.street.municipality + '' +
                            '
';
        this.el.addEventListener('click', this.onclick);
        return  this.el;
    };

    this.iconSrc = function() {
        return "/static/images/marker/marker-" + idx + ".png";
    },

    this.onclick = function(event) {
        additionalInfo = {
            'streetName'   : self.address.street.name,
            'number'       : self.address.number,
            'postCode'     : self.address.street.postCode,
            'municipality' : self.address.street.municipality
        }

        initDragMarker(self.x, self.y, additionalInfo);
    }
}

function getAddressFromPoint(lang, x, y) {
    var self = this;
    $.ajax({
        url: 'http://service.gis.irisnet.be/urbis/Rest/Localize/getaddressfromxy',
        type:'POST',
        dataType:'jsonp',
        data: {
            json: ['{',
                '"language": "' + lang + '",',
                '"point":{x:' + x + ',y:' + y + '}',
                '}'].join('\n')
        },
        success:function(response)
        {
            //Fix problem with postcode 1041
            if (response.result.address.street.postCode === "1041") {
                response.result.address.street.postCode = "1040";
            }

            cleanMap();

            var street = response.result.address.street.name;
            var number = response.result.address.number;
            var postCode = response.result.address.street.postCode;
            var municipality = zipcodes[postCode].commune;

            var x = response.result.point.x;
            var y = response.result.point.y;

            //var popupContent = "
" + street + ", " + number;
            // Convert the point and url for google street view
            var pointStreetView = UtilGeolocation.convertCoordinatesToWGS84(x, y);
            var streetBiewLink = 'https://maps.google.be/maps?q=' + pointStreetView.y +','+ pointStreetView.x +'&layer=c&z=17&iwloc=A&sll='+ pointStreetView.y + ',' + pointStreetView.x + '&cbp=13,240.6,0,0,0&cbll=' + pointStreetView.y + ',' + pointStreetView.x;
            var popupContent = "
Déplacez-moi à l'adresse exacte
";
            popupContent += "
" + street + ", " + number;
            popupContent += "
" + postCode + " " + municipality + "
";

            if (NEXT_PAGE_URL) {
                popupContent += "C'est ici !";
            }
            popupContent += '
Street View
';

            var popup = new OpenLayers.Popup(
                "popup",
                new OpenLayers.LonLat(draggableMarker.components[0].x, draggableMarker.components[0].y),
                new OpenLayers.Size(200,150),
                popupContent,
                true
            );
            popup.panMapIfOutOfView = true;

            fms.currentMap.map.addPopup(popup);
        },
        error:function(response)
        {
            // Error
            //~ var msg = 'Error: ' + response.status;
            //~ if(response.status == 'error') {
                //~ msg = 'Unable to locate this address';
            //~ }
        }
    });
}

function initDragMarker(x, y, additionalInfo) {
        cleanMap();

        // Remove message info
        var $map = $('#map');
        map.classList.remove("map-big-message");
        map.classList.add("map-big");

        var $proposalMessage = $('#proposal-message');
        $proposalMessage.slideUp();

        draggableMarker = fms.currentMap.addDraggableMarker(x, y);
        fms.currentMap.centerOnDraggableMarker();
        fms.currentMap.map.zoomTo(6);

        var popupContent = "
Déplacez-moi à l'adresse exacte
";

        if (additionalInfo) {
            popupContent += "
" + additionalInfo.streetName + ", " + additionalInfo.number;
            popupContent += "
" + additionalInfo.postCode + " " + additionalInfo.municipality + "
";

            if (additionalInfo.number) {
                popupContent += "C'est ici !";
                popupContent += '
Street View
';
            }
        }

        var popup = new OpenLayers.Popup(
            "popup",
            new OpenLayers.LonLat(x, y),
            new OpenLayers.Size(200,150),
            popupContent,
            true
        );
        popup.panMapIfOutOfView = true;

        fms.currentMap.map.addPopup(popup);
}

function cleanMap() {
    // Remove all popups
    while(fms.currentMap.map.popups.length) {
         fms.currentMap.map.removePopup(fms.currentMap.map.popups[0]);
    }

    // Remove all features
    if (fms.currentMap.markersLayer) {
        fms.currentMap.markersLayer.destroyFeatures();
    }
    if (fms.currentMap.homepageMarkersLayer) {
        fms.currentMap.homepageMarkersLayer.destroyFeatures();
    }

    // Hide results
    $proposalContainer = $('#proposal-container');
    $proposalContainer.slideUp();
}

$(function(){

    var $searchStreet = $('#input-search');
    var $searchStreetNumber = $('#input-streetnumber');
    var $searchMunicipality = $('#input-ward');
    var $searchAddressForm = $('#search-address-form');
    var $searchTicketForm = $('#search-ticket-form');
    var $searchButton = $('#widget-search-button');
    var $searchTicketButton = $('#widget-search-ticket-button');
    var $proposal = $('#proposal');
    var $proposalContainer = $('#proposal-container');
    var $proposalMessage = $('#proposal-message');

    var $map = $('#map');
    $map.bind('markermoved',function(evt,point) {
        getAddressFromPoint(LANGUAGE_CODE, point.x, point.y);
    });

    $searchTicketForm.submit(function(event){
        event.preventDefault();
        var searchValue = $('#input-ticket-search').val();

        if (searchValue && searchValue.length > 0) {
            //Get current language
            var currentLng = 'en';
            if (window.location.href.indexOf('/nl/') != -1) {
                currentLng = 'nl';
            } else if (window.location.href.indexOf('/fr/') != -1) {
                currentLng = 'fr'
            }
            if (window.location.href.indexOf('pro') != -1) {
                window.location = "/"+currentLng+"/pro/report/search?report_id="+searchValue;
            } else {
                window.location = "/"+currentLng+"/report/search?report_id="+searchValue;
            }
        }
    });

    $searchAddressForm.submit(function(event){
        event.preventDefault();

        paginationResults = 0;

        var searchValue = $searchStreet.val();
        $proposalContainer.slideUp();

        if (!$searchStreet.val()) {
            $searchTicketForm.show();
            return;
        }

        var btnLocalizeviamap = document.getElementById('btn-localizeviamap');
        if (btnLocalizeviamap) {
            btnLocalizeviamap.parentNode.removeChild(btnLocalizeviamap);
        }

        $searchStreet.addClass('loading');
        $searchButton.prop('disabled',true);
        $.ajax({
            url: URBIS_URL + 'service/urbis/Rest/Localize/getaddressesfields',
            dataType:'jsonp',
            data:{
                json:'{"language": "' + LANGUAGE_CODE + '",' +
                '"address": {' +
                    '"street": {' +
                        '"name": "' + $searchStreet.val().replace("\"","\\\"") + '",' +
                        '"postcode": "' + $searchMunicipality.val().replace("\"","\\\"") + '"' +
                    '},"number": "' + $searchStreetNumber.val() + '"' +
                '}}'
            }
        }).success(function(response){
            if(response.status == 'success' && response.result.length > 0)
            {
                var map = document.getElementById('map');
                var mapViewPort = document.getElementById('OpenLayers.Map_4_OpenLayers_ViewPort');

                cleanMap();

                $searchStreet.removeClass('loading');
                $searchButton.prop('disabled',false);
                $proposalMessage.empty();

                // Urbis response 1 result
                if(response.result.length == 1) {
                    var pos    = response.result[0].point;
                    var address = response.result[0].address;

                    additionalInfo = {
                        'streetName'   : address.street.name,
                        'number'       : address.number,
                        'postCode'     : address.street.postCode,
                        'municipality' : address.street.municipality
                    }

                    initDragMarker(pos.x, pos.y, additionalInfo);

                    map.classList.remove("map-big-message");
                    map.classList.add("map-big");
                }
                // Urbis response many results
                else {
                    features = [];

                    results = response.result;
                    document.getElementById('numberResults').innerHTML = results.length;

                    if (!fms.currentMap.homepageMarkersLayer) {
                        // Create vector layer
                        fms.currentMap.homepageMarkersLayer = new OpenLayers.Layer.Vector("Overlay");

                        // Add layer to map
                        fms.currentMap.map.addLayer(fms.currentMap.homepageMarkersLayer);

                        // Function to bind selector to initDragMarker
                        function bindClick(feature) {
                            var x = feature.geometry.x;
                            var y = feature.geometry.y;

                            initDragMarker(x, y, feature.attributes.additionalInfo);
                        }

                        // Add the selector control to the vectorLayer
                        var clickCtrl = new OpenLayers.Control.SelectFeature(fms.currentMap.homepageMarkersLayer, { onSelect: bindClick });
                        fms.currentMap.map.addControl(clickCtrl);
                        clickCtrl.activate();
                    }

                    renderResults();

                }
            }
            else
            {
                $searchTicketForm.show();
                $searchStreet.removeClass('loading');
                $searchButton.prop('disabled',false);
                if(response.status == "noresult" || response.status == "success")
                {
                    $proposalMessage.html('No corresponding address has been found').slideDown();
                }
                else
                {
                    $proposalMessage.html('' + response.status + '').slideDown();
                }
            }
        }).error(function(xhr,msg,error){
            $searchStreet.removeClass('loading');
            $searchButton.prop('disabled',false);

            $proposalMessage.html('Unexpected error.').slideDown();
        });
    });

    $searchAddressForm.submit();
});

(function() {

    var streetKeywords = document.getElementById('input-search');
    var postalCode = document.getElementById('input-ward');
    var searchBtn = document.getElementById('widget-search-button')

    function enableSearch() {
        var enableSearchBtn = false;

        if (streetKeywords.value) {
            enableSearchBtn = true;
        }

        searchBtn.disabled = !enableSearchBtn;
    }

    function municipalityChange() {
        enableSearch();

        if ( !(streetKeywords.value) && (postalCode.value) ) {
            alert('middle of municipality : ' + postalCode.value);
        }
    }
    // Enable search button if one of fields contain a value
    streetKeywords.addEventListener('keyup', enableSearch);
    streetKeywords.addEventListener('change', enableSearch);
    postalCode.addEventListener('change', municipalityChange);

})(document);

// Localize report with map
(function() {

    var btnLocalizeviamap = document.getElementById('btn-localizeviamap');

    btnLocalizeviamap.addEventListener("click", function() {
        // Hard code center of map (~same value than fms.currentMap.map.getCenter() but as x,y and not lat,lon)
        initDragMarker(148894,170714);
        fms.currentMap.map.zoomTo(2);

        btnLocalizeviamap.parentNode.removeChild(btnLocalizeviamap);
    });

})(document);
<commit_msg>add feature center on selected municipality<commit_after>var results           = [];
var paginationResults;

var previousResults = document.getElementById("previousResults");
var nextResults     = document.getElementById("nextResults");

var draggableMarker;

previousResults.addEventListener('click', function() {
    paginationResults -= 1;
    renderResults();
})
nextResults.addEventListener('click', function() {
    paginationResults += 1;
    renderResults();
})

function renderResults() {
    var $proposal = $('#proposal');
    var $proposalMessage = $('#proposal-message');

    var $searchStreet = $('#input-search');
    var $searchMunicipality = $('#input-ward');

    $proposal.empty();
    var features = [];

    var iconIdx = 0;
    for(var i=paginationResults*5, length=results.length; i<length && features.length<5; i++) {
        var address = results[i].address;
        var pos = results[i].point;

        var newAddress = new AddressResult(pos.x, pos.y, address, iconIdx++);
        $proposal.append(newAddress.render());

        // Create feature on vectore layer
        var feature = new OpenLayers.Feature.Vector(
                new OpenLayers.Geometry.Point(pos.x, pos.y),
                { 'additionalInfo' :
                    {
                        'streetName'   : address.street.name,
                        'number'       : address.number,
                        'postCode'     : address.street.postCode,
                        'municipality' : address.street.municipality
                    }
                },
                {
                    externalGraphic: newAddress.iconSrc(),
                    graphicWidth: 25,
                    graphicHeight: 32,
                    graphicYOffset: -32
                }
            );
        features.push(feature);

        // Define message if needed
        if ($searchMunicipality.val()) {
            $proposalMessage.html("Cette rue n'est pas répertoriée dans cette commune");
        } else if ($searchStreet.val().toLowerCase() == address.street.name.toLowerCase()) {
            $proposalMessage.html("Cette rue existe dans plusieurs communes, merci de préciser");
        }
    }

    // Mask/display pagination buttons
    if (paginationResults == 0) {
        previousResults.hidden = true;
    } else {
        previousResults.hidden = false;
    }

    if (results.length >= paginationResults * 5 + 6) {
        nextResults.hidden = false;
    } else {
        nextResults.hidden = true;
    }

    // Add features to layer
    cleanMap();
    fms.currentMap.homepageMarkersLayer.addFeatures(features);

    // Zoom to markers
    var markersBound = fms.currentMap.homepageMarkersLayer.getDataExtent();
    fms.currentMap.map.zoomToExtent(markersBound);

    // Show/hide proposal and message
    $proposalContainer = $('#proposal-container');
    $proposalContainer.slideDown();

    if ($proposalMessage.html()) {
        map.classList.add("map-big-message");
        $proposalMessage.slideDown();
    } else {
        map.classList.remove("map-big-message");
        map.classList.add("map-big");
        $proposalMessage.slideUp();
    }

}
function AddressResult(x, y, address, idx)
{
    var self = this;

    this.x = x;
    this.y = y;
    this.el = document.createElement('li');

    this.address = address;

    this.render = function() {
        this.el.innerHTML = '
' + this.address.street.name +
                            '
' +
                            '' + this.address.street.postCode + ' ' + this.address.street.municipality + '' +
                            '
';
        this.el.addEventListener('click', this.onclick);
        return  this.el;
    };

    this.iconSrc = function() {
        return "/static/images/marker/marker-" + idx + ".png";
    },

    this.onclick = function(event) {
        additionalInfo = {
            'streetName'   : self.address.street.name,
            'number'       : self.address.number,
            'postCode'     : self.address.street.postCode,
            'municipality' : self.address.street.municipality
        }

        initDragMarker(self.x, self.y, additionalInfo);
    }
}

function getAddressFromPoint(lang, x, y) {
    var self = this;
    $.ajax({
        url: 'http://service.gis.irisnet.be/urbis/Rest/Localize/getaddressfromxy',
        type:'POST',
        dataType:'jsonp',
        data: {
            json: ['{',
                '"language": "' + lang + '",',
                '"point":{x:' + x + ',y:' + y + '}',
                '}'].join('\n')
        },
        success:function(response)
        {
            //Fix problem with postcode 1041
            if (response.result.address.street.postCode === "1041") {
                response.result.address.street.postCode = "1040";
            }

            cleanMap();

            var street = response.result.address.street.name;
            var number = response.result.address.number;
            var postCode = response.result.address.street.postCode;
            var municipality = zipcodes[postCode].commune;

            var x = response.result.point.x;
            var y = response.result.point.y;

            //var popupContent = "
" + street + ", " + number;
            // Convert the point and url for google street view
            var pointStreetView = UtilGeolocation.convertCoordinatesToWGS84(x, y);
            var streetBiewLink = 'https://maps.google.be/maps?q=' + pointStreetView.y +','+ pointStreetView.x +'&layer=c&z=17&iwloc=A&sll='+ pointStreetView.y + ',' + pointStreetView.x + '&cbp=13,240.6,0,0,0&cbll=' + pointStreetView.y + ',' + pointStreetView.x;
            var popupContent = "
Déplacez-moi à l'adresse exacte
";
            popupContent += "
" + street + ", " + number;
            popupContent += "
" + postCode + " " + municipality + "
";

            if (NEXT_PAGE_URL) {
                popupContent += "C'est ici !";
            }
            popupContent += '
Street View
';

            var popup = new OpenLayers.Popup(
                "popup",
                new OpenLayers.LonLat(draggableMarker.components[0].x, draggableMarker.components[0].y),
                new OpenLayers.Size(200,150),
                popupContent,
                true
            );
            popup.panMapIfOutOfView = true;

            fms.currentMap.map.addPopup(popup);
        },
        error:function(response)
        {
            // Error
            //~ var msg = 'Error: ' + response.status;
            //~ if(response.status == 'error') {
                //~ msg = 'Unable to locate this address';
            //~ }
        }
    });
}

function initDragMarker(x, y, additionalInfo) {
        cleanMap();

        // Remove message info
        var $map = $('#map');
        map.classList.remove("map-big-message");
        map.classList.add("map-big");

        var $proposalMessage = $('#proposal-message');
        $proposalMessage.slideUp();

        draggableMarker = fms.currentMap.addDraggableMarker(x, y);
        fms.currentMap.centerOnDraggableMarker();
        fms.currentMap.map.zoomTo(6);

        var popupContent = "
Déplacez-moi à l'adresse exacte
";

        if (additionalInfo) {
            popupContent += "
" + additionalInfo.streetName + ", " + additionalInfo.number;
            popupContent += "
" + additionalInfo.postCode + " " + additionalInfo.municipality + "
";

            if (additionalInfo.number) {
                popupContent += "C'est ici !";
                popupContent += '
Street View
';
            }
        }

        var popup = new OpenLayers.Popup(
            "popup",
            new OpenLayers.LonLat(x, y),
            new OpenLayers.Size(200,150),
            popupContent,
            true
        );
        popup.panMapIfOutOfView = true;

        fms.currentMap.map.addPopup(popup);
}

function cleanMap() {
    // Remove all popups
    while(fms.currentMap.map.popups.length) {
         fms.currentMap.map.removePopup(fms.currentMap.map.popups[0]);
    }

    // Remove all features
    if (fms.currentMap.markersLayer) {
        fms.currentMap.markersLayer.destroyFeatures();
    }
    if (fms.currentMap.homepageMarkersLayer) {
        fms.currentMap.homepageMarkersLayer.destroyFeatures();
    }

    // Hide results
    $proposalContainer = $('#proposal-container');
    $proposalContainer.slideUp();
}

$(function(){

    var $searchStreet = $('#input-search');
    var $searchStreetNumber = $('#input-streetnumber');
    var $searchMunicipality = $('#input-ward');
    var $searchAddressForm = $('#search-address-form');
    var $searchTicketForm = $('#search-ticket-form');
    var $searchButton = $('#widget-search-button');
    var $searchTicketButton = $('#widget-search-ticket-button');
    var $proposal = $('#proposal');
    var $proposalContainer = $('#proposal-container');
    var $proposalMessage = $('#proposal-message');

    var $map = $('#map');
    $map.bind('markermoved',function(evt,point) {
        getAddressFromPoint(LANGUAGE_CODE, point.x, point.y);
    });

    /*$searchMunicipality.change(function(event){
        fms.currentMap.centerOnMunicipality($(this).val());
    });*/

    $searchTicketForm.submit(function(event){
        event.preventDefault();
        var searchValue = $('#input-ticket-search').val();

        if (searchValue && searchValue.length > 0) {
            //Get current language
            var currentLng = 'en';
            if (window.location.href.indexOf('/nl/') != -1) {
                currentLng = 'nl';
            } else if (window.location.href.indexOf('/fr/') != -1) {
                currentLng = 'fr'
            }
            if (window.location.href.indexOf('pro') != -1) {
                window.location = "/"+currentLng+"/pro/report/search?report_id="+searchValue;
            } else {
                window.location = "/"+currentLng+"/report/search?report_id="+searchValue;
            }
        }
    });

    $searchAddressForm.submit(function(event){
        event.preventDefault();

        paginationResults = 0;

        var searchValue = $searchStreet.val();
        $proposalContainer.slideUp();

        if (!$searchStreet.val()) {
            $searchTicketForm.show();
            return;
        }

        var btnLocalizeviamap = document.getElementById('btn-localizeviamap');
        if (btnLocalizeviamap) {
            btnLocalizeviamap.parentNode.removeChild(btnLocalizeviamap);
        }

        $searchStreet.addClass('loading');
        $searchButton.prop('disabled',true);
        $.ajax({
            url: URBIS_URL + 'service/urbis/Rest/Localize/getaddressesfields',
            dataType:'jsonp',
            data:{
                json:'{"language": "' + LANGUAGE_CODE + '",' +
                '"address": {' +
                    '"street": {' +
                        '"name": "' + $searchStreet.val().replace("\"","\\\"") + '",' +
                        '"postcode": "' + $searchMunicipality.val().replace("\"","\\\"") + '"' +
                    '},"number": "' + $searchStreetNumber.val() + '"' +
                '}}'
            }
        }).success(function(response){
            if(response.status == 'success' && response.result.length > 0)
            {
                var map = document.getElementById('map');
                var mapViewPort = document.getElementById('OpenLayers.Map_4_OpenLayers_ViewPort');

                cleanMap();

                $searchStreet.removeClass('loading');
                $searchButton.prop('disabled',false);
                $proposalMessage.empty();

                // Urbis response 1 result
                if(response.result.length == 1) {
                    var pos    = response.result[0].point;
                    var address = response.result[0].address;

                    additionalInfo = {
                        'streetName'   : address.street.name,
                        'number'       : address.number,
                        'postCode'     : address.street.postCode,
                        'municipality' : address.street.municipality
                    }

                    initDragMarker(pos.x, pos.y, additionalInfo);

                    map.classList.remove("map-big-message");
                    map.classList.add("map-big");
                }
                // Urbis response many results
                else {
                    features = [];

                    results = response.result;
                    document.getElementById('numberResults').innerHTML = results.length;

                    if (!fms.currentMap.homepageMarkersLayer) {
                        // Create vector layer
                        fms.currentMap.homepageMarkersLayer = new OpenLayers.Layer.Vector("Overlay");

                        // Add layer to map
                        fms.currentMap.map.addLayer(fms.currentMap.homepageMarkersLayer);

                        // Function to bind selector to initDragMarker
                        function bindClick(feature) {
                            var x = feature.geometry.x;
                            var y = feature.geometry.y;

                            initDragMarker(x, y, feature.attributes.additionalInfo);
                        }

                        // Add the selector control to the vectorLayer
                        var clickCtrl = new OpenLayers.Control.SelectFeature(fms.currentMap.homepageMarkersLayer, { onSelect: bindClick });
                        fms.currentMap.map.addControl(clickCtrl);
                        clickCtrl.activate();
                    }

                    renderResults();

                }
            }
            else
            {
                $searchTicketForm.show();
                $searchStreet.removeClass('loading');
                $searchButton.prop('disabled',false);
                if(response.status == "noresult" || response.status == "success")
                {
                    $proposalMessage.html('No corresponding address has been found').slideDown();
                }
                else
                {
                    $proposalMessage.html('' + response.status + '').slideDown();
                }
            }
        }).error(function(xhr,msg,error){
            $searchStreet.removeClass('loading');
            $searchButton.prop('disabled',false);

            $proposalMessage.html('Unexpected error.').slideDown();
        });
    });

    $searchAddressForm.submit();
});

(function() {

    var streetKeywords = document.getElementById('input-search');
    var postalCode = document.getElementById('input-ward');
    var searchBtn = document.getElementById('widget-search-button')

    function enableSearch() {
        var enableSearchBtn = false;

        if (streetKeywords.value) {
            enableSearchBtn = true;
        }

        searchBtn.disabled = !enableSearchBtn;
    }

    function municipalityChange() {
        enableSearch();

        fms.currentMap.centerOnMunicipality(postalCode.value);
        /*if ( !(streetKeywords.value) && (postalCode.value) ) {
            alert('middle of municipality : ' + postalCode.value);
        }*/
    }
    // Enable search button if one of fields contain a value
    streetKeywords.addEventListener('keyup', enableSearch);
    streetKeywords.addEventListener('change', enableSearch);
    postalCode.addEventListener('change', municipalityChange);

})(document);

// Localize report with map
(function() {

    var btnLocalizeviamap = document.getElementById('btn-localizeviamap');

    btnLocalizeviamap.addEventListener("click", function() {
        // Hard code center of map (~same value than fms.currentMap.map.getCenter() but as x,y and not lat,lon)
        initDragMarker(148894,170714);
        fms.currentMap.map.zoomTo(2);

        btnLocalizeviamap.parentNode.removeChild(btnLocalizeviamap);
    });

})(document);
<|endoftext|>


<commit_before>/**
 * Created by Scott on 5/31/2015.
 */
define(['../../lib/temple', '../../lib/notify',  'hello/hello', 'main'], function (temple, notify, hello, main) {
    var preloader = {};

    preloader.load = function () {
        var templates = [
            // array of templates  -- get and cache
            // TODO: Move this to discussion forum Demo
            temple.getTemplate('./hello/hello.html', true),
            temple.getTemplate('./templates/comment.html', true),
            temple.getTemplate('./templates/discussion.html', true),
            temple.getTemplate('./templates/post.html', true),
            temple.getTemplate('./templates/topic.html', true)
        ];

        // tell notify.js to auto-publish this topic when the uri hash changes to it
        // hello.js (view) loaded and is listening to this topic, so no need to 'start' it manually
        notify.setRoute('#hello');

        Promise.all(templates).then(function () {
            // resources are cached...
                // launch the app (this does almost nothing in this example except run tests)
                main.start();
            //});
        }).catch(function (e) {
            // errors land here... do with them what you wish
        });
    };

    return preloader;
});<commit_msg>Comments<commit_after>/**
 * Created by Scott on 5/31/2015.
 */
define(['../../lib/temple', '../../lib/notify',  'hello/hello', 'main'], function (temple, notify, hello, main) {
    var preloader = {};

    preloader.load = function () {
        var templates = [
            // array of templates  -- get and cache
            // TODO: Move this to discussion forum Demo
            temple.getTemplate('./hello/hello.html', true),
            temple.getTemplate('./templates/comment.html', true),
            temple.getTemplate('./templates/discussion.html', true),
            temple.getTemplate('./templates/post.html', true),
            temple.getTemplate('./templates/topic.html', true)
        ];

        // hello.js (view) loaded and is listening to the #hello topic, so no need to 'start' it manually

        Promise.all(templates).then(function () {
            // resources are cached...
                // launch the app (this does almost nothing in this example except run tests)
                main.start();
            //});
        }).catch(function (e) {
            // errors land here... do with them what you wish
        });
    };

    return preloader;
});<|endoftext|>


<commit_before>import Vue from 'vue'

import ListenersMixin from '../../mixins/listeners.js'

import debounce from '../../utils/debounce.js'
import { height } from '../../utils/dom.js'
import { getScrollTarget, getScrollHeight, getScrollPosition, setScrollPosition } from '../../utils/scroll.js'
import { listenOpts } from '../../utils/event.js'
import { slot, uniqueSlot } from '../../utils/slot.js'

export default Vue.extend({
  name: 'QInfiniteScroll',

  mixins: [ ListenersMixin ],

  props: {
    offset: {
      type: Number,
      default: 500
    },

    debounce: {
      type: [ String, Number ],
      default: 100
    },

    scrollTarget: {
      default: void 0
    },

    initialIndex: Number,

    disable: Boolean,
    reverse: Boolean
  },

  data () {
    return {
      index: this.initialIndex || 0,
      fetching: false,
      working: true
    }
  },

  watch: {
    disable (val) {
      if (val === true) {
        this.stop()
      }
      else {
        this.resume()
      }
    },

    scrollTarget () {
      this.updateScrollTarget()
    },

    debounce (val) {
      this.__setDebounce(val)
    }
  },

  methods: {
    poll () {
      if (this.disable === true || this.fetching === true || this.working === false) {
        return
      }

      const
        scrollHeight = getScrollHeight(this.__scrollTarget),
        scrollPosition = getScrollPosition(this.__scrollTarget),
        containerHeight = height(this.__scrollTarget)

      if (this.reverse === false) {
        if (scrollPosition + containerHeight + this.offset >= scrollHeight) {
          this.trigger()
        }
      }
      else {
        if (scrollPosition < this.offset) {
          this.trigger()
        }
      }
    },

    trigger () {
      if (this.disable === true || this.fetching === true || this.working === false) {
        return
      }

      this.index++
      this.fetching = true

      const heightBefore = getScrollHeight(this.__scrollTarget)

      this.$emit('load', this.index, stop => {
        if (this.working === true) {
          this.fetching = false
          this.$nextTick(() => {
            if (this.reverse === true) {
              const
                heightAfter = getScrollHeight(this.__scrollTarget),
                scrollPosition = getScrollPosition(this.__scrollTarget),
                heightDifference = heightAfter - heightBefore

              setScrollPosition(this.__scrollTarget, scrollPosition + heightDifference)
            }

            if (stop === true) {
              this.stop()
            }
            else {
              this.$el.closest('body') && this.poll()
            }
          })
        }
      })
    },

    reset () {
      this.index = 0
    },

    resume () {
      if (this.working === false) {
        this.working = true
        this.__scrollTarget.addEventListener('scroll', this.poll, listenOpts.passive)
      }
      this.immediatePoll()
    },

    stop () {
      if (this.working === true) {
        this.working = false
        this.fetching = false
        this.__scrollTarget.removeEventListener('scroll', this.poll, listenOpts.passive)
      }
    },

    updateScrollTarget () {
      if (this.__scrollTarget && this.working === true) {
        this.__scrollTarget.removeEventListener('scroll', this.poll, listenOpts.passive)
      }

      this.__scrollTarget = getScrollTarget(this.$el, this.scrollTarget)

      if (this.working === true) {
        this.__scrollTarget.addEventListener('scroll', this.poll, listenOpts.passive)
      }
    },

    setIndex (index) {
      this.index = index
    },

    __setDebounce (val) {
      val = parseInt(val, 10)
      if (val <= 0) {
        this.poll = this.immediatePoll
      }
      else {
        this.poll = debounce(this.immediatePoll, isNaN(val) === true ? 100 : val)
      }
    }
  },

  mounted () {
    this.immediatePoll = this.poll
    this.__setDebounce(this.debounce)

    this.updateScrollTarget()

    if (this.reverse === true) {
      const
        scrollHeight = getScrollHeight(this.__scrollTarget),
        containerHeight = height(this.__scrollTarget)

      setScrollPosition(this.__scrollTarget, scrollHeight - containerHeight)
    }

    this.immediatePoll()
  },

  beforeDestroy () {
    if (this.working === true) {
      this.__scrollTarget.removeEventListener('scroll', this.poll, listenOpts.passive)
    }
  },

  render (h) {
    const child = uniqueSlot(this, 'default', [])

    if (this.disable !== true && this.working === true) {
      child[this.reverse === false ? 'push' : 'unshift'](
        h('div', {
          staticClass: 'q-infinite-scroll__loading',
          class: this.fetching === true ? '' : 'invisible'
        }, slot(this, 'loading'))
      )
    }

    return h('div', {
      staticClass: 'q-infinite-scroll',
      on: { ...this.qListeners }
    }, child)
  }
})
<commit_msg>fix(QInfiniteScroll): calling __setDebounce should update attached events<commit_after>import Vue from 'vue'

import ListenersMixin from '../../mixins/listeners.js'

import debounce from '../../utils/debounce.js'
import { height } from '../../utils/dom.js'
import { getScrollTarget, getScrollHeight, getScrollPosition, setScrollPosition } from '../../utils/scroll.js'
import { listenOpts } from '../../utils/event.js'
import { slot, uniqueSlot } from '../../utils/slot.js'

export default Vue.extend({
  name: 'QInfiniteScroll',

  mixins: [ ListenersMixin ],

  props: {
    offset: {
      type: Number,
      default: 500
    },

    debounce: {
      type: [ String, Number ],
      default: 100
    },

    scrollTarget: {
      default: void 0
    },

    initialIndex: Number,

    disable: Boolean,
    reverse: Boolean
  },

  data () {
    return {
      index: this.initialIndex || 0,
      fetching: false,
      working: true
    }
  },

  watch: {
    disable (val) {
      if (val === true) {
        this.stop()
      }
      else {
        this.resume()
      }
    },

    scrollTarget () {
      this.updateScrollTarget()
    },

    debounce (val) {
      this.__setDebounce(val)
    }
  },

  methods: {
    poll () {
      if (this.disable === true || this.fetching === true || this.working === false) {
        return
      }

      const
        scrollHeight = getScrollHeight(this.__scrollTarget),
        scrollPosition = getScrollPosition(this.__scrollTarget),
        containerHeight = height(this.__scrollTarget)

      if (this.reverse === false) {
        if (scrollPosition + containerHeight + this.offset >= scrollHeight) {
          this.trigger()
        }
      }
      else {
        if (scrollPosition < this.offset) {
          this.trigger()
        }
      }
    },

    trigger () {
      if (this.disable === true || this.fetching === true || this.working === false) {
        return
      }

      this.index++
      this.fetching = true

      const heightBefore = getScrollHeight(this.__scrollTarget)

      this.$emit('load', this.index, stop => {
        if (this.working === true) {
          this.fetching = false
          this.$nextTick(() => {
            if (this.reverse === true) {
              const
                heightAfter = getScrollHeight(this.__scrollTarget),
                scrollPosition = getScrollPosition(this.__scrollTarget),
                heightDifference = heightAfter - heightBefore

              setScrollPosition(this.__scrollTarget, scrollPosition + heightDifference)
            }

            if (stop === true) {
              this.stop()
            }
            else {
              this.$el.closest('body') && this.poll()
            }
          })
        }
      })
    },

    reset () {
      this.index = 0
    },

    resume () {
      if (this.working === false) {
        this.working = true
        this.__scrollTarget.addEventListener('scroll', this.poll, listenOpts.passive)
      }
      this.immediatePoll()
    },

    stop () {
      if (this.working === true) {
        this.working = false
        this.fetching = false
        this.__scrollTarget.removeEventListener('scroll', this.poll, listenOpts.passive)
      }
    },

    updateScrollTarget () {
      if (this.__scrollTarget && this.working === true) {
        this.__scrollTarget.removeEventListener('scroll', this.poll, listenOpts.passive)
      }

      this.__scrollTarget = getScrollTarget(this.$el, this.scrollTarget)

      if (this.working === true) {
        this.__scrollTarget.addEventListener('scroll', this.poll, listenOpts.passive)
      }
    },

    setIndex (index) {
      this.index = index
    },

    __setDebounce (val) {
      val = parseInt(val, 10)

      const oldPoll = this.poll

      this.poll = val <= 0
        ? this.immediatePoll
        : debounce(this.immediatePoll, isNaN(val) === true ? 100 : val)

      if (this.__scrollTarget && this.working === true) {
        if (oldPoll !== void 0) {
          this.__scrollTarget.removeEventListener('scroll', oldPoll, listenOpts.passive)
        }

        this.__scrollTarget.addEventListener('scroll', this.poll, listenOpts.passive)
      }
    }
  },

  mounted () {
    this.immediatePoll = this.poll
    this.__setDebounce(this.debounce)

    this.updateScrollTarget()

    if (this.reverse === true) {
      const
        scrollHeight = getScrollHeight(this.__scrollTarget),
        containerHeight = height(this.__scrollTarget)

      setScrollPosition(this.__scrollTarget, scrollHeight - containerHeight)
    }

    this.immediatePoll()
  },

  beforeDestroy () {
    if (this.working === true) {
      this.__scrollTarget.removeEventListener('scroll', this.poll, listenOpts.passive)
    }
  },

  render (h) {
    const child = uniqueSlot(this, 'default', [])

    if (this.disable !== true && this.working === true) {
      child[this.reverse === false ? 'push' : 'unshift'](
        h('div', {
          staticClass: 'q-infinite-scroll__loading',
          class: this.fetching === true ? '' : 'invisible'
        }, slot(this, 'loading'))
      )
    }

    return h('div', {
      staticClass: 'q-infinite-scroll',
      on: { ...this.qListeners }
    }, child)
  }
})
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014 airbug inc. http://airbug.com
 *
 * bugmeta may be freely distributed under the MIT license.
 */

//-------------------------------------------------------------------------------
// Common Modules
//-------------------------------------------------------------------------------

var bugcore             = require('bugcore');
var bugflow             = require('bugflow');
var bugfs               = require('bugfs');
var buildbug            = require('buildbug');

//-------------------------------------------------------------------------------
// Simplify References
//-------------------------------------------------------------------------------

var enableModule        = buildbug.enableModule;
var TypeUtil            = bugcore.TypeUtil;
var $series             = bugflow.$series;
var $task               = bugflow.$task;

//-------------------------------------------------------------------------------
// Enable Modules
//-------------------------------------------------------------------------------

var lintbug             = enableModule("lintbug");

//-------------------------------------------------------------------------------
// Lint Tasks
//-------------------------------------------------------------------------------

lintbug.lintTask("ensureNewLineEnding", function(lintFile, callback) {
    var fileContents    = lintFile.getFileContents();
    var lines           = fileContents.split("\n");
    var lastLine        = lines.pop();
    lines.push(lastLine);
    if (lastLine !== "") {
        lines.push("");
    }
    lintFile.setFileContents(lines.join("\n"));
    callback();
});

lintbug.lintTask("indentEqualSignsForPreClassVars", function(lintFile, callback) {
    var indentSpacing   = 4;
    var fileContents    = lintFile.getFileContents();
    var lines           = fileContents.split("\n");
    var startIndex      = bugcore.ArrayUtil.indexOf(lines, /^\s*\/\/ Context\s*$/);
    var endIndex        = bugcore.ArrayUtil.indexOf(lines, /^\s*\/\/ Declare Class\s*$/);
    var varRegex        = /^(\s*)var (\w+)\s*=(.*)$/;
    var varObjects      = [];
    var longestIndent   = 0;
    if (startIndex > -1 && endIndex > -1) {
        for (var i = startIndex; i < endIndex; i++) {
            var line        = lines[i];
            var results     = line.match(varRegex);
            if (results) {
                var indent  = results[1];
                var varName = results[2];
                varObjects.push({
                    index: i,
                    indent: indent,
                    name: varName,
                    afterEqualsContent: results[3]
                });

                //NOTE BRN: Add 2 at the end so that we are at least two spaces away from the variable name
                var indentLength = Math.ceil((indent + ("var ").length + varName.length + 2) / indentSpacing);
                if (indentLength > longestIndent) {
                    longestIndent = indentLength;
                }
            }
        }
        varObjects.forEach(function(varObject) {
            var numberCharsBeforeEquals = longestIndent * indentSpacing;
            var preEqualsText = bugcore.StringUtil.rpad(varObject.indent + "var " + varObject.name, " ", numberCharsBeforeEquals);
            lines[varObject.index] = preEqualsText + "=" + varObject.afterEqualsContent;
        });
    }
    lintFile.setFileContents(lines.join("\n"));
    callback();
});

lintbug.lintTask("orderBugpackRequires", function(lintFile, callback) {
    var fileContents    = lintFile.getFileContents();
    fileContents = sortBugpackRequires(fileContents);
    lintFile.setFileContents(fileContents);
    callback();
});

lintbug.lintTask("orderRequireAnnotations", function(lintFile, callback) {
    var fileContents    = lintFile.getFileContents();
    fileContents = sortRequireAnnotations(fileContents);
    lintFile.setFileContents(fileContents);
    callback();
});

lintbug.lintTask("updateCopyright", function(lintFile, callback) {
    var fileContents    = lintFile.getFileContents();
    var copyright       = getCopyright();
    var copyrightRegex  = /^(\s*)\/\*(([^.]|[.])+?)Copyright \(c\)(([^.]|[.])+?)\*\/(\s*)/;
    if (copyrightRegex.test(fileContents)) {
        fileContents = fileContents.replace(copyrightRegex, copyright + "\n\n");
    } else {
        fileContents = copyright + "\n\n" + fileContents;
    }
    lintFile.setFileContents(fileContents);
    callback();
});

//-------------------------------------------------------------------------------
// Helper Methods
//-------------------------------------------------------------------------------

/**
 * @private
 * @param {string} fileContents
 * @returns {Array.<{index: number, line: string}>}
 */
var generateLines = function(fileContents) {
    return bugcore.StringUtil.split(fileContents, "\n", function(line, index) {
        return {
            index: index,
            line: line
        };
    });
};

var copyright = null;
/**
 * @private
 * @return {string}
 */
var getCopyright = function() {
    if (copyright === null) {
        var copyrightText   = bugcore.StringUtil.trim(bugfs.readFileSync(__dirname + "/COPYRIGHT", 'utf8'));
        var copyrightLines  = copyrightText.split("\n");
        copyright = "/*\n";
        copyrightLines.forEach(function(copyrightLine) {
            copyrightLine = bugcore.StringUtil.trim(copyrightLine);
            if (copyrightLine !== "") {
                copyright += " * " + copyrightLine + "\n";
            } else {
                copyright += " *\n";
            }
        });
        copyright += " */\n";
    }
    return copyright;
};

/**
 * @private
 * @param {Array.<{index: number, line: string}>} lines
 * @return {string}
 */
var mergeLines = function(lines) {
    var result = "";
    var first = true;
    lines.forEach(function(line) {
        if (first) {
            result += line.line;
            first = false;
        } else {
            result += "\n" + line.line;
        }
    });
    return result;
};

/**
 * @private
 * @param {string} fileContents
 * @return {string}
 */
var sortRequireAnnotations = function(fileContents) {
    var lines   = generateLines(fileContents);
    lines.sort(function(a, b) {
        var resultsA = a.line.match(/^\s*\/\/\s*@Require\(('|")((?:\w|\.)*)\1\)\s*$/);
        var resultsB = b.line.match(/^\s*\/\/\s*@Require\(('|")((?:\w|\.)*)\1\)\s*$/);

        if (resultsA && resultsB) {
            var partsA = resultsA[2].split(".");
            var partsB = resultsB[2].split(".");
            var classNameA = partsA.pop();
            var classNameB = partsB.pop();
            var packageNameA = partsA.join(".");
            var packageNameB = partsB.join(".");
            if (packageNameA < packageNameB) {
                return -1;
            }
            if (packageNameA > packageNameB) {
                return 1;
            }
            if (classNameA < classNameB) {
                return -1;
            }
            if (classNameA > classNameB) {
                return 1;
            }
        }
        if (a.index < b.index) {
            return -1;
        }
        return 1;
    });
    return mergeLines(lines);
};

/**
 * @private
 * @param {string} fileContents
 * @return {string}
 */
var sortBugpackRequires = function(fileContents) {
    var lines   = generateLines(fileContents);
    lines.sort(function(a, b) {
        var resultsA = a.line.match(/^\s*var \w+\s+=\s+bugpack\.require\(('|")((?:\w|\.)*)\1\);\s*$/);
        var resultsB = b.line.match(/^\s*var \w+\s+=\s+bugpack\.require\(('|")((?:\w|\.)*)\1\);\s*$/);

        if (resultsA && resultsB) {
            var partsA = resultsA[2].split(".");
            var partsB = resultsB[2].split(".");
            var classNameA = partsA.pop();
            var classNameB = partsB.pop();
            var packageNameA = partsA.join(".");
            var packageNameB = partsB.join(".");
            if (packageNameA < packageNameB) {
                return -1;
            }
            if (packageNameA > packageNameB) {
                return 1;
            }
            if (classNameA < classNameB) {
                return -1;
            }
            if (classNameA > classNameB) {
                return 1;
            }
        }
        if (a.index < b.index) {
            return -1;
        }
        return 1;
    });
    return mergeLines(lines);
};
<commit_msg>Fix bug in indentEqualSignsForPreClassVars task<commit_after>/*
 * Copyright (c) 2014 airbug inc. http://airbug.com
 *
 * bugmeta may be freely distributed under the MIT license.
 */

//-------------------------------------------------------------------------------
// Common Modules
//-------------------------------------------------------------------------------

var bugcore             = require('bugcore');
var bugflow             = require('bugflow');
var bugfs               = require('bugfs');
var buildbug            = require('buildbug');

//-------------------------------------------------------------------------------
// Simplify References
//-------------------------------------------------------------------------------

var enableModule        = buildbug.enableModule;
var TypeUtil            = bugcore.TypeUtil;
var $series             = bugflow.$series;
var $task               = bugflow.$task;

//-------------------------------------------------------------------------------
// Enable Modules
//-------------------------------------------------------------------------------

var lintbug             = enableModule("lintbug");

//-------------------------------------------------------------------------------
// Lint Tasks
//-------------------------------------------------------------------------------

lintbug.lintTask("ensureNewLineEnding", function(lintFile, callback) {
    var fileContents    = lintFile.getFileContents();
    var lines           = fileContents.split("\n");
    var lastLine        = lines.pop();
    lines.push(lastLine);
    if (lastLine !== "") {
        lines.push("");
    }
    lintFile.setFileContents(lines.join("\n"));
    callback();
});

lintbug.lintTask("indentEqualSignsForPreClassVars", function(lintFile, callback) {
    var indentSpacing   = 4;
    var fileContents    = lintFile.getFileContents();
    var lines           = fileContents.split("\n");
    var startIndex      = bugcore.ArrayUtil.indexOf(lines, /^\s*\/\/ Context\s*$/);
    var endIndex        = bugcore.ArrayUtil.indexOf(lines, /^\s*\/\/ (Declare Class|Declare Interface)\s*$/);
    var varRegex        = /^(\s*)var (\w+)\s*=(.*)$/;
    var varObjects      = [];
    var longestIndent   = 0;
    if (startIndex > -1 && endIndex > -1) {
        for (var i = startIndex; i < endIndex; i++) {
            var line        = lines[i];
            var results     = line.match(varRegex);
            if (results) {
                var indent  = results[1];
                var varName = results[2];
                varObjects.push({
                    index: i,
                    indent: indent,
                    name: varName,
                    afterEqualsContent: results[3]
                });

                //NOTE BRN: Add 2 at the end so that we are at least two spaces away from the variable name
                var indentLength = Math.ceil((indent.length + ("var ").length + varName.length + 2) / indentSpacing);
                if (indentLength > longestIndent) {
                    longestIndent = indentLength;
                }
            }
        }
        varObjects.forEach(function(varObject) {
            var numberCharsBeforeEquals = longestIndent * indentSpacing;
            var preEqualsText = bugcore.StringUtil.rpad(varObject.indent + "var " + varObject.name, " ", numberCharsBeforeEquals);
            lines[varObject.index] = preEqualsText + "=" + varObject.afterEqualsContent;
        });
    }
    lintFile.setFileContents(lines.join("\n"));
    callback();
});

lintbug.lintTask("orderBugpackRequires", function(lintFile, callback) {
    var fileContents    = lintFile.getFileContents();
    fileContents = sortBugpackRequires(fileContents);
    lintFile.setFileContents(fileContents);
    callback();
});

lintbug.lintTask("orderRequireAnnotations", function(lintFile, callback) {
    var fileContents    = lintFile.getFileContents();
    fileContents = sortRequireAnnotations(fileContents);
    lintFile.setFileContents(fileContents);
    callback();
});

lintbug.lintTask("updateCopyright", function(lintFile, callback) {
    var fileContents    = lintFile.getFileContents();
    var copyright       = getCopyright();
    var copyrightRegex  = /^(\s*)\/\*(([^.]|[.])+?)Copyright \(c\)(([^.]|[.])+?)\*\/(\s*)/;
    if (copyrightRegex.test(fileContents)) {
        fileContents = fileContents.replace(copyrightRegex, copyright + "\n\n");
    } else {
        fileContents = copyright + "\n\n" + fileContents;
    }
    lintFile.setFileContents(fileContents);
    callback();
});

//-------------------------------------------------------------------------------
// Helper Methods
//-------------------------------------------------------------------------------

/**
 * @private
 * @param {string} fileContents
 * @returns {Array.<{index: number, line: string}>}
 */
var generateLines = function(fileContents) {
    return bugcore.StringUtil.split(fileContents, "\n", function(line, index) {
        return {
            index: index,
            line: line
        };
    });
};

var copyright = null;
/**
 * @private
 * @return {string}
 */
var getCopyright = function() {
    if (copyright === null) {
        var copyrightText   = bugcore.StringUtil.trim(bugfs.readFileSync(__dirname + "/COPYRIGHT", 'utf8'));
        var copyrightLines  = copyrightText.split("\n");
        copyright = "/*\n";
        copyrightLines.forEach(function(copyrightLine) {
            copyrightLine = bugcore.StringUtil.trim(copyrightLine);
            if (copyrightLine !== "") {
                copyright += " * " + copyrightLine + "\n";
            } else {
                copyright += " *\n";
            }
        });
        copyright += " */\n";
    }
    return copyright;
};

/**
 * @private
 * @param {Array.<{index: number, line: string}>} lines
 * @return {string}
 */
var mergeLines = function(lines) {
    var result = "";
    var first = true;
    lines.forEach(function(line) {
        if (first) {
            result += line.line;
            first = false;
        } else {
            result += "\n" + line.line;
        }
    });
    return result;
};

/**
 * @private
 * @param {string} fileContents
 * @return {string}
 */
var sortRequireAnnotations = function(fileContents) {
    var lines   = generateLines(fileContents);
    lines.sort(function(a, b) {
        var resultsA = a.line.match(/^\s*\/\/\s*@Require\(('|")((?:\w|\.)*)\1\)\s*$/);
        var resultsB = b.line.match(/^\s*\/\/\s*@Require\(('|")((?:\w|\.)*)\1\)\s*$/);

        if (resultsA && resultsB) {
            var partsA = resultsA[2].split(".");
            var partsB = resultsB[2].split(".");
            var classNameA = partsA.pop();
            var classNameB = partsB.pop();
            var packageNameA = partsA.join(".");
            var packageNameB = partsB.join(".");
            if (packageNameA < packageNameB) {
                return -1;
            }
            if (packageNameA > packageNameB) {
                return 1;
            }
            if (classNameA < classNameB) {
                return -1;
            }
            if (classNameA > classNameB) {
                return 1;
            }
        }
        if (a.index < b.index) {
            return -1;
        }
        return 1;
    });
    return mergeLines(lines);
};

/**
 * @private
 * @param {string} fileContents
 * @return {string}
 */
var sortBugpackRequires = function(fileContents) {
    var lines   = generateLines(fileContents);
    lines.sort(function(a, b) {
        var resultsA = a.line.match(/^\s*var \w+\s+=\s+bugpack\.require\(('|")((?:\w|\.)*)\1\);\s*$/);
        var resultsB = b.line.match(/^\s*var \w+\s+=\s+bugpack\.require\(('|")((?:\w|\.)*)\1\);\s*$/);

        if (resultsA && resultsB) {
            var partsA = resultsA[2].split(".");
            var partsB = resultsB[2].split(".");
            var classNameA = partsA.pop();
            var classNameB = partsB.pop();
            var packageNameA = partsA.join(".");
            var packageNameB = partsB.join(".");
            if (packageNameA < packageNameB) {
                return -1;
            }
            if (packageNameA > packageNameB) {
                return 1;
            }
            if (classNameA < classNameB) {
                return -1;
            }
            if (classNameA > classNameB) {
                return 1;
            }
        }
        if (a.index < b.index) {
            return -1;
        }
        return 1;
    });
    return mergeLines(lines);
};
<|endoftext|>