<commit_before>var util = require('util');

var logger = require('../logger')
  , config = require('../config')
  , doc = require('../doc.json');

var session = require('../app');

module.exports = {

    friendMsgHandler(steamID, msg, EChatEntryType) {
    // Message from friend
        var user = getUserName(steamID);
        logger.log('debug', "friendMsg event", { user, steamID, EChatEntryType });
        if (msg.length == 0)
            this.emit('isTyping', steamID, user, getUserState.call(this, steamID));
        else
            this.emit('message', steamID, user, getUserState.call(this, steamID), msg);
    },

    chatMsgHandler(chatID, msg, EChatEntryType, steamID) {
    // Message in group chat
        var user = getUserName(steamID);
        logger.log('debug', "chatMsg event", { chatID, user, steamID, EChatEntryType });

        clearTimeout(session.ghostTimer);
        session.ghostTimer = setTimeout(
            checkIfGhostChat.bind(this, chatID),
            config.get('ghostcheck')
        );

        this.emit('groupMessage', chatID, user, getUserState.call(this, steamID), msg);
    },

    personaStateHandler(friend) {
    // Someone has made changes to their persona
        logger.log('silly', "personaState event", friend);

        if (friend.friendid == session.steamID) {
            session.steamNick = friend.player_name;
            this.emit('updateNick');
        }

        session.users[friend.friendid] = {
            persona_state: getPersonaState.call(this, friend.persona_state, friend.game_name),
            player_name: friend.player_name,
            game_name: friend.game_name
        };

        this.emit('listUpdate');
    },

    clanStateHandler(clanState) {
    // A group has changed their clan state
        logger.log('debug', "clanState event", clanState);

        var e = session.clans[clanState.steamid_clan];
        if (e === undefined) e = {};
        e.clan_name = clanState.name_info.clan_name;
        e.user_count = clanState.user_counts.chatting;
    },

    chatEnterHandler(chatID, EChatRoomEnterResponse) {
    // You just entered a group chat. On success, chatRooms is now populated with chatID.
        logger.log('debug', "chatEnter event", { chatID, EChatRoomEnterResponse });

        var res = this.Steam.EChatRoomEnterResponse;
        switch (EChatRoomEnterResponse) {
            case res.Success:
                this.emit('enteredChat', chatID);
                var e = this.friends.chatRooms[chatID];
                var count = 0;
                for (var steamID in e) {
                    session.clans[chatID].members[steamID] =
                        getRank.call(this, e[steamID].rank);
                    count++;
                }
                session.clans[chatID].user_live = count;
                break;
            case res.Banned: case res.CommunityBan:
                logger.log('info', doc.msg.bannedFromChat);
                break;
        }
    },

    chatInviteHandler(chatID, chatName, steamID) {
    // Received invite to group chat.
        var user = getUserName(steamID);
        logger.log('debug', "chatInvite event", { chatID, chatName, steamID, user });

        session.lastInvite = chatID;
        logger.log('info', doc.msg.chatInvite, chatName, user);
    },

    friendHandler(steamID, EFriendRelationship) {
    // Activity in friends list.
        var user = getUserName(steamID);
        logger.log('debug', "friend event", { user, steamID, FriendRelationship });

        var fr = this.Steam.EFriendRelationship;
        switch (EFriendRelationship) {
            case fr.None: // steamID has removed you as a friend
                removeFriendsElement(steamID);
                var e = this.Steam.EPersonaState;
                if (session.users[steamID].persona_state == e.Offline)
                    delete session.users[steamID];
                break;
            case fr.RequestRecipient: // steamID has sent you a friend request
                logger.log('info', doc.msg.friendRequest, user);
                break;
            case fr.Friend: // steamID is now your friend
                session.friends.push(steamID);
                break;
        }
        this.emit('listUpdate');
    },

    chatStateChangeHandler(MemberStateChange, steamID, chatID, actorID) {
    // Something happened in group chat/clan.
        logger.log('debug', "chatStateChange event", { MemberStateChange, steamID, chatID, actorID });

        var text = getStateChangeText.call(this,
            MemberStateChange,
            steamID == session.steamID,
            getUserName(steamID),
            getUserName(actorID)
        );

        if (text !== null)
            this.emit('activity', chatID, text);

        if (MemberStateChange === this.Steam.EChatMemberStateChange.Entered) {
            var clan = session.clans[chatID];
            clan.members[steamID] =
                getRank.call(this, this.friends.chatRooms[chatID][steamID].rank);
            clan.user_live++;
        } else { // user left/disconnected or got kicked/banned.
            if (steamID == session.steamID) { // user is you
                for (var memberID in session.clans[chatID].members) {
                    var i = session.friends.indexOf(memberID);
                    if (i < 0 && memberID != session.steamID)
                        delete session.users[memberID];
                }
                delete session.clans[chatID];
                clearTimeout(session.ghostTimer);
            } else { // user is not you
                if (session.users.hasOwnProperty(steamID)) {
                    var i = session.friends.indexOf(steamID);
                    if (i < 0) delete session.users[steamID];
                }
                var clan = session.clans[chatID];
                delete clan.members[steamID];
                clan.user_live--;
            }
        }

        this.emit('listUpdate');
    },

    relationshipsHandler() {
    // Emits once on login. Check if there are pending friend requests and set which users are your friends. this.friends.friends is now populated.
        logger.log('debug', "relationships event emitted");

        var friends = this.friends.friends;
        var fr = this.Steam.EFriendRelationship;
        for (var steamID in friends) {
            switch (friends[steamID]) {
                case fr.None: // not a friend
                    logger.log('debug', "IMPORTANT: What?? Someone whom isn't a friend is in the friend object!?", friends[steamID]);
                    removeFriendsElement(steamID);
                    break;
                case fr.RequestRecipient: // pending friend request
                    logger.log('info', doc.msg.pendingFriendRequests);
                    break;
                case fr.Friend: // your friend
                    session.friends.push(steamID);
                    break;
            }
        }
        this.emit('listUpdate');
    }

};

function checkIfGhostChat(chatID) {
    logger.log('debug', "Checking if chatID %s is a ghost chat.", chatID);
    if (session.clans.hasOwnProperty(chatID)) {
        var clan = session.clans[chatID];
        if (clan.user_count !== clan.user_live) {
            logger.log('verbose', "Ghost chat detected on chatID %s. Rejoining chat.", chatID);
            this.friends.joinChat(chatID);
        }
    }
}

function removeFriendsElement(steamID) {
    var i = session.friends.indexOf(steamID);
    if (i > -1) session.friends.splice(i, 1);
}

function getUserState(steamID) {
    var friend = this.friends.personaStates[steamID];
    return getPersonaState.call(this, friend.persona_state, friend.game_name);
}

function getPersonaState(persona, game) {
    if (game.length > 0) {
        return "ingame";
    } else {
        var e = this.Steam.EPersonaState;
        switch (persona) {
            case e.Offline:        return "offline";
            case e.Online:         return "online";
            case e.Busy:           return "busy";
            case e.Away:           return "away";
            case e.Snooze:         return "snooze";
            case e.LookingToTrade: return "lookingToTrade";
            case e.LookingToPlay:  return "lookingToPlay";
            default:               return "undefined";
        }
    }
}

function getStateChangeText(MemberStateChange, youBool, user, actor) {
    var e = this.Steam.EChatMemberStateChange;
    switch (MemberStateChange) {
        case e.Entered:
            if (!youBool)
                return `${user} entered chat.`;
            else
                return null;
        case e.Left:
            if (!youBool)
                return `${user} left chat.`;
            else
                return null;
        case e.Disconnected:
            if (!youBool)
                return `${user} disconnected.`;
            else
                return null;
        case e.Kicked:
            if (!youBool)
                return `${user} was kicked by ${actor}.`;
            else
                return `You have been kicked from the chat by ${actor}.`;
        case e.Banned:
            if (!youBool)
                return `${user} was banned by ${actor}.`;
            else
                return `You have been banned from the chat by ${actor}.`;
        default:
            return null;
    }
}

function getRank(ClanPermission) {
    var e = this.Steam.EClanPermission;
    switch (ClanPermission) {
        case e.Owner:
        case e.Officer:
        case e.OwnerAndOfficer: return "admin";
        case e.Moderator:       return "moderator";
        case e.Member:          return "member";
        case e.Nobody:          return "nobody";
        case e.OGGGameOwner:    return "gameowner";
        case e.NonMember:       return "nonmember";
        default:                return "undefined";
    }
}

function getUserName(steamID) {
    var user = "undefined";

    if (session.users.hasOwnProperty(steamID)) {
        if (session.users[steamID].hasOwnProperty('player_name')) {
            user = session.users[steamID].player_name;
            logger.log('debug', "IMPORTANT: session.users works fine! You can deprecate lib/steam/friendsHandler.js:getUserName() and possibly getUserState().");
        }
    }

    return user;
}
<commit_msg>Fix crash from incomplete clanState object by using safeGet<commit_after>var util = require('util');

var logger = require('../logger')
  , config = require('../config')
  , doc = require('../doc.json');

var session = require('../app')
  , { safeGet } = require('../helpers');

module.exports = {

    friendMsgHandler(steamID, msg, EChatEntryType) {
    // Message from friend
        var user = getUserName(steamID);
        logger.log('debug', "friendMsg event", { user, steamID, EChatEntryType });
        if (msg.length == 0)
            this.emit('isTyping', steamID, user, getUserState.call(this, steamID));
        else
            this.emit('message', steamID, user, getUserState.call(this, steamID), msg);
    },

    chatMsgHandler(chatID, msg, EChatEntryType, steamID) {
    // Message in group chat
        var user = getUserName(steamID);
        logger.log('debug', "chatMsg event", { chatID, user, steamID, EChatEntryType });

        clearTimeout(session.ghostTimer);
        session.ghostTimer = setTimeout(
            checkIfGhostChat.bind(this, chatID),
            config.get('ghostcheck')
        );

        this.emit('groupMessage', chatID, user, getUserState.call(this, steamID), msg);
    },

    personaStateHandler(friend) {
    // Someone has made changes to their persona
        logger.log('silly', "personaState event", friend);

        if (friend.friendid == session.steamID) {
            session.steamNick = friend.player_name;
            this.emit('updateNick');
        }

        session.users[friend.friendid] = {
            persona_state: getPersonaState.call(this, friend.persona_state, friend.game_name),
            player_name: friend.player_name,
            game_name: friend.game_name
        };

        this.emit('listUpdate');
    },

    clanStateHandler(clanState) {
    // A group has changed their clan state
        logger.log('debug', "clanState event", clanState);

        const ID = clanState.steamid_clan;
        const name = safeGet(clanState, 'name_info', 'clan_name');
        const count = safeGet(clanState, 'user_counts', 'chatting');

        const clan = Object.assign({}, session.clans[ID]);
        session.clans[ID] = Object.assign({}, clan, {
            clan_name: name || safeGet(clan, 'clan_name'),
            user_count: count || safeGet(clan, 'user_count')
        });

    },

    chatEnterHandler(chatID, EChatRoomEnterResponse) {
    // You just entered a group chat. On success, chatRooms is now populated with chatID.
        logger.log('debug', "chatEnter event", { chatID, EChatRoomEnterResponse });

        var res = this.Steam.EChatRoomEnterResponse;
        switch (EChatRoomEnterResponse) {
            case res.Success:
                this.emit('enteredChat', chatID);
                var e = this.friends.chatRooms[chatID];
                var count = 0;
                for (var steamID in e) {
                    session.clans[chatID].members[steamID] =
                        getRank.call(this, e[steamID].rank);
                    count++;
                }
                session.clans[chatID].user_live = count;
                break;
            case res.Banned: case res.CommunityBan:
                logger.log('info', doc.msg.bannedFromChat);
                break;
        }
    },

    chatInviteHandler(chatID, chatName, steamID) {
    // Received invite to group chat.
        var user = getUserName(steamID);
        logger.log('debug', "chatInvite event", { chatID, chatName, steamID, user });

        session.lastInvite = chatID;
        logger.log('info', doc.msg.chatInvite, chatName, user);
    },

    friendHandler(steamID, EFriendRelationship) {
    // Activity in friends list.
        var user = getUserName(steamID);
        logger.log('debug', "friend event", { user, steamID, FriendRelationship });

        var fr = this.Steam.EFriendRelationship;
        switch (EFriendRelationship) {
            case fr.None: // steamID has removed you as a friend
                removeFriendsElement(steamID);
                var e = this.Steam.EPersonaState;
                if (session.users[steamID].persona_state == e.Offline)
                    delete session.users[steamID];
                break;
            case fr.RequestRecipient: // steamID has sent you a friend request
                logger.log('info', doc.msg.friendRequest, user);
                break;
            case fr.Friend: // steamID is now your friend
                session.friends.push(steamID);
                break;
        }
        this.emit('listUpdate');
    },

    chatStateChangeHandler(MemberStateChange, steamID, chatID, actorID) {
    // Something happened in group chat/clan.
        logger.log('debug', "chatStateChange event", { MemberStateChange, steamID, chatID, actorID });

        var text = getStateChangeText.call(this,
            MemberStateChange,
            steamID == session.steamID,
            getUserName(steamID),
            getUserName(actorID)
        );

        if (text !== null)
            this.emit('activity', chatID, text);

        if (MemberStateChange === this.Steam.EChatMemberStateChange.Entered) {
            var clan = session.clans[chatID];
            clan.members[steamID] =
                getRank.call(this, this.friends.chatRooms[chatID][steamID].rank);
            clan.user_live++;
        } else { // user left/disconnected or got kicked/banned.
            if (steamID == session.steamID) { // user is you
                for (var memberID in session.clans[chatID].members) {
                    var i = session.friends.indexOf(memberID);
                    if (i < 0 && memberID != session.steamID)
                        delete session.users[memberID];
                }
                delete session.clans[chatID];
                clearTimeout(session.ghostTimer);
            } else { // user is not you
                if (session.users.hasOwnProperty(steamID)) {
                    var i = session.friends.indexOf(steamID);
                    if (i < 0) delete session.users[steamID];
                }
                var clan = session.clans[chatID];
                delete clan.members[steamID];
                clan.user_live--;
            }
        }

        this.emit('listUpdate');
    },

    relationshipsHandler() {
    // Emits once on login. Check if there are pending friend requests and set which users are your friends. this.friends.friends is now populated.
        logger.log('debug', "relationships event emitted");

        var friends = this.friends.friends;
        var fr = this.Steam.EFriendRelationship;
        for (var steamID in friends) {
            switch (friends[steamID]) {
                case fr.None: // not a friend
                    logger.log('debug', "IMPORTANT: What?? Someone whom isn't a friend is in the friend object!?", friends[steamID]);
                    removeFriendsElement(steamID);
                    break;
                case fr.RequestRecipient: // pending friend request
                    logger.log('info', doc.msg.pendingFriendRequests);
                    break;
                case fr.Friend: // your friend
                    session.friends.push(steamID);
                    break;
            }
        }
        this.emit('listUpdate');
    }

};

function checkIfGhostChat(chatID) {
    logger.log('debug', "Checking if chatID %s is a ghost chat.", chatID);
    if (session.clans.hasOwnProperty(chatID)) {
        var clan = session.clans[chatID];
        if (clan.user_count !== clan.user_live) {
            logger.log('verbose', "Ghost chat detected on chatID %s. Rejoining chat.", chatID);
            this.friends.joinChat(chatID);
        }
    }
}

function removeFriendsElement(steamID) {
    var i = session.friends.indexOf(steamID);
    if (i > -1) session.friends.splice(i, 1);
}

function getUserState(steamID) {
    var friend = this.friends.personaStates[steamID];
    return getPersonaState.call(this, friend.persona_state, friend.game_name);
}

function getPersonaState(persona, game) {
    if (game.length > 0) {
        return "ingame";
    } else {
        var e = this.Steam.EPersonaState;
        switch (persona) {
            case e.Offline:        return "offline";
            case e.Online:         return "online";
            case e.Busy:           return "busy";
            case e.Away:           return "away";
            case e.Snooze:         return "snooze";
            case e.LookingToTrade: return "lookingToTrade";
            case e.LookingToPlay:  return "lookingToPlay";
            default:               return "undefined";
        }
    }
}

function getStateChangeText(MemberStateChange, youBool, user, actor) {
    var e = this.Steam.EChatMemberStateChange;
    switch (MemberStateChange) {
        case e.Entered:
            if (!youBool)
                return `${user} entered chat.`;
            else
                return null;
        case e.Left:
            if (!youBool)
                return `${user} left chat.`;
            else
                return null;
        case e.Disconnected:
            if (!youBool)
                return `${user} disconnected.`;
            else
                return null;
        case e.Kicked:
            if (!youBool)
                return `${user} was kicked by ${actor}.`;
            else
                return `You have been kicked from the chat by ${actor}.`;
        case e.Banned:
            if (!youBool)
                return `${user} was banned by ${actor}.`;
            else
                return `You have been banned from the chat by ${actor}.`;
        default:
            return null;
    }
}

function getRank(ClanPermission) {
    var e = this.Steam.EClanPermission;
    switch (ClanPermission) {
        case e.Owner:
        case e.Officer:
        case e.OwnerAndOfficer: return "admin";
        case e.Moderator:       return "moderator";
        case e.Member:          return "member";
        case e.Nobody:          return "nobody";
        case e.OGGGameOwner:    return "gameowner";
        case e.NonMember:       return "nonmember";
        default:                return "undefined";
    }
}

function getUserName(steamID) {
    var user = "undefined";

    if (session.users.hasOwnProperty(steamID)) {
        if (session.users[steamID].hasOwnProperty('player_name')) {
            user = session.users[steamID].player_name;
            logger.log('debug', "IMPORTANT: session.users works fine! You can deprecate lib/steam/friendsHandler.js:getUserName() and possibly getUserState().");
        }
    }

    return user;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2013 BlackBerry Ltd.
 *
 * 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.
 */
var ProjectInfoView,
    projectInfoTemplate,
    AccessListView = require("./accessList"),
    IconSplashListView = require("./iconSplashList"),
    PermissionsListView = require("./permissionsList");

function updatePref(config, name, value) {
    var newPref = true,
        valueMap = {
            "on": "true",
            "off": "false"
        };

    // Use value in the map if it exists
    value = valueMap[value] || value;

    config.widget.preference.forEach(function (pref) {
        if (pref.name === name) {
            pref.value = value;
            newPref = false;
        }
    });

    if (newPref) {
        config.widget.preference.push({
            name: name,
            value: value
        });
    }
}

function preparePrefs(config) {
    // Set default values
    var prefs = {
            appBackgroundColor: "#FFFFFF",
            autoHideSplashScreen: "true",
            diskCache: "false",
            orientation: "default",
            childBrowser: "true",
            hideKeyboardFormBar: "false",
            popupBlocker: "false",
            webSecurity: "true"
        };

    config.widget.preference = config.widget.preference || [];

    config.widget.preference.forEach(function (pref) {
        if (pref.name === "BackgroundColor") {
            prefs.appBackgroundColor = "#" + pref.value.substring(4);
        } else if (pref.name === "AutoHideSplashScreen") {
            prefs.autoHideSplashScreen = pref.value;
        } else if (pref.name === "DiskCache") {
            prefs.diskCache = (pref.value === "enable") ? "true" : "false";
        } else if (pref.name === "Orientation") {
            prefs.orientation = pref.value;
        } else if (pref.name === "ChildBrowser") {
            prefs.childBrowser = (pref.value === "enable") ? "true" : "false";
        } else if (pref.name === "HideKeyboardFormAccesoryBar") {
            prefs.hideKeyboardFormBar = pref.value;
        } else if (pref.name === "PopupBlocker") {
            prefs.popupBlocker = (pref.value === "enable") ? "true" : "false";
        } else if (pref.name === "WebSecurity") {
            prefs.webSecurity = (pref.value === "enable") ? "true" : "false";
        }
    });

    return prefs;
}

function renderSubviews(model) {
    // Access list
    window.views.accessList.setElement($("#accessList"));
    window.views.accessList.display(model);

    // Icon List
    window.views.iconList.setElement($("#icon"));
    window.views.iconList.display(model);

    // Splash List
    window.views.splashList.setElement($("#splash"));
    window.views.splashList.display(model);

    // Permissions List
    window.views.permissionsList.setElement($("#permissions"));
    window.views.permissionsList.display(model);
}

ProjectInfoView = Backbone.View.extend({
    initialize: function () {
        // Find the template and save it
        $.get("pages/info.html", function (data) {
            projectInfoTemplate = data;
        });

        window.views.accessList = new AccessListView();
        window.views.iconList = new IconSplashListView({type: "icon"});
        window.views.permissionsList = new PermissionsListView();
        window.views.splashList = new IconSplashListView({type: "rim$splash"});
    },
    render: function () {
        // Use the template to create the html to display
        var view = this;

        // Put the new html inside of the assigned element
        this.model.getConfigXml(function (config) {
            // Set the models
            view.model.set("config", config);
            view.dirty = false;

            // Add location, just for the view
            var template = _.template(projectInfoTemplate, {
                widget: config.widget,
                location: view.model.get("location"),
                prefs: preparePrefs(config)
            });
            view.$el.html(template);

            // Handle access list, icon, and splash
            renderSubviews(view.model);
        });
    },
    display: function (model) {
        this.model = model;
        window.views.projectList.setActiveProject(model);
        this.render();
    },
    events: {
        "change #configForm input" : "updateConfig",
        "change #configForm select" : "updateConfig",
        "click #saveConfig" : "saveConfigToServer"
    },
    updateConfig: function (event) {
        var element = $(event.currentTarget)[0],
            type = element.getAttribute("name"),
            config = this.model.get("config");

        if (type === "appId") {
            config.widget.id = element.value;
        } else if (type === "appName") {
            config.widget.name.$t = element.value;
        } else if (type === "appDescription") {
            config.widget.description.$t = element.value;
        } else if (type === "author") {
            config.widget.author.$t = element.value;
        } else if (type === "authorEmail") {
            config.widget.author.email = element.value;
        } else if (type === "authorUrl") {
            config.widget.author.href = element.value;
        } else if (type === "appBackgroundColor") {
            updatePref(config, "BackgroundColor", "0xff" + element.value.substring(1));
        } else if (type === "license") {
            if (!config.widget.license) {
                config.widget.license = {};
            }
            config.widget.license.href = element.value;
        } else if (type === "mainUrl") {
            if (!config.widget.content) {
                config.widget.content = {};
            }
            config.widget.content.src = element.value;
        } else if (type === "autohideSplash") {
            updatePref(config, "AutoHideSplashScreen", element.checked.toString());
        } else if (type === "childBrowser") {
            updatePref(config, "ChildBrowser", element.checked ? "enable" : "disable");
        } else if (type === "diskCache") {
            updatePref(config, "DiskCache", element.checked ? "enable" : "disable");
        } else if (type === "hideKeyboardFormBar") {
            updatePref(config, "HideKeyboardFormAccessoryBar", element.checked.toString());
        } else if (type === "orientation") {
            updatePref(config, "Orientation", element.value);
        } else if (type === "popupBlocker") {
            updatePref(config, "PopupBlocker", element.checked ? "enable" : "disable");
        } else if (type === "webSecurity") {
            updatePref(config, "WebSecurity", element.checked ? "enable" : "disable");
        }

        // Save to model
        this.model.set("config", config);
        this.dirty = true;
    },
    saveConfigToServer: function () {
        var view = this,
            model = this.model;

        model.setConfigXml(function (result) {
            view.dirty = false;
            alert(result.success ? "Successfully saved" : result.error);
            if (result.success) {
                model.set("projectId", model.get("config").widget.id);
                model.sync("update");
            }
        });
    }
});

module.exports = ProjectInfoView;
<commit_msg>App ID updates in project list and info page after clicking save<commit_after>/*
 * Copyright 2013 BlackBerry Ltd.
 *
 * 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.
 */
var ProjectInfoView,
    projectInfoTemplate,
    AccessListView = require("./accessList"),
    IconSplashListView = require("./iconSplashList"),
    PermissionsListView = require("./permissionsList");

function updatePref(config, name, value) {
    var newPref = true,
        valueMap = {
            "on": "true",
            "off": "false"
        };

    // Use value in the map if it exists
    value = valueMap[value] || value;

    config.widget.preference.forEach(function (pref) {
        if (pref.name === name) {
            pref.value = value;
            newPref = false;
        }
    });

    if (newPref) {
        config.widget.preference.push({
            name: name,
            value: value
        });
    }
}

function preparePrefs(config) {
    // Set default values
    var prefs = {
            appBackgroundColor: "#FFFFFF",
            autoHideSplashScreen: "true",
            diskCache: "false",
            orientation: "default",
            childBrowser: "true",
            hideKeyboardFormBar: "false",
            popupBlocker: "false",
            webSecurity: "true"
        };

    config.widget.preference = config.widget.preference || [];

    config.widget.preference.forEach(function (pref) {
        if (pref.name === "BackgroundColor") {
            prefs.appBackgroundColor = "#" + pref.value.substring(4);
        } else if (pref.name === "AutoHideSplashScreen") {
            prefs.autoHideSplashScreen = pref.value;
        } else if (pref.name === "DiskCache") {
            prefs.diskCache = (pref.value === "enable") ? "true" : "false";
        } else if (pref.name === "Orientation") {
            prefs.orientation = pref.value;
        } else if (pref.name === "ChildBrowser") {
            prefs.childBrowser = (pref.value === "enable") ? "true" : "false";
        } else if (pref.name === "HideKeyboardFormAccesoryBar") {
            prefs.hideKeyboardFormBar = pref.value;
        } else if (pref.name === "PopupBlocker") {
            prefs.popupBlocker = (pref.value === "enable") ? "true" : "false";
        } else if (pref.name === "WebSecurity") {
            prefs.webSecurity = (pref.value === "enable") ? "true" : "false";
        }
    });

    return prefs;
}

function renderSubviews(model) {
    // Access list
    window.views.accessList.setElement($("#accessList"));
    window.views.accessList.display(model);

    // Icon List
    window.views.iconList.setElement($("#icon"));
    window.views.iconList.display(model);

    // Splash List
    window.views.splashList.setElement($("#splash"));
    window.views.splashList.display(model);

    // Permissions List
    window.views.permissionsList.setElement($("#permissions"));
    window.views.permissionsList.display(model);
}

ProjectInfoView = Backbone.View.extend({
    initialize: function () {
        // Find the template and save it
        $.get("pages/info.html", function (data) {
            projectInfoTemplate = data;
        });

        window.views.accessList = new AccessListView();
        window.views.iconList = new IconSplashListView({type: "icon"});
        window.views.permissionsList = new PermissionsListView();
        window.views.splashList = new IconSplashListView({type: "rim$splash"});
    },
    render: function () {
        // Use the template to create the html to display
        var view = this;

        // Put the new html inside of the assigned element
        this.model.getConfigXml(function (config) {
            // Set the models
            view.model.set("config", config);
            view.dirty = false;

            // Add location, just for the view
            var template = _.template(projectInfoTemplate, {
                widget: config.widget,
                location: view.model.get("location"),
                prefs: preparePrefs(config)
            });
            view.$el.html(template);

            // Handle access list, icon, and splash
            renderSubviews(view.model);
        });
    },
    display: function (model) {
        this.model = model;
        window.views.projectList.setActiveProject(model);
        this.render();
    },
    events: {
        "change #configForm input" : "updateConfig",
        "change #configForm select" : "updateConfig",
        "click #saveConfig" : "saveConfigToServer"
    },
    updateConfig: function (event) {
        var element = $(event.currentTarget)[0],
            type = element.getAttribute("name"),
            config = this.model.get("config");

        if (type === "appId") {
            config.widget.id = element.value;
        } else if (type === "appName") {
            config.widget.name.$t = element.value;
        } else if (type === "appDescription") {
            config.widget.description.$t = element.value;
        } else if (type === "author") {
            config.widget.author.$t = element.value;
        } else if (type === "authorEmail") {
            config.widget.author.email = element.value;
        } else if (type === "authorUrl") {
            config.widget.author.href = element.value;
        } else if (type === "appBackgroundColor") {
            updatePref(config, "BackgroundColor", "0xff" + element.value.substring(1));
        } else if (type === "license") {
            if (!config.widget.license) {
                config.widget.license = {};
            }
            config.widget.license.href = element.value;
        } else if (type === "mainUrl") {
            if (!config.widget.content) {
                config.widget.content = {};
            }
            config.widget.content.src = element.value;
        } else if (type === "autohideSplash") {
            updatePref(config, "AutoHideSplashScreen", element.checked.toString());
        } else if (type === "childBrowser") {
            updatePref(config, "ChildBrowser", element.checked ? "enable" : "disable");
        } else if (type === "diskCache") {
            updatePref(config, "DiskCache", element.checked ? "enable" : "disable");
        } else if (type === "hideKeyboardFormBar") {
            updatePref(config, "HideKeyboardFormAccessoryBar", element.checked.toString());
        } else if (type === "orientation") {
            updatePref(config, "Orientation", element.value);
        } else if (type === "popupBlocker") {
            updatePref(config, "PopupBlocker", element.checked ? "enable" : "disable");
        } else if (type === "webSecurity") {
            updatePref(config, "WebSecurity", element.checked ? "enable" : "disable");
        }

        // Save to model
        this.model.set("config", config);
        this.dirty = true;
    },
    saveConfigToServer: function () {
        var view = this,
            model = this.model;

        model.setConfigXml(function (result) {
            view.dirty = false;
            alert(result.success ? "Successfully saved" : result.error);
            if (result.success) {
                model.set("projectId", model.get("config").widget.id);
                model.sync("update");
            }
            window.views.projectList.render();
            view.render();
        });
    }
});

module.exports = ProjectInfoView;
<|endoftext|>


<commit_before>var _ = require('lodash');
var wrench = require('wrench');
var fs = require('fs');
var ncp = require('ncp').ncp;

module.exports = function (params, options, callback) {
  var self = this;

  if (!callback) {
    callback = function () {};
  }

  options.target = options.target || 'build/';

  this.build(function (err, bundle) {
    if (err) {
      if (err.code === 404) {
        console.log('Module ' + err.base + ' ' + err.path + 'could not be resolved.\n' +
        'If it is a package you might need to install it trough bower or npm.');
      }

      return callback(err);
    }

    wrench.rmdirSyncRecursive(process.cwd() + '/' + options.target, true);
    wrench.mkdirSyncRecursive(process.cwd() + '/' + options.target);

    fs.writeFileSync(process.cwd()
    + '/'
    + options.target.replace(/\/$/, '')
    + '/version', options.version);

    var index = self.getIndex(options);

    if (index) {
      fs.writeFileSync(process.cwd()
      + '/'
      + options.target.replace(/\/$/, '')
      + '/index.html', index);
    }

    (function nextModule(i) {
      var module = bundle.modules[i];
      if (!module) {
        self._getAssetDirs(function (err, dirs) {
          if (err) {
            return callback(err);
          }

          (function nextAssetDir(i) {
            var assetDir = dirs[i];
            if (!assetDir) {
              return callback(null, bundle);
            }

            ncp(
              assetDir.replace(/\/$/, ''),
              process.cwd() + '/' + options.target.replace(/\/$/, '') + '/assets/',
              function (err) {
                if (err) {
                  return callback(err);
                }

                nextAssetDir(++i);
            });
          })(0);
        });
        return;
      }

      fs.writeFileSync(
        process.cwd()
        + '/'
        + options.target.replace(/\/$/, '')
        + '/'
        + module.output_name.replace(/\.js$/, '')
        + '.map', JSON.stringify(module.getSourceMap()));

      var writeStream = fs.createWriteStream(
        process.cwd()
        + '/'
        + options.target.replace(/\/$/, '')
        + '/'
        + module.output_name.replace(/\.js$/, '')
        + '.js');

      var output = module.output();
      output.on('end', function () {
        nextModule(++i);
      });
      output.pipe(writeStream);
    })(0);
  });
};
<commit_msg>Temp fix for filenames<commit_after>var _ = require('lodash');
var wrench = require('wrench');
var fs = require('fs');
var ncp = require('ncp').ncp;

module.exports = function (params, options, callback) {
  var self = this;

  if (!callback) {
    callback = function () {};
  }

  options.target = options.target || 'build/';

  this.build(function (err, bundle) {
    if (err) {
      if (err.code === 404) {
        console.log('Module ' + err.base + ' ' + err.path + 'could not be resolved.\n' +
        'If it is a package you might need to install it trough bower or npm.');
      }

      return callback(err);
    }

    wrench.rmdirSyncRecursive(process.cwd() + '/' + options.target, true);
    wrench.mkdirSyncRecursive(process.cwd() + '/' + options.target);

    fs.writeFileSync(process.cwd()
    + '/'
    + options.target.replace(/\/$/, '')
    + '/version', options.version);

    var index = self.getIndex(options);

    if (index) {
      fs.writeFileSync(process.cwd()
      + '/'
      + options.target.replace(/\/$/, '')
      + '/index.html', index);
    }

    (function nextModule(i) {
      var module = bundle.modules[i];
      if (!module) {
        self._getAssetDirs(function (err, dirs) {
          if (err) {
            return callback(err);
          }

          (function nextAssetDir(i) {
            var assetDir = dirs[i];
            if (!assetDir) {
              return callback(null, bundle);
            }

            ncp(
              assetDir.replace(/\/$/, ''),
              process.cwd() + '/' + options.target.replace(/\/$/, '') + '/assets/',
              function (err) {
                if (err) {
                  return callback(err);
                }

                nextAssetDir(++i);
            });
          })(0);
        });
        return;
      }

      fs.writeFileSync(
        process.cwd()
        + '/'
        + options.target.replace(/\/$/, '')
        + '/'
        + module.output_name.replace(/\.js$/, '')
        + '.map', JSON.stringify(module.getSourceMap()));

      var writeStream = fs.createWriteStream(
        process.cwd()
        + '/'
        + options.target.replace(/\/$/, '')
        + '/'
        + module.output_name.replace(/\.js$/, '')
        + (i === 0 ? '.js' : ''));

      var output = module.output();
      output.on('end', function () {
        nextModule(++i);
      });
      output.pipe(writeStream);
    })(0);
  });
};
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';

import Octicon from './octicon';

const typeAndStateToIcon = {
  Issue: {
    OPEN: 'issue-opened',
    CLOSED: 'issue-closed',
  },
  PullRequest: {
    OPEN: 'git-pull-request',
    CLOSED: 'git-pull-request',
    MERGED: 'git-merge',
  },
};

export default function IssueishBadge({type, state, ...others}) {
  const icons = typeAndStateToIcon[type] || {};
  const icon = icons[state] || '';

  return (

      <Octicon icon={icon} />
      {state.toLowerCase()}

  );
}

IssueishBadge.propTypes = {
  type: PropTypes.oneOf([
    'Issue', 'PullRequest',
  ]),
  state: PropTypes.oneOf([
    'OPEN', 'CLOSED', 'MERGED',
  ]),
};
<commit_msg>Fix up various prop issues with IssueishBadge<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';

import Octicon from './octicon';

const typeAndStateToIcon = {
  Issue: {
    OPEN: 'issue-opened',
    CLOSED: 'issue-closed',
  },
  PullRequest: {
    OPEN: 'git-pull-request',
    CLOSED: 'git-pull-request',
    MERGED: 'git-merge',
  },
};

export default function IssueishBadge({type, state, ...others}) {
  const icons = typeAndStateToIcon[type] || {};
  const icon = icons[state] || '';

  const {className, ...otherProps} = others;
  return (

      <Octicon icon={icon} />
      {state.toLowerCase()}

  );
}

IssueishBadge.propTypes = {
  type: PropTypes.oneOf([
    'Issue', 'PullRequest',
  ]).isRequired,
  state: PropTypes.oneOf([
    'OPEN', 'CLOSED', 'MERGED',
  ]).isRequired,
};
<|endoftext|>


<commit_before>import {autobind} from 'core-decorators';

const COPY = {};

export default class ListSelection {
  constructor(options = {}) {
    if (options._copy !== COPY) {
      this.options = {
        isItemSelectable: options.isItemSelectable || (item => true),
      };
      this.setItems(options.items || []);
    } else {
      this.options = {
        isItemSelectable: options.isItemSelectable,
      };
      this.items = options.items;
      this.selections = options.selections;
    }
  }

  copy() {
    // Deep-copy selections because it will be modified.
    // (That's temporary, until ListSelection is changed to be immutable, too.)
    return new ListSelection({
      _copy: COPY,
      isItemSelectable: this.options.isItemSelectable,
      items: this.items,
      selections: this.selections.map(({head, tail, negate}) => ({head, tail, negate})),
    });
  }

  @autobind
  isItemSelectable(item) {
    return this.options.isItemSelectable(item);
  }

  setItems(items) {
    let newSelectionIndex;
    if (this.selections && this.selections.length > 0) {
      const [{head, tail}] = this.selections;
      newSelectionIndex = Math.min(head, tail, items.length - 1);
    } else {
      newSelectionIndex = 0;
    }

    this.items = items;
    if (items.length > 0) {
      this.selections = [{head: newSelectionIndex, tail: newSelectionIndex}];
    } else {
      this.selections = [];
    }
  }

  getItems() {
    return this.items;
  }

  getLastItem() {
    return this.items[this.items.length - 1];
  }

  selectFirstItem(preserveTail) {
    for (let i = 0; i < this.items.length; i++) {
      const item = this.items[i];
      if (this.isItemSelectable(item)) {
        this.selectItem(item, preserveTail);
        break;
      }
    }
  }

  selectLastItem(preserveTail) {
    for (let i = this.items.length - 1; i > 0; i--) {
      const item = this.items[i];
      if (this.isItemSelectable(item)) {
        this.selectItem(item, preserveTail);
        break;
      }
    }
  }

  selectAllItems() {
    this.selectFirstItem();
    this.selectLastItem(true);
  }

  selectNextItem(preserveTail) {
    if (this.selections.length === 0) {
      this.selectFirstItem();
      return;
    }

    let itemIndex = this.selections[0].head;
    let nextItemIndex = itemIndex;
    while (itemIndex < this.items.length - 1) {
      itemIndex++;
      if (this.isItemSelectable(this.items[itemIndex])) {
        nextItemIndex = itemIndex;
        break;
      }
    }

    this.selectItem(this.items[nextItemIndex], preserveTail);
  }

  selectPreviousItem(preserveTail) {
    if (this.selections.length === 0) {
      this.selectLastItem();
      return;
    }

    let itemIndex = this.selections[0].head;
    let previousItemIndex = itemIndex;

    while (itemIndex > 0) {
      itemIndex--;
      if (this.isItemSelectable(this.items[itemIndex])) {
        previousItemIndex = itemIndex;
        break;
      }
    }

    this.selectItem(this.items[previousItemIndex], preserveTail);
  }

  selectItem(item, preserveTail, addOrSubtract) {
    if (addOrSubtract && preserveTail) {
      throw new Error('addOrSubtract and preserveTail cannot both be true at the same time');
    }

    const itemIndex = this.items.indexOf(item);
    if (preserveTail && this.selections[0]) {
      this.selections[0].head = itemIndex;
    } else {
      const selection = {head: itemIndex, tail: itemIndex};
      if (addOrSubtract) {
        if (this.getSelectedItems().has(item)) { selection.negate = true; }
        this.selections.unshift(selection);
      } else {
        this.selections = [selection];
      }
    }
  }

  addOrSubtractSelection(item) {
    this.selectItem(item, false, true);
  }

  coalesce() {
    if (this.selections.length === 0) { return; }

    const mostRecent = this.selections[0];
    let mostRecentStart = Math.min(mostRecent.head, mostRecent.tail);
    let mostRecentEnd = Math.max(mostRecent.head, mostRecent.tail);
    while (mostRecentStart > 0 && !this.isItemSelectable(this.items[mostRecentStart - 1])) {
      mostRecentStart--;
    }
    while (mostRecentEnd < (this.items.length - 1) && !this.isItemSelectable(this.items[mostRecentEnd + 1])) {
      mostRecentEnd++;
    }

    for (let i = 1; i < this.selections.length;) {
      const current = this.selections[i];
      const currentStart = Math.min(current.head, current.tail);
      const currentEnd = Math.max(current.head, current.tail);
      if (mostRecentStart <= currentEnd + 1 && currentStart - 1 <= mostRecentEnd) {
        if (mostRecent.negate) {
          const truncatedSelections = [];
          if (current.head > current.tail) {
            if (currentEnd > mostRecentEnd) { // suffix
              truncatedSelections.push({tail: mostRecentEnd + 1, head: currentEnd});
            }
            if (currentStart < mostRecentStart) { // prefix
              truncatedSelections.push({tail: currentStart, head: mostRecentStart - 1});
            }
          } else {
            if (currentStart < mostRecentStart) { // prefix
              truncatedSelections.push({head: currentStart, tail: mostRecentStart - 1});
            }
            if (currentEnd > mostRecentEnd) { // suffix
              truncatedSelections.push({head: mostRecentEnd + 1, tail: currentEnd});
            }
          }
          this.selections.splice(i, 1, ...truncatedSelections);
          i += truncatedSelections.length;
        } else {
          mostRecentStart = Math.min(mostRecentStart, currentStart);
          mostRecentEnd = Math.max(mostRecentEnd, currentEnd);
          if (mostRecent.head >= mostRecent.tail) {
            mostRecent.head = mostRecentEnd;
            mostRecent.tail = mostRecentStart;
          } else {
            mostRecent.head = mostRecentStart;
            mostRecent.tail = mostRecentEnd;
          }
          this.selections.splice(i, 1);
        }
      } else {
        i++;
      }
    }

    if (mostRecent.negate) { this.selections.shift(); }
  }

  getSelectedItems() {
    const selectedItems = new Set();
    for (const {head, tail, negate} of this.selections.slice().reverse()) {
      const start = Math.min(head, tail);
      const end = Math.max(head, tail);
      for (let i = start; i <= end; i++) {
        const item = this.items[i];
        if (this.isItemSelectable(item)) {
          if (negate) {
            selectedItems.delete(item);
          } else {
            selectedItems.add(item);
          }
        }
      }
    }
    return selectedItems;
  }

  getHeadItem() {
    return this.selections.length > 0 ? this.items[this.selections[0].head] : null;
  }

  getMostRecentSelectionStartIndex() {
    const selection = this.selections[0];
    return Math.min(selection.head, selection.tail);
  }

  getTailIndex() {
    return this.selections[0] ? this.selections[0].tail : null;
  }
}
<commit_msg>Only select item in ListSelection if it exists (as default)<commit_after>import {autobind} from 'core-decorators';

const COPY = {};

export default class ListSelection {
  constructor(options = {}) {
    if (options._copy !== COPY) {
      this.options = {
        isItemSelectable: options.isItemSelectable || (item => item && true),
      };
      this.setItems(options.items || []);
    } else {
      this.options = {
        isItemSelectable: options.isItemSelectable,
      };
      this.items = options.items;
      this.selections = options.selections;
    }
  }

  copy() {
    // Deep-copy selections because it will be modified.
    // (That's temporary, until ListSelection is changed to be immutable, too.)
    return new ListSelection({
      _copy: COPY,
      isItemSelectable: this.options.isItemSelectable,
      items: this.items,
      selections: this.selections.map(({head, tail, negate}) => ({head, tail, negate})),
    });
  }

  @autobind
  isItemSelectable(item) {
    return this.options.isItemSelectable(item);
  }

  setItems(items) {
    let newSelectionIndex;
    if (this.selections && this.selections.length > 0) {
      const [{head, tail}] = this.selections;
      newSelectionIndex = Math.min(head, tail, items.length - 1);
    } else {
      newSelectionIndex = 0;
    }

    this.items = items;
    if (items.length > 0) {
      this.selections = [{head: newSelectionIndex, tail: newSelectionIndex}];
    } else {
      this.selections = [];
    }
  }

  getItems() {
    return this.items;
  }

  getLastItem() {
    return this.items[this.items.length - 1];
  }

  selectFirstItem(preserveTail) {
    for (let i = 0; i < this.items.length; i++) {
      const item = this.items[i];
      if (this.isItemSelectable(item)) {
        this.selectItem(item, preserveTail);
        break;
      }
    }
  }

  selectLastItem(preserveTail) {
    for (let i = this.items.length - 1; i > 0; i--) {
      const item = this.items[i];
      if (this.isItemSelectable(item)) {
        this.selectItem(item, preserveTail);
        break;
      }
    }
  }

  selectAllItems() {
    this.selectFirstItem();
    this.selectLastItem(true);
  }

  selectNextItem(preserveTail) {
    if (this.selections.length === 0) {
      this.selectFirstItem();
      return;
    }

    let itemIndex = this.selections[0].head;
    let nextItemIndex = itemIndex;
    while (itemIndex < this.items.length - 1) {
      itemIndex++;
      if (this.isItemSelectable(this.items[itemIndex])) {
        nextItemIndex = itemIndex;
        break;
      }
    }

    this.selectItem(this.items[nextItemIndex], preserveTail);
  }

  selectPreviousItem(preserveTail) {
    if (this.selections.length === 0) {
      this.selectLastItem();
      return;
    }

    let itemIndex = this.selections[0].head;
    let previousItemIndex = itemIndex;

    while (itemIndex > 0) {
      itemIndex--;
      if (this.isItemSelectable(this.items[itemIndex])) {
        previousItemIndex = itemIndex;
        break;
      }
    }

    this.selectItem(this.items[previousItemIndex], preserveTail);
  }

  selectItem(item, preserveTail, addOrSubtract) {
    if (addOrSubtract && preserveTail) {
      throw new Error('addOrSubtract and preserveTail cannot both be true at the same time');
    }

    const itemIndex = this.items.indexOf(item);
    if (preserveTail && this.selections[0]) {
      this.selections[0].head = itemIndex;
    } else {
      const selection = {head: itemIndex, tail: itemIndex};
      if (addOrSubtract) {
        if (this.getSelectedItems().has(item)) { selection.negate = true; }
        this.selections.unshift(selection);
      } else {
        this.selections = [selection];
      }
    }
  }

  addOrSubtractSelection(item) {
    this.selectItem(item, false, true);
  }

  coalesce() {
    if (this.selections.length === 0) { return; }

    const mostRecent = this.selections[0];
    let mostRecentStart = Math.min(mostRecent.head, mostRecent.tail);
    let mostRecentEnd = Math.max(mostRecent.head, mostRecent.tail);
    while (mostRecentStart > 0 && !this.isItemSelectable(this.items[mostRecentStart - 1])) {
      mostRecentStart--;
    }
    while (mostRecentEnd < (this.items.length - 1) && !this.isItemSelectable(this.items[mostRecentEnd + 1])) {
      mostRecentEnd++;
    }

    for (let i = 1; i < this.selections.length;) {
      const current = this.selections[i];
      const currentStart = Math.min(current.head, current.tail);
      const currentEnd = Math.max(current.head, current.tail);
      if (mostRecentStart <= currentEnd + 1 && currentStart - 1 <= mostRecentEnd) {
        if (mostRecent.negate) {
          const truncatedSelections = [];
          if (current.head > current.tail) {
            if (currentEnd > mostRecentEnd) { // suffix
              truncatedSelections.push({tail: mostRecentEnd + 1, head: currentEnd});
            }
            if (currentStart < mostRecentStart) { // prefix
              truncatedSelections.push({tail: currentStart, head: mostRecentStart - 1});
            }
          } else {
            if (currentStart < mostRecentStart) { // prefix
              truncatedSelections.push({head: currentStart, tail: mostRecentStart - 1});
            }
            if (currentEnd > mostRecentEnd) { // suffix
              truncatedSelections.push({head: mostRecentEnd + 1, tail: currentEnd});
            }
          }
          this.selections.splice(i, 1, ...truncatedSelections);
          i += truncatedSelections.length;
        } else {
          mostRecentStart = Math.min(mostRecentStart, currentStart);
          mostRecentEnd = Math.max(mostRecentEnd, currentEnd);
          if (mostRecent.head >= mostRecent.tail) {
            mostRecent.head = mostRecentEnd;
            mostRecent.tail = mostRecentStart;
          } else {
            mostRecent.head = mostRecentStart;
            mostRecent.tail = mostRecentEnd;
          }
          this.selections.splice(i, 1);
        }
      } else {
        i++;
      }
    }

    if (mostRecent.negate) { this.selections.shift(); }
  }

  getSelectedItems() {
    const selectedItems = new Set();
    for (const {head, tail, negate} of this.selections.slice().reverse()) {
      const start = Math.min(head, tail);
      const end = Math.max(head, tail);
      for (let i = start; i <= end; i++) {
        const item = this.items[i];
        if (this.isItemSelectable(item)) {
          if (negate) {
            selectedItems.delete(item);
          } else {
            selectedItems.add(item);
          }
        }
      }
    }
    return selectedItems;
  }

  getHeadItem() {
    return this.selections.length > 0 ? this.items[this.selections[0].head] : null;
  }

  getMostRecentSelectionStartIndex() {
    const selection = this.selections[0];
    return Math.min(selection.head, selection.tail);
  }

  getTailIndex() {
    return this.selections[0] ? this.selections[0].tail : null;
  }
}
<|endoftext|>


<commit_before>/**
 * @class CM_Form_Abstract
 * @extends CM_View_Abstract
 */
var CM_Form_Abstract = CM_View_Abstract.extend({
  _class: 'CM_Form_Abstract',

  /** @type Object **/
  _fields: {},

  /** @type Object **/
  _actions: {},

  /** @type Boolean **/
  _stopErrorPropagation: false,

  initialize: function() {
    CM_View_Abstract.prototype.initialize.call(this);

    this._fields = {};
    this._actions = {};
  },

  events: {
    'reset': function() {
      _.each(this._fields, function(field) {
        field.reset();
      });
      this.resetErrors();
    }
  },

  ready: function() {
  },

  _ready: function() {
    var handler = this;

    _.each(this._actions, function(action, name) {
      var $btn = $('#' + this.getAutoId() + '-' + name + '-button');
      var event = $btn.data('event');
      if (!event) {
        event = 'click';
      }
      $btn.on(event, {action: name}, function(event) {
        event.preventDefault();
        event.stopPropagation();
        return handler.submit(event.data.action).catch(CM_Exception_FormFieldValidation, function(error) {
          // this error type is already handled and displayed in `submit`
        });
      });
    }, this);

    this.$el.on('submit', function() {
      handler.$el.find('input[type="submit"], button[type="submit"]').first().click();
      return false;
    });

    CM_View_Abstract.prototype._ready.call(this);
  },

  /**
   * @param {CM_FormField_Abstract} field
   */
  registerField: function(field) {
    this._fields[field.getName()] = field;

    field.on('change', function() {
      this.trigger('change');
    }, this);
  },

  /**
   * @param {String} name
   * @param {Object} presentation
   */
  registerAction: function(name, presentation) {
    this._actions[name] = presentation;
  },

  /**
   * @return CM_FormField_Abstract
   */
  getField: function(name) {
    if (!this._fields[name]) {
      throw new CM_Exception(this.getClass() + ' cannot find form field `' + name + '`');
    }
    return this._fields[name];
  },

  /**
   * @return Boolean
   */
  hasField: function(name) {
    return !!this._fields[name];
  },

  /**
   * @return jQuery
   */
  $: function(selector) {
    if (!selector) {
      return this.$el;
    }
    selector = selector.replace('#', '#' + this.getAutoId() + '-');
    return $(selector, this.el);
  },

  /**
   * @param {String|Null} [actionName]
   */
  getData: function(actionName) {
    var action = actionName ? this._actions[actionName] : null;
    var data = {};

    if (action) {
      _.each(action.fields, function(isRequired, fieldName) {
        if (this.hasField(fieldName)) {
          var field = this.getField(fieldName);
          var value = field.getValue();
          if (value && value.toString()) {
            //if (null !== value && 'undefined' !== typeof value) {
            data[fieldName] = value;
          }
        } else {
          if (isRequired) {
            throw new CM_Exception(this.getClass() + ' does not contain a required form field `' + fieldName + '`');
          }
        }
      }, this);
    }

    return data;
  },

  /**
   * @param {String} [actionName]
   * @param {Object} [options]
   * @return Promise
   */
  submit: function(actionName, options) {
    options = _.defaults(options || {}, {
      handleErrors: true,
      disableUI: true
    });
    var action = this._getAction(actionName);
    var data = this.getData(action.name);
    var errorList = this._getErrorList(action.name);

    if (options.handleErrors) {
      _.each(this._fields, function(field, fieldName) {
        if (errorList[fieldName]) {
          field.error(errorList[fieldName]);
        } else {
          field.error(null);
        }
      }, this);
    }

    if (_.size(errorList)) {
      return Promise.reject(new CM_Exception_FormFieldValidation(errorList));
    } else {
      if (options.disableUI) {
        this.disable();
      }
      this.trigger('submit', [data]);

      var handler = this;
      return cm.ajax('form', {viewInfoList: this.getViewInfoList(), actionName: action.name, data: data})
        .then(function(response) {
          if (response.errors) {
            if (options.handleErrors) {
              for (var i = response.errors.length - 1, error; error = response.errors[i]; i--) {
                if (_.isArray(error)) {
                  handler.getField(error[1]).error(error[0]);
                } else {
                  handler.error(error);
                }
              }
            }

            throw new CM_Exception_FormFieldValidation(response.errors);
          }

          if (response.exec) {
            handler.evaluation = new Function(response.exec);
            handler.evaluation();
          }

          if (response.messages) {
            for (var i = 0, msg; msg = response.messages[i]; i++) {
              handler.message(msg);
            }
          }

          handler.trigger('success success.' + action.name, response.data);
          return response.data;
        })
        .catch(CM_Exception_FormFieldValidation, function(error){
          handler._stopErrorPropagation = false;
          handler.trigger('error error.' + action.name, error.message, error.name, error.isPublic);
          if (!handler._stopErrorPropagation) {
            throw error;
          }
        })
        .finally(function(){
          if (options.disableUI) {
            handler.enable();
          }
          handler.trigger('complete');
        });
    }
  },

  stopErrorPropagation: function() {
    this._stopErrorPropagation = true;
  },

  reset: function() {
    this.el.reset();
  },

  disable: function() {
    this.$().disable();
  },

  enable: function() {
    this.$().enable();
  },

  /**
   * @param {String} message
   */
  error: function(message) {
    cm.window.hint(message);
  },

  /**
   * @param {String} message
   */
  message: function(message) {
    cm.window.hint(message);
  },

  resetErrors: function() {
    _.each(this._fields, function(field) {
      field.error(null);
    });
  },

  /**
   * @param {String} [actionName]
   * @returns {Object}
   */
  _getAction: function(actionName) {
    actionName = actionName || _.first(_.keys(this._actions));
    var action = this._actions[actionName];
    if (!action) {
      throw new CM_Exception('Form `' + this.getClass() + '` has no action `' + actionName + '`.');
    }
    action.name = actionName;
    return action;
  },

  /**
   * @param {String} [actionName]
   * @returns {Object}
   */
  _getErrorList: function(actionName) {
    var action = this._getAction(actionName);
    var data = this.getData(action.name);

    var errorList = {};

    _.each(_.keys(action.fields).reverse(), function(fieldName) {
      var required = action.fields[fieldName];
      if (required) {
        var field = this.getField(fieldName);
        if (field.isEmpty(data[fieldName])) {
          var label;
          var $textInput = field.$('input, textarea');
          var $labels = $('label[for="' + field.getAutoId() + '-input"]');
          if ($labels.length) {
            label = $labels.first().text();
          } else if ($textInput.attr('placeholder')) {
            label = $textInput.attr('placeholder');
          }
          if (label) {
            errorList[fieldName] = cm.language.get('{$label} is required.', {label: label});
          } else {
            errorList[fieldName] = cm.language.get('Required');
          }
        }
      }
    }, this);

    return errorList;
  }
});
<commit_msg>Consider field getEnabled when gather data<commit_after>/**
 * @class CM_Form_Abstract
 * @extends CM_View_Abstract
 */
var CM_Form_Abstract = CM_View_Abstract.extend({
  _class: 'CM_Form_Abstract',

  /** @type Object **/
  _fields: {},

  /** @type Object **/
  _actions: {},

  /** @type Boolean **/
  _stopErrorPropagation: false,

  initialize: function() {
    CM_View_Abstract.prototype.initialize.call(this);

    this._fields = {};
    this._actions = {};
  },

  events: {
    'reset': function() {
      _.each(this._fields, function(field) {
        field.reset();
      });
      this.resetErrors();
    }
  },

  ready: function() {
  },

  _ready: function() {
    var handler = this;

    _.each(this._actions, function(action, name) {
      var $btn = $('#' + this.getAutoId() + '-' + name + '-button');
      var event = $btn.data('event');
      if (!event) {
        event = 'click';
      }
      $btn.on(event, {action: name}, function(event) {
        event.preventDefault();
        event.stopPropagation();
        return handler.submit(event.data.action).catch(CM_Exception_FormFieldValidation, function(error) {
          // this error type is already handled and displayed in `submit`
        });
      });
    }, this);

    this.$el.on('submit', function() {
      handler.$el.find('input[type="submit"], button[type="submit"]').first().click();
      return false;
    });

    CM_View_Abstract.prototype._ready.call(this);
  },

  /**
   * @param {CM_FormField_Abstract} field
   */
  registerField: function(field) {
    this._fields[field.getName()] = field;

    field.on('change', function() {
      this.trigger('change');
    }, this);
  },

  /**
   * @param {String} name
   * @param {Object} presentation
   */
  registerAction: function(name, presentation) {
    this._actions[name] = presentation;
  },

  /**
   * @return CM_FormField_Abstract
   */
  getField: function(name) {
    if (!this._fields[name]) {
      throw new CM_Exception(this.getClass() + ' cannot find form field `' + name + '`');
    }
    return this._fields[name];
  },

  /**
   * @return Boolean
   */
  hasField: function(name) {
    return !!this._fields[name];
  },

  /**
   * @return jQuery
   */
  $: function(selector) {
    if (!selector) {
      return this.$el;
    }
    selector = selector.replace('#', '#' + this.getAutoId() + '-');
    return $(selector, this.el);
  },

  /**
   * @param {String|Null} [actionName]
   */
  getData: function(actionName) {
    var action = actionName ? this._actions[actionName] : null;
    var data = {};

    if (action) {
      _.each(action.fields, function(isRequired, fieldName) {
        if (this.hasField(fieldName) && this.getField(fieldName).getEnabled()) {
          var value = this.getField(fieldName).getValue();
          if (value && value.toString()) {
            //if (null !== value && 'undefined' !== typeof value) {
            data[fieldName] = value;
          }
        } else {
          if (isRequired) {
            throw new CM_Exception(this.getClass() + ' does not contain a required form field `' + fieldName + '`');
          }
        }
      }, this);
    }

    return data;
  },

  /**
   * @param {String} [actionName]
   * @param {Object} [options]
   * @return Promise
   */
  submit: function(actionName, options) {
    options = _.defaults(options || {}, {
      handleErrors: true,
      disableUI: true
    });
    var action = this._getAction(actionName);
    var data = this.getData(action.name);
    var errorList = this._getErrorList(action.name);

    if (options.handleErrors) {
      _.each(this._fields, function(field, fieldName) {
        if (errorList[fieldName]) {
          field.error(errorList[fieldName]);
        } else {
          field.error(null);
        }
      }, this);
    }

    if (_.size(errorList)) {
      return Promise.reject(new CM_Exception_FormFieldValidation(errorList));
    } else {
      if (options.disableUI) {
        this.disable();
      }
      this.trigger('submit', [data]);

      var handler = this;
      return cm.ajax('form', {viewInfoList: this.getViewInfoList(), actionName: action.name, data: data})
        .then(function(response) {
          if (response.errors) {
            if (options.handleErrors) {
              for (var i = response.errors.length - 1, error; error = response.errors[i]; i--) {
                if (_.isArray(error)) {
                  handler.getField(error[1]).error(error[0]);
                } else {
                  handler.error(error);
                }
              }
            }

            throw new CM_Exception_FormFieldValidation(response.errors);
          }

          if (response.exec) {
            handler.evaluation = new Function(response.exec);
            handler.evaluation();
          }

          if (response.messages) {
            for (var i = 0, msg; msg = response.messages[i]; i++) {
              handler.message(msg);
            }
          }

          handler.trigger('success success.' + action.name, response.data);
          return response.data;
        })
        .catch(CM_Exception_FormFieldValidation, function(error){
          handler._stopErrorPropagation = false;
          handler.trigger('error error.' + action.name, error.message, error.name, error.isPublic);
          if (!handler._stopErrorPropagation) {
            throw error;
          }
        })
        .finally(function(){
          if (options.disableUI) {
            handler.enable();
          }
          handler.trigger('complete');
        });
    }
  },

  stopErrorPropagation: function() {
    this._stopErrorPropagation = true;
  },

  reset: function() {
    this.el.reset();
  },

  disable: function() {
    this.$().disable();
  },

  enable: function() {
    this.$().enable();
  },

  /**
   * @param {String} message
   */
  error: function(message) {
    cm.window.hint(message);
  },

  /**
   * @param {String} message
   */
  message: function(message) {
    cm.window.hint(message);
  },

  resetErrors: function() {
    _.each(this._fields, function(field) {
      field.error(null);
    });
  },

  /**
   * @param {String} [actionName]
   * @returns {Object}
   */
  _getAction: function(actionName) {
    actionName = actionName || _.first(_.keys(this._actions));
    var action = this._actions[actionName];
    if (!action) {
      throw new CM_Exception('Form `' + this.getClass() + '` has no action `' + actionName + '`.');
    }
    action.name = actionName;
    return action;
  },

  /**
   * @param {String} [actionName]
   * @returns {Object}
   */
  _getErrorList: function(actionName) {
    var action = this._getAction(actionName);
    var data = this.getData(action.name);

    var errorList = {};

    _.each(_.keys(action.fields).reverse(), function(fieldName) {
      var required = action.fields[fieldName];
      if (required) {
        var field = this.getField(fieldName);
        if (field.isEmpty(data[fieldName])) {
          var label;
          var $textInput = field.$('input, textarea');
          var $labels = $('label[for="' + field.getAutoId() + '-input"]');
          if ($labels.length) {
            label = $labels.first().text();
          } else if ($textInput.attr('placeholder')) {
            label = $textInput.attr('placeholder');
          }
          if (label) {
            errorList[fieldName] = cm.language.get('{$label} is required.', {label: label});
          } else {
            errorList[fieldName] = cm.language.get('Required');
          }
        }
      }
    }, this);

    return errorList;
  }
});
<|endoftext|>


<commit_before><commit_msg>Optimised gallery image upload previews (#1079)<commit_after><|endoftext|>


<commit_before>APP.applicationController = (function () {
    'use strict';

    var fastClick,
    	iOSPrivateBrowsing;

    function offlineWarning() {
        alert("This feature is only available online.");
    }

    function pageNotFound() {
        alert("That page you were looking for cannot be found.");
    }

    function showHome() {
        $("#body").html(APP.templates.home());

        // Load up the last cached copy of the news
        APP.articlesController.showArticleList();

        $('#refreshButton').click(function () {

            // If the user is offline, don't bother trying to synchronize
            if (navigator && navigator.onLine === false) {
                offlineWarning();
            } else {
                APP.articlesController.synchronizeWithServer(offlineWarning);
            }
        });
    }

    function showArticle(id) {
        $("#body").html(APP.templates.articleLoading());
        APP.articlesController.showArticle(id);
    }

    function route() {
        var page = window.location.hash;
        if (page) {
            page = page.substring(1);
        }
        if (page.length > 0) {
            if (parseInt(page, 10) > 0) {
                showArticle(parseInt(page, 10));
            } else {
                pageNotFound();
            }
        } else {
            showHome();
        }
    }

    function initialize(resources) {

        // Listen to the hash tag changing
        if ("onhashchange" in window) {
            $(window).bind("hashchange", route);

        // Support for old IE (which didn't have hash change)
        } else {
            (function () {
                var lastHash = window.location.hash;
                window.setInterval(function () {
                    if (window.location.hash !== lastHash) {
                        lastHash = window.location.hash;
                        route();
                    }
                }, 100);
            }());
        }

        // Set up FastClick
        fastClick = new FastClick(document.body);

        // Initalise appcache if app not in private browsing mode
        if (!iOSPrivateBrowsing) {
	        APP.appcache.start();
        }

        // Inject CSS Into the DOM
        $("head").append("<style>" + resources.css + "</style>");

        // Create app elements
        $("body").append(APP.templates.application());

        // Remove our loading splash screen
        $("#loading").remove();

        route();
    }

    // This is to our webapp what main() is to C, $(document).ready is to jQuery, etc
    function start(resources, storeResources) {

        // Try to detect whether iOS private browsing mode is enabled
        try {
            localStorage.test = '';
            localStorage.removeItem('item');
        } catch (exception) {
            if (exception.code === 22) {
                iOSPrivateBrowsing = true;
            }
        }

        if (iOSPrivateBrowsing) {
            return APP.network.start(function networkSuccess() {
                APP.database = APP.network;
                initialize(resources);
            });
        }

        // When indexedDB available, use it!
        APP.indexedDB.start(function indexedDBSuccess() {
            APP.database = APP.indexedDB;
            initialize(resources);

            // When indexedDB is not available, fallback to trying websql
        }, function indexedDBFailure() {
            APP.webSQL.start(function webSQLSuccess() {
                APP.database = APP.webSQL;
                initialize(resources);

            // When webSQL not available, fall back to using the network
            }, function webSQLFailure() {
                APP.network.start(function networkSuccess() {
                    APP.database = APP.network;
                    initialize(resources);
                });
            });
        });

        if (storeResources && window['localStorage']) {
            localStorage.resources = JSON.stringify(resources);
        }
    }

    return {
        start: start
    };
}());
<commit_msg>Fix yet another tiny mistake in iOSPrivateBrowsing fix.<commit_after>APP.applicationController = (function () {
    'use strict';

    var fastClick,
    	iOSPrivateBrowsing;

    function offlineWarning() {
        alert("This feature is only available online.");
    }

    function pageNotFound() {
        alert("That page you were looking for cannot be found.");
    }

    function showHome() {
        $("#body").html(APP.templates.home());

        // Load up the last cached copy of the news
        APP.articlesController.showArticleList();

        $('#refreshButton').click(function () {

            // If the user is offline, don't bother trying to synchronize
            if (navigator && navigator.onLine === false) {
                offlineWarning();
            } else {
                APP.articlesController.synchronizeWithServer(offlineWarning);
            }
        });
    }

    function showArticle(id) {
        $("#body").html(APP.templates.articleLoading());
        APP.articlesController.showArticle(id);
    }

    function route() {
        var page = window.location.hash;
        if (page) {
            page = page.substring(1);
        }
        if (page.length > 0) {
            if (parseInt(page, 10) > 0) {
                showArticle(parseInt(page, 10));
            } else {
                pageNotFound();
            }
        } else {
            showHome();
        }
    }

    function initialize(resources) {

        // Listen to the hash tag changing
        if ("onhashchange" in window) {
            $(window).bind("hashchange", route);

        // Support for old IE (which didn't have hash change)
        } else {
            (function () {
                var lastHash = window.location.hash;
                window.setInterval(function () {
                    if (window.location.hash !== lastHash) {
                        lastHash = window.location.hash;
                        route();
                    }
                }, 100);
            }());
        }

        // Set up FastClick
        fastClick = new FastClick(document.body);

        // Initalise appcache if app not in private browsing mode
        if (!iOSPrivateBrowsing) {
	        APP.appcache.start();
        }

        // Inject CSS Into the DOM
        $("head").append("<style>" + resources.css + "</style>");

        // Create app elements
        $("body").append(APP.templates.application());

        // Remove our loading splash screen
        $("#loading").remove();

        route();
    }

    // This is to our webapp what main() is to C, $(document).ready is to jQuery, etc
    function start(resources, storeResources) {

        // Try to detect whether iOS private browsing mode is enabled
        try {
            localStorage.test = '';
            localStorage.removeItem('test');
        } catch (exception) {
            if (exception.code === 22) {
                iOSPrivateBrowsing = true;
            }
        }

        if (iOSPrivateBrowsing) {
            return APP.network.start(function networkSuccess() {
                APP.database = APP.network;
                initialize(resources);
            });
        }

        // When indexedDB available, use it!
        APP.indexedDB.start(function indexedDBSuccess() {
            APP.database = APP.indexedDB;
            initialize(resources);

            // When indexedDB is not available, fallback to trying websql
        }, function indexedDBFailure() {
            APP.webSQL.start(function webSQLSuccess() {
                APP.database = APP.webSQL;
                initialize(resources);

            // When webSQL not available, fall back to using the network
            }, function webSQLFailure() {
                APP.network.start(function networkSuccess() {
                    APP.database = APP.network;
                    initialize(resources);
                });
            });
        });

        if (storeResources && window['localStorage']) {
            localStorage.resources = JSON.stringify(resources);
        }
    }

    return {
        start: start
    };
}());
<|endoftext|>