<commit_before>var z = {
    body: $(document.body),
    page: $('#container'),
    context: $('#page').data('context'),
    prefix: (function() {
        try {
            var s = window.getComputedStyle(document.body, '');
            return (Array.prototype.slice.call(s).join('').match(/moz|webkit|ms|khtml/)||(s.OLink===''&&['o']))[0];
        } catch (e) {
            return 'moz';
        }
    })(),
    prefixed: function(property) {
        if (!z.prefix) return property;
        return '-' + z.prefix + '-' + property;
    },
    canInstallApps: true,
    allowAnonInstalls: !!$('body').data('allow-anon-installs'),
    // if ($('#myDialog li').length > z.confirmBreakNum) add class 'two-col'.
    confirmBreakNum: 6
};

z.prefixUpper = z.prefix[0].toUpperCase() + z.prefix.substr(1);

(function() {
    _.extend(z, {'nav': BrowserUtils()});
    if (!z.nav.browser.firefox ||
        z.nav.browser.mobile || z.nav.os.maemo ||
        VersionCompare.compareVersions(z.nav.browserVersion, '16.0a1') < 0) {
        z.canInstallApps = false;
    }
})();

// Initialize webtrends tracking.
z.page.on('fragmentloaded', webtrendsAsyncInit);

(function() {
    function trigger() {
        $(window).trigger('saferesize');
    }
    window.addEventListener('resize', _.debounce(trigger, 200), false);
})();

$(document).ready(function() {
    // Initialize email links.
    z.page.on('fragmentloaded', function() {
        $('span.emaillink').each(function() {
            var $this = $(this);
            $this.find('.i').remove();
            var em = $this.text().split('').reverse().join('');
            $this.prev('a').attr('href', 'mailto:' + em);
        });
    });
    if (z.readonly) {
        $('form[method=post]')
            .before(gettext('This feature is temporarily disabled while we ' +
                            'perform website maintenance. Please check back ' +
                            'a little later.'))
            .find('button, input, select, textarea').attr('disabled', true)
            .addClass('disabled');
    }
    var data_user = $('body').data('user');
    _.extend(z, {
        anonymous: data_user.anonymous,
        pre_auth: data_user.pre_auth
    });

    stick.basic();
});

z.page.on('fragmentloaded', function() {
    var badPlatform = '
' + gettext('This app is unavailable for your platform.') + '
';

    z.apps = {};
    if (z.capabilities.webApps) {
        // Get list of installed apps and mark as such.
        r = window.navigator.mozApps.getInstalled();
        r.onsuccess = function() {
            _.each(r.result, function(val) {
                z.apps[val.manifestURL] = val;
                $(window).trigger('app_install_success',
                                  [val, {'manifest_url': val.manifestURL}, false]);
            });
        };
        if (!z.capabilities.gaia) {
            // Only Firefox OS currently supports packaged apps.
            // (The 'bad' class ensures we append the message only once.)
            $('.listing .product[data-is_packaged="true"]').addClass('disabled')
                .closest('.mkt-tile:not(.bad)').addClass('bad').append(badPlatform);
        }
    }

    if (!z.canInstallApps) {
        $(window).trigger('app_install_disabled');
        $('.listing .mkt-tile:not(.bad)').addClass('bad').append(badPlatform);
    }

    // Navigation toggle.
    var $header = $('#site-header'),
        $nav = $header.find('nav ul'),
        $outer = $('html, body');
    $header.on('click', '.menu-button', _pd(function() {
        $nav.addClass('active');
        $('.nav-overlay').addClass('show');
    })).on('click', '.region', _pd(function() {
        $outer.animate({scrollTop: $outer.height()}, 1000);
    }));

    $(window).bind('overlay_dismissed', function() {
       $nav.removeClass('active');
    });

    // Hijack external links if we're on mobile.
    if (z.capabilities.touch) {
        $('a[rel=external]').attr('target', '_blank');
    }

    // Header controls.
    $('header').on('click', '.header-button', function(e) {
        var $this = $(this),
            $btns = $('.header-button');

        if ($this.hasClass('dismiss')) {
            // Dismiss looks like back but actually just dismisses an overlay.
            $('#filters').hide();
        } else if ($this.hasClass('filter')) {
            // Yea...
            var sortoption = z.getVars(location.href);

            // This will not scale if we have more than two.
            $('#filter-sort li a').removeClass('sel');
            switch(sortoption.sort) {
                case 'None':
                    $('#filter-sort li.relevancy a').addClass('sel');
                    break;
                case 'popularity':
                    $('#filter-sort li.popularity a').addClass('sel');
                    break;
                case 'rating':
                    $('#filter-sort li.rating a').addClass('sel');

            }
            $('#filters').show();
        } else if ($this.hasClass('search')) {
            z.body.addClass('show-search');
            $btns.blur();
            $('#search-q').focus();
        } else if ($this.hasClass('cancel')) {
            z.body.removeClass('show-search');
            $('#search-q').blur();
            $btns.blur();
        }

        z.page.on('fragmentloaded', function() {
            z.body.removeClass('show-search');
            $('#search-q').blur();
        });
        e.preventDefault();
    });

    // iPhone-style scroll up.
    z.body.on('click', '#top, header', function(e) {
        var $target = $(e.target);
        if (!$target.filter('a, form, input').length) {
            e.preventDefault();
            $outer.animate({scrollTop: 0}, 500);
        }
    });
});
<commit_msg>disable app installs in Fennec 16 and earlier (bug 795189)<commit_after>var z = {
    body: $(document.body),
    page: $('#container'),
    context: $('#page').data('context'),
    prefix: (function() {
        try {
            var s = window.getComputedStyle(document.body, '');
            return (Array.prototype.slice.call(s).join('').match(/moz|webkit|ms|khtml/)||(s.OLink===''&&['o']))[0];
        } catch (e) {
            return 'moz';
        }
    })(),
    prefixed: function(property) {
        if (!z.prefix) return property;
        return '-' + z.prefix + '-' + property;
    },
    canInstallApps: true,
    allowAnonInstalls: !!$('body').data('allow-anon-installs'),
    // if ($('#myDialog li').length > z.confirmBreakNum) add class 'two-col'.
    confirmBreakNum: 6
};

z.prefixUpper = z.prefix[0].toUpperCase() + z.prefix.substr(1);

(function() {
    _.extend(z, {'nav': BrowserUtils()});
    if (!z.nav.browser.firefox ||
        z.nav.browser.mobile || z.nav.os.maemo ||
        VersionCompare.compareVersions(z.nav.browserVersion, '16.0') < 0 ||
        (z.nav.os.android && VersionCompare.compareVersions(z.nav.browserVersion, '17.0') < 0)) {
        z.canInstallApps = false;
    }
})();

// Initialize webtrends tracking.
z.page.on('fragmentloaded', webtrendsAsyncInit);

(function() {
    function trigger() {
        $(window).trigger('saferesize');
    }
    window.addEventListener('resize', _.debounce(trigger, 200), false);
})();

$(document).ready(function() {
    // Initialize email links.
    z.page.on('fragmentloaded', function() {
        $('span.emaillink').each(function() {
            var $this = $(this);
            $this.find('.i').remove();
            var em = $this.text().split('').reverse().join('');
            $this.prev('a').attr('href', 'mailto:' + em);
        });
    });
    if (z.readonly) {
        $('form[method=post]')
            .before(gettext('This feature is temporarily disabled while we ' +
                            'perform website maintenance. Please check back ' +
                            'a little later.'))
            .find('button, input, select, textarea').attr('disabled', true)
            .addClass('disabled');
    }
    var data_user = $('body').data('user');
    _.extend(z, {
        anonymous: data_user.anonymous,
        pre_auth: data_user.pre_auth
    });

    stick.basic();
});

z.page.on('fragmentloaded', function() {
    var badPlatform = '
' + gettext('This app is unavailable for your platform.') + '
';

    z.apps = {};
    if (z.capabilities.webApps) {
        // Get list of installed apps and mark as such.
        r = window.navigator.mozApps.getInstalled();
        r.onsuccess = function() {
            _.each(r.result, function(val) {
                z.apps[val.manifestURL] = val;
                $(window).trigger('app_install_success',
                                  [val, {'manifest_url': val.manifestURL}, false]);
            });
        };
        if (!z.capabilities.gaia) {
            // Only Firefox OS currently supports packaged apps.
            // (The 'bad' class ensures we append the message only once.)
            $('.listing .product[data-is_packaged="true"]').addClass('disabled')
                .closest('.mkt-tile:not(.bad)').addClass('bad').append(badPlatform);
        }
    }

    if (!z.canInstallApps) {
        $(window).trigger('app_install_disabled');
        $('.listing .mkt-tile:not(.bad)').addClass('bad').append(badPlatform);
    }

    // Navigation toggle.
    var $header = $('#site-header'),
        $nav = $header.find('nav ul'),
        $outer = $('html, body');
    $header.on('click', '.menu-button', _pd(function() {
        $nav.addClass('active');
        $('.nav-overlay').addClass('show');
    })).on('click', '.region', _pd(function() {
        $outer.animate({scrollTop: $outer.height()}, 1000);
    }));

    $(window).bind('overlay_dismissed', function() {
       $nav.removeClass('active');
    });

    // Hijack external links if we're on mobile.
    if (z.capabilities.touch) {
        $('a[rel=external]').attr('target', '_blank');
    }

    // Header controls.
    $('header').on('click', '.header-button', function(e) {
        var $this = $(this),
            $btns = $('.header-button');

        if ($this.hasClass('dismiss')) {
            // Dismiss looks like back but actually just dismisses an overlay.
            $('#filters').hide();
        } else if ($this.hasClass('filter')) {
            // Yea...
            var sortoption = z.getVars(location.href);

            // This will not scale if we have more than two.
            $('#filter-sort li a').removeClass('sel');
            switch(sortoption.sort) {
                case 'None':
                    $('#filter-sort li.relevancy a').addClass('sel');
                    break;
                case 'popularity':
                    $('#filter-sort li.popularity a').addClass('sel');
                    break;
                case 'rating':
                    $('#filter-sort li.rating a').addClass('sel');

            }
            $('#filters').show();
        } else if ($this.hasClass('search')) {
            z.body.addClass('show-search');
            $btns.blur();
            $('#search-q').focus();
        } else if ($this.hasClass('cancel')) {
            z.body.removeClass('show-search');
            $('#search-q').blur();
            $btns.blur();
        }

        z.page.on('fragmentloaded', function() {
            z.body.removeClass('show-search');
            $('#search-q').blur();
        });
        e.preventDefault();
    });

    // iPhone-style scroll up.
    z.body.on('click', '#top, header', function(e) {
        var $target = $(e.target);
        if (!$target.filter('a, form, input').length) {
            e.preventDefault();
            $outer.animate({scrollTop: 0}, 500);
        }
    });
});
<|endoftext|>


<commit_before>var FirebaseService = require('../services/firebase-service'),
    ConfigService = require('../services/config-service'),
    ObjectService = require('../services/object-service'),
    LogService = require('../services/log-service'),
    FileService = require('../services/file-service'),
    MessageService = require('../services/message-service'),
    AWSService = require('../services/aws-service'),
    AWS = AWSService.AWS,
    S3 = new AWS.S3(),
    fs = require('fs-extra'),
    Q = require('q'),
    slug = require('slug'),
    moment = require('moment'),
    _ = require('underscore'),
    shortid = require('shortid'),
    request = require('superagent'),
    publicBucket = ConfigService.get('public.amazon.publicBucket'),
    fileUserPrefix = (ConfigService.get('private.amazon.filePrefix') || 'cms') + '/user',
    errorHandler = function(res, err) {
        LogService.error('message', err);
        res.status(500).send(err);
    };

return module.exports = {
    log: function(req, res) {
        var user = req.user,
            userId = req.params.userId,
            assignmentKey = req.params.assignmentKey,
            type = req.params.type,
            messagesRef = FirebaseService.getMessages(),
            now = moment(),
            unix = now.unix(),
            message = {
                created: now.format(),
                assignmentKey: assignmentKey,
                type: type,
                userEmail: user.email,
                userName: user.name || user.preferredEmail || user.email,
                userId: userId,
                keys: {}
            },
            userDeferred = Q.defer(),
            moderatorDeferred = Q.defer();

        _.defaults(message, req.body);

        if (message.$priority) {
            delete message.$priority;
        }

        FirebaseService.authWithSecret(messagesRef).then(function(ref) {
            var logRef = ref.push(),
                moderatorLogRef;

            message.keys.log = logRef.key();

            if (assignmentKey && !user.isModerator) { // Don't duplicate moderator comments in the moderator logs
                moderatorLogRef = FirebaseService.getModeratorMessages(assignmentKey).push();
                message.keys.moderator = moderatorLogRef.key();
            } else {
                // LogService.email('assignmentKey missing for user ' + userId);
            }

            logRef.set(message, function(err) {
                if (err) {
                    return userDeferred.reject(err);
                } else {
                    if (message.recipientId && message.recipientId !== userId) { // Handle case where there is a recipient
                        FirebaseService.getUserMessages(message.recipientId).push(message, function(err) {
                            return err ? userDeferred.reject(err) : userDeferred.resolve(message);
                        });
                    } else {
                        userDeferred.resolve(message);
                    }

                    if (moderatorLogRef) {
                        moderatorLogRef.set(message, function(err) {
                            return err ? moderatorDeferred.reject(err) : moderatorDeferred.resolve(message);
                        });
                    } else {
                        moderatorDeferred.resolve(message);
                    }

                }

            });

        });

        Q.all([userDeferred.promise, moderatorDeferred.promise]).spread(function(message) {
            return res.json(message);
        }, function(err) {
            return res.status(500).send(err);
        });

    },

    upload: function(req, res) {
        /*
         * Configure AWS.S3 payload from request
         */
        var userId = req.params.userId,
            assignmentKey = req.params.assignmentKey,
            assignmentTitle = req.body.assignmentTitle,
            fileName = req.body.fileName.toLowerCase(),
            parts = fileName.split('.'),
            suffix = parts[parts.length - 1],
            filePath = req.body.uploadDir + '/' + fileName,
            readDeferred = Q.defer(),
            fileDeferred = Q.defer(),
            mimeType,
            ETag;

        // Calculate mime types
        if (~ConfigService.get('public.supportedImageTypes').indexOf(suffix)) {
            mimeType = 'image';
        } else if (~ConfigService.get('public.supportedVideoTypes').indexOf(suffix)) {
            mimeType = 'video';
        } else {
            mimeType = 'application';
        }
        mimeType += '/' + suffix;

        if (!fileName) {
            return errorHandler(res, {
                "error": "No file sent."
            });
        }

        fs.readFile(filePath, {
            encoding: "base64"
        }, function(err, data) {
            return err ? readDeferred.reject(err) : readDeferred.resolve(data);
        });

        readDeferred.promise.then(function() {
            fs.unlink(filePath, function(err) {
                return err ? console.log(err) : true;
            });
        });

        /*
         * Handle s3 upload
         */
        readDeferred.promise.then(function(data) {
            var file = new Buffer(data, "base64"),
                payload = {
                    Bucket: publicBucket,
                    Key: fileUserPrefix + '/' + userId + '/' + shortid.generate() + ':' + fileName.replace(/#/g, '-').replace(/:/g, "/"), // Filenames with forward slashes in them get copied to the filesystem with ":" instead of the slashes.
                    ACL: 'public-read',
                    Body: file,
                    CacheControl: "max-age=34536000",
                    Expires: moment().add(5, 'year').unix(),
                    ContentType: mimeType,
                    StorageClass: "REDUCED_REDUNDANCY"
                };

            // console.log('key', payload.Key);

            /*
             * Upload file
             * Report progress
             */
            var notificationsRef = req.userRef.child('notifications').child(slug(fileName, {
                charmap: {
                    '.': '-'
                }
            }).toLowerCase());

            var s3request = S3.putObject(payload, function(err, data) {
                if (err) {
                    fileDeferred.reject(err);
                } else {
                    ETag = data.ETag;
                    notificationsRef.remove();
                    fileDeferred.resolve(data);
                }

            });

            s3request.on('httpUploadProgress', function(progress) {
                progress.loaded = 2 / 3 * progress.loaded + 1 / 3 * progress.total; // Inflate the loaded value by 33%, because this part of the process is the second two thirds of the upload.
                notificationsRef.set(progress);
            });
        }, function(err) {
            errorHandler(res, err)
        });

        /*
         * Handle file upload results
         */
        var rootRef;
        fileDeferred.promise.then(function () {
            return FirebaseService.authWithSecret(FirebaseService.firebaseRoot);
        }).then(function (ref) {
            rootRef = ref;
        }).then(function() {
            return FileService.updateUserFilesRegister(userId);
        }).then(function(s3Data) {
            var upload = _.findWhere(s3Data.Contents, {
                    ETag: ETag
                }),
                uploadsRef = FirebaseService.getUploads(),
                record;

            upload.LastModified = moment(upload.LastModified).format();

            record = {
                created: moment().format(),
                userId: userId,
                userEmail: req.user.email,
                assignmentKey: assignmentKey,
                assignmentTitle: assignmentTitle,
                upload: upload,
                keys: {}
            };

            return Q(record);
        }).then(function (record) {
            var adminUploadRef = FirebaseService.chainFromRef(rootRef, FirebaseService.getUploads().toString()).push();
            var moderatorUploadRef = FirebaseService.chainFromRef(rootRef, FirebaseService.getModeratorUploads(record.assignmentKey).toString()).push();
            var rootRegex = new RegExp(rootRef.toString());
            var updates = {};

            record.keys = {
                log: adminUploadRef.key(),
                moderator: moderatorUploadRef.key()
            };
            updates[adminUploadRef.toString().replace(rootRegex, '')] = record;
            updates[moderatorUploadRef.toString().replace(rootRegex, '')] = record;
            return rootRef.update(updates)
                .then(function () {
                    return Q(record);
                });
        }).then(function(record) {
            var deferred = Q.defer(),
                userAssignmentUploads = FirebaseService.getUserAssignmentUploads(userId, assignmentKey);

            deferred.resolve(record);

            FirebaseService.authWithSecret(userAssignmentUploads).then(function(ref) {
                ref.once('value', function(snap) {
                    var found = false;
                    snap.forEach(function(childSnap) {
                        var upload = childSnap.val();
                        if (upload.ETag === ETag) {
                            found = true;
                        }
                    });

                    if (!found) {
                        ref.push(record.upload, function(err) {
                            return err ? deferred.reject(err) : deferred.resolve(record);
                        });
                    } else {
                        deferred.reject("File already exists.");
                    }
                });

            });

            return deferred.promise;
        }).then(function(record) {
            res.json(record);
        }, function(err) {
            errorHandler(res, err);
        });

    },

    remove: function(req, res) {
        var userId = req.params.userId,
            Key = req.body.Key,
            deleteDeferred = Q.defer();

        S3.deleteObject({
            Bucket: publicBucket,
            Key: Key
        }, function(err, data) {
            return err ? deleteDeferred.reject(err) : deleteDeferred.resolve(data);
        });

        deleteDeferred.promise.then(function() {
            return FileService.updateUserFilesRegister(userId);
        }).then(function(s3Data) {
            res.json(s3Data);
        }, function(err) {
            errorHandler(res, err);
        });

    },

    queueFeedbackEmail: function(req, res) {
        MessageService.queueFeedbackEmail(req.params.userId, req.params.assignmentKey).then(function(queuedEmail) {
            res.json(queuedEmail);
        }, function(err) {
            res.status(500).send(err);
        });

    },

    sendQueuedEmail: function(req, res) {
        MessageService.sendQueuedEmail(req.params.emailKey).then(function(result) {
            res.sendStatus(200);
        }, function(err) {
            res.status(500).send(err);
        });
    },

    sendQueuedFeedback: function(req, res) {
        MessageService.sendAllQueuedFeedback().then(function(result) {
            res.sendStatus(200);
        }, function(err) {
            res.status(500).send(err);
        });
    },

    send: function(req, res) {
        MessageService.send(req.params.userId, req.params.recipientId, req.body.text).then(function(result) {
            res.sendStatus(200);
        }, function(err) {
            res.status(500).send(err);
        });
    }

};<commit_msg>Needs comment<commit_after>var FirebaseService = require('../services/firebase-service'),
    ConfigService = require('../services/config-service'),
    ObjectService = require('../services/object-service'),
    LogService = require('../services/log-service'),
    FileService = require('../services/file-service'),
    MessageService = require('../services/message-service'),
    AWSService = require('../services/aws-service'),
    AWS = AWSService.AWS,
    S3 = new AWS.S3(),
    fs = require('fs-extra'),
    Q = require('q'),
    slug = require('slug'),
    moment = require('moment'),
    _ = require('underscore'),
    shortid = require('shortid'),
    request = require('superagent'),
    publicBucket = ConfigService.get('public.amazon.publicBucket'),
    fileUserPrefix = (ConfigService.get('private.amazon.filePrefix') || 'cms') + '/user',
    errorHandler = function(res, err) {
        LogService.error('message', err);
        res.status(500).send(err);
    };

return module.exports = {
    log: function(req, res) {
        var user = req.user,
            userId = req.params.userId,
            assignmentKey = req.params.assignmentKey,
            type = req.params.type,
            messagesRef = FirebaseService.getMessages(),
            now = moment(),
            unix = now.unix(),
            message = {
                created: now.format(),
                assignmentKey: assignmentKey,
                type: type,
                userEmail: user.email,
                userName: user.name || user.preferredEmail || user.email,
                userId: userId,
                keys: {}
            },
            userDeferred = Q.defer(),
            moderatorDeferred = Q.defer();

        _.defaults(message, req.body);

        if (message.$priority) {
            delete message.$priority;
        }

        FirebaseService.authWithSecret(messagesRef).then(function(ref) {
            var logRef = ref.push(),
                moderatorLogRef;

            message.keys.log = logRef.key();

            if (assignmentKey && !user.isModerator) { // Don't duplicate moderator comments in the moderator logs
                moderatorLogRef = FirebaseService.getModeratorMessages(assignmentKey).push();
                message.keys.moderator = moderatorLogRef.key();
            } else {
                // Admin uploads don't have assignment keys!
                // LogService.email('assignmentKey missing for user ' + userId);
            }

            logRef.set(message, function(err) {
                if (err) {
                    return userDeferred.reject(err);
                } else {
                    if (message.recipientId && message.recipientId !== userId) { // Handle case where there is a recipient
                        FirebaseService.getUserMessages(message.recipientId).push(message, function(err) {
                            return err ? userDeferred.reject(err) : userDeferred.resolve(message);
                        });
                    } else {
                        userDeferred.resolve(message);
                    }

                    if (moderatorLogRef) {
                        moderatorLogRef.set(message, function(err) {
                            return err ? moderatorDeferred.reject(err) : moderatorDeferred.resolve(message);
                        });
                    } else {
                        moderatorDeferred.resolve(message);
                    }

                }

            });

        });

        Q.all([userDeferred.promise, moderatorDeferred.promise]).spread(function(message) {
            return res.json(message);
        }, function(err) {
            return res.status(500).send(err);
        });

    },

    upload: function(req, res) {
        /*
         * Configure AWS.S3 payload from request
         */
        var userId = req.params.userId,
            assignmentKey = req.params.assignmentKey,
            assignmentTitle = req.body.assignmentTitle,
            fileName = req.body.fileName.toLowerCase(),
            parts = fileName.split('.'),
            suffix = parts[parts.length - 1],
            filePath = req.body.uploadDir + '/' + fileName,
            readDeferred = Q.defer(),
            fileDeferred = Q.defer(),
            mimeType,
            ETag;

        // Calculate mime types
        if (~ConfigService.get('public.supportedImageTypes').indexOf(suffix)) {
            mimeType = 'image';
        } else if (~ConfigService.get('public.supportedVideoTypes').indexOf(suffix)) {
            mimeType = 'video';
        } else {
            mimeType = 'application';
        }
        mimeType += '/' + suffix;

        if (!fileName) {
            return errorHandler(res, {
                "error": "No file sent."
            });
        }

        fs.readFile(filePath, {
            encoding: "base64"
        }, function(err, data) {
            return err ? readDeferred.reject(err) : readDeferred.resolve(data);
        });

        readDeferred.promise.then(function() {
            fs.unlink(filePath, function(err) {
                return err ? console.log(err) : true;
            });
        });

        /*
         * Handle s3 upload
         */
        readDeferred.promise.then(function(data) {
            var file = new Buffer(data, "base64"),
                payload = {
                    Bucket: publicBucket,
                    Key: fileUserPrefix + '/' + userId + '/' + shortid.generate() + ':' + fileName.replace(/#/g, '-').replace(/:/g, "/"), // Filenames with forward slashes in them get copied to the filesystem with ":" instead of the slashes.
                    ACL: 'public-read',
                    Body: file,
                    CacheControl: "max-age=34536000",
                    Expires: moment().add(5, 'year').unix(),
                    ContentType: mimeType,
                    StorageClass: "REDUCED_REDUNDANCY"
                };

            // console.log('key', payload.Key);

            /*
             * Upload file
             * Report progress
             */
            var notificationsRef = req.userRef.child('notifications').child(slug(fileName, {
                charmap: {
                    '.': '-'
                }
            }).toLowerCase());

            var s3request = S3.putObject(payload, function(err, data) {
                if (err) {
                    fileDeferred.reject(err);
                } else {
                    ETag = data.ETag;
                    notificationsRef.remove();
                    fileDeferred.resolve(data);
                }

            });

            s3request.on('httpUploadProgress', function(progress) {
                progress.loaded = 2 / 3 * progress.loaded + 1 / 3 * progress.total; // Inflate the loaded value by 33%, because this part of the process is the second two thirds of the upload.
                notificationsRef.set(progress);
            });
        }, function(err) {
            errorHandler(res, err)
        });

        /*
         * Handle file upload results
         */
        var rootRef;
        fileDeferred.promise.then(function () {
            return FirebaseService.authWithSecret(FirebaseService.firebaseRoot);
        }).then(function (ref) {
            rootRef = ref;
        }).then(function() {
            return FileService.updateUserFilesRegister(userId);
        }).then(function(s3Data) {
            var upload = _.findWhere(s3Data.Contents, {
                    ETag: ETag
                }),
                uploadsRef = FirebaseService.getUploads(),
                record;

            upload.LastModified = moment(upload.LastModified).format();

            record = {
                created: moment().format(),
                userId: userId,
                userEmail: req.user.email,
                assignmentKey: assignmentKey,
                assignmentTitle: assignmentTitle,
                upload: upload,
                keys: {}
            };

            return Q(record);
        }).then(function (record) {
            var adminUploadRef = FirebaseService.chainFromRef(rootRef, FirebaseService.getUploads().toString()).push();
            var moderatorUploadRef = FirebaseService.chainFromRef(rootRef, FirebaseService.getModeratorUploads(record.assignmentKey).toString()).push();
            var rootRegex = new RegExp(rootRef.toString());
            var updates = {};

            record.keys = {
                log: adminUploadRef.key(),
                moderator: moderatorUploadRef.key()
            };
            updates[adminUploadRef.toString().replace(rootRegex, '')] = record;
            updates[moderatorUploadRef.toString().replace(rootRegex, '')] = record;
            return rootRef.update(updates)
                .then(function () {
                    return Q(record);
                });
        }).then(function(record) {
            var deferred = Q.defer(),
                userAssignmentUploads = FirebaseService.getUserAssignmentUploads(userId, assignmentKey);

            deferred.resolve(record);

            FirebaseService.authWithSecret(userAssignmentUploads).then(function(ref) {
                ref.once('value', function(snap) {
                    var found = false;
                    snap.forEach(function(childSnap) {
                        var upload = childSnap.val();
                        if (upload.ETag === ETag) {
                            found = true;
                        }
                    });

                    if (!found) {
                        ref.push(record.upload, function(err) {
                            return err ? deferred.reject(err) : deferred.resolve(record);
                        });
                    } else {
                        deferred.reject("File already exists.");
                    }
                });

            });

            return deferred.promise;
        }).then(function(record) {
            res.json(record);
        }, function(err) {
            errorHandler(res, err);
        });

    },

    remove: function(req, res) {
        var userId = req.params.userId,
            Key = req.body.Key,
            deleteDeferred = Q.defer();

        S3.deleteObject({
            Bucket: publicBucket,
            Key: Key
        }, function(err, data) {
            return err ? deleteDeferred.reject(err) : deleteDeferred.resolve(data);
        });

        deleteDeferred.promise.then(function() {
            return FileService.updateUserFilesRegister(userId);
        }).then(function(s3Data) {
            res.json(s3Data);
        }, function(err) {
            errorHandler(res, err);
        });

    },

    queueFeedbackEmail: function(req, res) {
        MessageService.queueFeedbackEmail(req.params.userId, req.params.assignmentKey).then(function(queuedEmail) {
            res.json(queuedEmail);
        }, function(err) {
            res.status(500).send(err);
        });

    },

    sendQueuedEmail: function(req, res) {
        MessageService.sendQueuedEmail(req.params.emailKey).then(function(result) {
            res.sendStatus(200);
        }, function(err) {
            res.status(500).send(err);
        });
    },

    sendQueuedFeedback: function(req, res) {
        MessageService.sendAllQueuedFeedback().then(function(result) {
            res.sendStatus(200);
        }, function(err) {
            res.status(500).send(err);
        });
    },

    send: function(req, res) {
        MessageService.send(req.params.userId, req.params.recipientId, req.body.text).then(function(result) {
            res.sendStatus(200);
        }, function(err) {
            res.status(500).send(err);
        });
    }

};<|endoftext|>


<commit_before>/**************************************
* Anagram plugin for Pokémon Showdown *
* By: jd                              *
**************************************/

var fs = require('fs');
var request = require('request');
var anagramWords = ['pokemon'];
try {
	anagramWords = fs.readFileSync('config/wordlist.txt','utf8').split('\n');
} catch (e) {
	request('http://pastebin.com/raw.php?i=5yQ8MLG2', function callback(error, response, body) {
	    if (!error && response.statusCode === 200) {
	    	fs.writeFileSync('config/wordlist.txt', body, 'utf8');
	    }
	});
}

exports.commands = {
	anagram: function(target, room, user) {
		if (!user.can('broadcast', null, room) || !this.canTalk()) return this.sendReply('/anagram - Access denied.');
		if (!target && !room.anagram) return this.sendReply("Usage: /anagram [normal/pokemon]");
		if (!this.canBroadcast()) return;
		if (room.anagram) return this.sendReplyBox("Word: " + room.anagram.scrambledWord);
		if (!room.anagram) room.anagram = {};

		target = toId(target);
		var theme = '';

		switch (target) {

			case 'pokemon':
				theme = 'Pokemon';
				var pokemon = Tools.dataSearch(Object.keys(Tools.data.Pokedex).sample().trim())[0];
				room.anagram.word = pokemon.name;
				while (pokemon.id.indexOf('mega') > -1 || pokemon.tier === 'CAP') {
					pokemon = Tools.dataSearch(Object.keys(Tools.data.Pokedex).sample().trim())[0];
					room.anagram.word = pokemon.name;
				}
				break;

			case 'info':
			case 'credits':
				this.sendReplyBox("Anagram plugin by jd");
				delete room.anagram;
				break;

			case 'end':
				if (!user.can('broadcast', null, room)) return this.sendReply('/endanagram - Access denied.');
				if (!room.anagram) return this.sendReply('There is no anagram running in here.');
				delete room.anagram;
				this.add('|raw|
The anagram game was ended by ' + Tools.escapeHTML(user.name) + '
');
				break;

			default:
			case 'normal':
				theme = 'Normal';
				room.anagram.word = anagramWords.sample().trim();
				while (room.anagram.word.length < 4 || room.anagram.word.length > 8) room.anagram.word = anagramWords.sample().trim();
				break;
		}

		room.anagram.scrambledWord = toId(room.anagram.word.split('').sort(function(){return 0.5-Math.random();}).join(''));
		while (room.anagram.scrambledWord === toId(room.anagram.word)) room.anagram.scrambledWord = toId(room.anagram.word.split('').sort(function(){return 0.5-Math.random();}).join(''));

		room.chat = function (user, message, connection) {
			message = CommandParser.parse(message, this, user, connection);
			if (message) {
				this.add('|c|' + user.getIdentity(this.id) + '|' + message, true);
				if (room.anagram && toId(message) === toId(room.anagram.word)) {
					this.add('|raw|
' + Tools.escapeHTML(user.name) + ' got the word! It was ' + room.anagram.word + '
');
					delete room.anagram;
					delete room.chat;
				}
			}
		this.update();
		};
		return this.add('|raw|
' + Tools.escapeHTML(user.name) + ' has started an anagram. Letters: ' + room.anagram.scrambledWord + ' Theme: ' + theme + '
');
	}
};<commit_msg>Revert making /endanagram apart of /anagram<commit_after>/**************************************
* Anagram plugin for Pokémon Showdown *
* By: jd                              *
**************************************/

var fs = require('fs');
var request = require('request');
var anagramWords = ['pokemon'];
try {
	anagramWords = fs.readFileSync('config/wordlist.txt','utf8').split('\n');
} catch (e) {
	request('http://pastebin.com/raw.php?i=5yQ8MLG2', function callback(error, response, body) {
	    if (!error && response.statusCode === 200) {
	    	fs.writeFileSync('config/wordlist.txt', body, 'utf8');
	    }
	});
}

exports.commands = {
	anagram: function(target, room, user) {
		if (!user.can('broadcast', null, room) || !this.canTalk()) return this.sendReply('/anagram - Access denied.');
		if (!target && !room.anagram) return this.sendReply("Usage: /anagram [normal/pokemon]");
		if (!this.canBroadcast()) return;
		if (room.anagram) return this.sendReplyBox("Word: " + room.anagram.scrambledWord);
		if (!room.anagram) room.anagram = {};

		target = toId(target);
		var theme = '';

		switch (target) {

			case 'pokemon':
				theme = 'Pokemon';
				var pokemon = Tools.dataSearch(Object.keys(Tools.data.Pokedex).sample().trim())[0];
				room.anagram.word = pokemon.name;
				while (pokemon.id.indexOf('mega') > -1 || pokemon.tier === 'CAP') {
					pokemon = Tools.dataSearch(Object.keys(Tools.data.Pokedex).sample().trim())[0];
					room.anagram.word = pokemon.name;
				}
				break;

			case 'info':
			case 'credits':
				this.sendReplyBox("Anagram plugin by jd");
				delete room.anagram;
				break;

			default:
			case 'normal':
				theme = 'Normal';
				room.anagram.word = anagramWords.sample().trim();
				while (room.anagram.word.length < 4 || room.anagram.word.length > 8) room.anagram.word = anagramWords.sample().trim();
				break;
		}

		room.anagram.scrambledWord = toId(room.anagram.word.split('').sort(function(){return 0.5-Math.random();}).join(''));
		while (room.anagram.scrambledWord === toId(room.anagram.word)) room.anagram.scrambledWord = toId(room.anagram.word.split('').sort(function(){return 0.5-Math.random();}).join(''));

		room.chat = function (user, message, connection) {
			message = CommandParser.parse(message, this, user, connection);
			if (message) {
				this.add('|c|' + user.getIdentity(this.id) + '|' + message, true);
				if (room.anagram && toId(message) === toId(room.anagram.word)) {
					this.add('|raw|
' + Tools.escapeHTML(user.name) + ' got the word! It was ' + room.anagram.word + '
');
					delete room.anagram;
					delete room.chat;
				}
			}
		this.update();
		};
		return this.add('|raw|
' + Tools.escapeHTML(user.name) + ' has started an anagram. Letters: ' + room.anagram.scrambledWord + ' Theme: ' + theme + '
');
	},

	endanagram: function(target, room, user) {
		if (!user.can('broadcast', null, room)) return this.sendReply('/endanagram - Access denied.');
		if (!room.anagram) return this.sendReply('There is no anagram running in here.');
		delete room.anagram;
		this.add('|raw|
The anagram game was ended by ' + Tools.escapeHTML(user.name) + '
');
	},
};<|endoftext|>


<commit_before>/* See license.txt for terms of usage */

FBL.ns(function() { with (FBL) {

// ************************************************************************************************
// Constants

const Cc = Components.classes;
const Ci = Components.interfaces;

var prefDomain = "extensions.firebug.consoleexport";

// ************************************************************************************************
// Module implementation

/**
 * This object implements sending messages to the server. The upload is based on XHR.
 * This object is also responsible for building XML messages.
 */
Firebug.ConsoleExport.Uploader =
/** @lends Firebug.ConsoleExport.Uploader */
{
    send: function(data)
    {
        var format = Firebug.getPref(prefDomain, "format")
        var content = null;
        var contentType = "application/xml";
        if (format != null && format.toLowerCase() == 'json') {
            contentType = "application/json";
            content = JSON.stringify(data);
        } else {
            content = this.buildPacket(data);
        }
        var url = Firebug.getPref(prefDomain, "serverURL");

        if (FBTrace.DBG_CONSOLEEXPORT)
            FBTrace.sysout("consoleexport.Uploader.send; to: " +
                (url ? url : "no server URL specified"), xml);

        if (!url)
            return;

        this.delayedAjax({
            url: url,
            data: content,
            type: "POST",
            contentType: contentType,
            complete: function(request)
            {
                if (FBTrace.DBG_CONSOLEEXPORT)
                    FBTrace.sysout("consoleexport.Uploader.send; Log has been sent: " +
                        request.statusText, request.responseText);
            }
        });
    },

    buildPacket: function(data)
    {
        var xml = "<log>";

        if (data.className)
            xml += "<class>" + data.className + "</class>";

        if (data.cat)
            xml += "<cat>" + data.cat + "</cat>";

        if (data.msg)
            xml += "<msg><![CDATA[" + data.msg + "]]></msg>";

        if (data.href)
            xml += "<href>" + data.href + "</href>";

        if (data.lineNo)
            xml += "<lineNo>" + data.lineNo + "</lineNo>";

        if (data.source)
            xml += "<source>" + data.source + "</source>";

        xml += "</log>";

        return xml;
    },

    delayedAjax: function(options)
    {
        var opt = options;
        setTimeout(function() {
            Firebug.ConsoleExport.Uploader.ajax(opt);
        }, 10);
    },

    ajax: function(options)
    {
        try
        {
            var request = CCIN("@mozilla.org/xmlextras/xmlhttprequest;1", "nsIXMLHttpRequest");
            request.open(options.type, options.url, true);
            request.setRequestHeader("Content-Type", options.contentType);
            request.setRequestHeader("Content-Length", options.data.length);
            request.onreadystatechange = function()
            {
                if (request.readyState == 4 && request.status == 200)
                {
                    if (options.complete)
                        options.complete(request);
                }
            }
            request.send(options.data);
        }
        catch (e)
        {
            if (FBTrace.DBG_CONSOLEEXPORT || FBTrace.DBG_ERRORS)
                FBTrace.sysout("consoleexport.ajax; EXCEPTION " + e, e);
        }
    }
};

// ************************************************************************************************
}});
<commit_msg>Follow up for: export in JSON format.<commit_after>/* See license.txt for terms of usage */

FBL.ns(function() { with (FBL) {

// ************************************************************************************************
// Constants

const Cc = Components.classes;
const Ci = Components.interfaces;

var prefDomain = "extensions.firebug.consoleexport";

// ************************************************************************************************
// Module implementation

/**
 * This object implements sending messages to the server. The upload is based on XHR.
 * This object is also responsible for building XML or JSON messages.
 *
 * Use "extensions.firebug.consoleexport.format" preference to specify whether
 * XML or JSON should be generated. Possible values are:
 *
 * xml (default)
 * json
 */
Firebug.ConsoleExport.Uploader =
/** @lends Firebug.ConsoleExport.Uploader */
{
    send: function(data)
    {
        var format = Firebug.getPref(prefDomain, "format")
        var content = null;
        var contentType = "application/xml";

        if (format != null && format.toLowerCase() == "json")
        {
            contentType = "application/json";
            content = JSON.stringify(data);
        }
        else
        {
            content = this.buildPacket(data);
        }

        var url = Firebug.getPref(prefDomain, "serverURL");

        if (FBTrace.DBG_CONSOLEEXPORT)
        {
            FBTrace.sysout("consoleexport.Uploader.send; to: " +
                (url ? url : "no server URL specified"), content);
        }

        if (!url)
            return;

        this.delayedAjax({
            url: url,
            data: content,
            type: "POST",
            contentType: contentType,
            complete: function(request)
            {
                if (FBTrace.DBG_CONSOLEEXPORT)
                    FBTrace.sysout("consoleexport.Uploader.send; Log has been sent: " +
                        request.statusText, request.responseText);
            }
        });
    },

    buildPacket: function(data)
    {
        var xml = "<log>";

        if (data.className)
            xml += "<class>" + data.className + "</class>";

        if (data.cat)
            xml += "<cat>" + data.cat + "</cat>";

        if (data.msg)
            xml += "<msg><![CDATA[" + data.msg + "]]></msg>";

        if (data.href)
            xml += "<href>" + data.href + "</href>";

        if (data.lineNo)
            xml += "<lineNo>" + data.lineNo + "</lineNo>";

        if (data.source)
            xml += "<source>" + data.source + "</source>";

        xml += "</log>";

        return xml;
    },

    delayedAjax: function(options)
    {
        var opt = options;
        setTimeout(function() {
            Firebug.ConsoleExport.Uploader.ajax(opt);
        }, 10);
    },

    ajax: function(options)
    {
        try
        {
            var request = CCIN("@mozilla.org/xmlextras/xmlhttprequest;1", "nsIXMLHttpRequest");
            request.open(options.type, options.url, true);
            request.setRequestHeader("Content-Type", options.contentType);
            request.setRequestHeader("Content-Length", options.data.length);
            request.onreadystatechange = function()
            {
                if (request.readyState == 4 && request.status == 200)
                {
                    if (options.complete)
                        options.complete(request);
                }
            }
            request.send(options.data);
        }
        catch (e)
        {
            if (FBTrace.DBG_CONSOLEEXPORT || FBTrace.DBG_ERRORS)
                FBTrace.sysout("consoleexport.ajax; EXCEPTION " + e, e);
        }
    }
};

// ************************************************************************************************
}});
<|endoftext|>


<commit_before>/*global describe, it, $, jasmine, spyOn, expect, SkirmishGeo, SkirmishApp, SkirmishTroupMovement, SkirmishTrigger, beforeEach, afterEach */
"use strict";

describe("Troup Movement and Attack", function () {

  describe("clickhandler", function() {
    beforeEach( function () {
      spyOn(SkirmishTroupMovement, 'collateRawMove');
      spyOn(SkirmishTroupMovement, 'clickCountUpdater');
      spyOn(SkirmishGameState, 'getCurrentPlayerId');
      spyOn(SkirmishApp, 'sendMove');
      this.city = {id: 1}
      this.lastCity = {id: 2}
    });

    it('updates count', function () {
      spyOn(SkirmishTroupMovement, 'validMoveClick').and.returnValue(false);
      SkirmishTroupMovement.clickHandler(this.city);
      expect(SkirmishTroupMovement.clickCountUpdater).toHaveBeenCalled()
    });

    it('sends move if valid', function () {
      spyOn(SkirmishTroupMovement, 'validMoveClick').and.returnValue(true);
      SkirmishTroupMovement.clickHandler(this.city);
      expect(SkirmishApp.sendMove).toHaveBeenCalled()
    })

    it('updates last city clicked', function () {
      spyOn(SkirmishTroupMovement, 'validMoveClick').and.returnValue(true);
      SkirmishTroupMovement.setPreviousCityClicked(this.lastCity)
      SkirmishTroupMovement.clickHandler(this.city);
      expect(SkirmishTroupMovement.previousCityClicked()).toEqual(this.city)
    })

  });

  it('collates raw move', function () {
    var city = {id:1, playerId: 2};
    var lastCity = {id:3, playerId: 4};
    spyOn(SkirmishTroupMovement, 'calculateUnits').and.returnValue(5);
    var move = {moveType: 'attack', originId: 3, targetId: 1, unitCount: 5}
    expect(SkirmishTroupMovement.collateRawMove(lastCity, city)).toEqual(move)
  });

  describe('validates move click', function () {

    beforeEach(function () {
      this.city = {id:1}
      this.lastCity = {id:2}
      SkirmishTroupMovement.setClickCount(2);
    });

    it('as legal - different cities, last city clicked, clicks recorded, in range', function() {
      spyOn(SkirmishGeo,'checkRange').and.returnValue(true)
      expect(SkirmishTroupMovement.validMoveClick(this.lastCity, this.city)).toBe(true)
    })

    it('as illegal - same cities', function() {
      spyOn(SkirmishGeo,'checkRange').and.returnValue(true)
      this.lastCity = {id:1}
      expect(SkirmishTroupMovement.validMoveClick(this.lastCity, this.city)).toBe(false)
    })

    it('as illegal - no last city clicked', function() {
      spyOn(SkirmishGeo,'checkRange').and.returnValue(true)
      this.lastCity = null;
      expect(SkirmishTroupMovement.validMoveClick(this.lastCity, this.city)).toBe(false)
    })

    it('as illegal - no clicks recorded', function() {
      spyOn(SkirmishGeo,'checkRange').and.returnValue(true)
      SkirmishTroupMovement.setClickCount(0);
      expect(SkirmishTroupMovement.validMoveClick(this.lastCity, this.city)).toBe(false)
    })

    it('as illegal - out range', function() {
      spyOn(SkirmishGeo, 'checkRange').and.returnValue(false)
      expect(SkirmishTroupMovement.validMoveClick(this.lastCity, this.city)).toBe(false)
    })

  });

  describe('validates click count updater', function () {

    beforeEach(function () {
      this.city = {id:1, playerId: 2}
      this.lastCity = {id:1, playerId: 2, units:[{id:1},{id:2},{id:3},{id:4},{id:5}]}
      SkirmishTroupMovement.setClickCount(3);
      spyOn(SkirmishGameState, 'getCurrentPlayerId').and.returnValue(2)
    });

    it('increments clickCount for same city I own', function (){
      var result = SkirmishTroupMovement.clickCountUpdater(this.lastCity, this.city)
      expect(result).toEqual(4);
    });

    it('resets clickCount to zero when click on other cities twice', function (){
      this.city = {id:1, playerId: 3}
      this.lastCity = {id:2, playerId: 3, units:[{id:1},{id:2},{id:3},{id:4},{id:5}]}
      var result = SkirmishTroupMovement.clickCountUpdater(this.lastCity, this.city)
      expect(result).toEqual(0);
    });

    it('limits clickCount to 5 on own same city', function (){
      SkirmishTroupMovement.setClickCount(5);
      var result = SkirmishTroupMovement.clickCountUpdater(this.lastCity, this.city)
      expect(result).toEqual(5);
    });

    it('resets clickCount for city I dont own', function() {
      this.city = {id:1, playerId: 3}
      var result = SkirmishTroupMovement.clickCountUpdater(this.lastCity, this.city)
      expect(result).toEqual(0);
    });

    it('resets clickCount to 1 when click on another own city', function () {
      this.city = {id:2, playerId: 2}
      var result = SkirmishTroupMovement.clickCountUpdater(this.lastCity, this.city)
      expect(result).toEqual(1);
    });

    it('resets clickCount to 1 when click on opening city', function () {
      this.city = {id:2, playerId: 2, units:[{id:1},{id:2},{id:3},{id:4},{id:5}]}
      this.lastCity = {id:-1, playerId: 2, units:[{id:1},{id:2},{id:3},{id:4},{id:5}]}
      var result = SkirmishTroupMovement.clickCountUpdater(this.lastCity, this.city)
      expect(result).toEqual(1);
    });

  });

  it('calculates Units from number of clicks', function() {
    var city = {units:{length:6}};
    expect(SkirmishTroupMovement.calculateUnits(city, 6)).toEqual(6)
  });

});
<commit_msg>fixed tests for count update<commit_after>/*global describe, it, $, jasmine, spyOn, expect, SkirmishGeo, SkirmishApp, SkirmishTroupMovement, SkirmishTrigger, beforeEach, afterEach */
"use strict";

describe("Troup Movement and Attack", function () {

  describe("clickhandler", function() {
    beforeEach( function () {
      spyOn(SkirmishTroupMovement, 'collateRawMove');
      spyOn(SkirmishTroupMovement, 'clickCountUpdater');
      spyOn(SkirmishGameState, 'getCurrentPlayerId');
      spyOn(SkirmishApp, 'sendMove');
      this.city = {id: 1}
      this.lastCity = {id: 2}
    });

    it('updates count', function () {
      spyOn(SkirmishTroupMovement, 'validMoveClick').and.returnValue(false);
      SkirmishTroupMovement.clickHandler(this.city);
      expect(SkirmishTroupMovement.clickCountUpdater).toHaveBeenCalled()
    });

    it('sends move if valid', function () {
      spyOn(SkirmishTroupMovement, 'validMoveClick').and.returnValue(true);
      SkirmishTroupMovement.clickHandler(this.city);
      expect(SkirmishApp.sendMove).toHaveBeenCalled()
    })

    it('updates last city clicked', function () {
      spyOn(SkirmishTroupMovement, 'validMoveClick').and.returnValue(false);
      SkirmishTroupMovement.setPreviousCityClicked(this.lastCity)
      SkirmishTroupMovement.clickHandler(this.city);
      expect(SkirmishTroupMovement.previousCityClicked()).toEqual(this.city)
    })

  });

  it('collates raw move', function () {
    var city = {id:1, playerId: 2};
    var lastCity = {id:3, playerId: 4};
    spyOn(SkirmishTroupMovement, 'calculateUnits').and.returnValue(5);
    var move = {moveType: 'attack', originId: 3, targetId: 1, unitCount: 5}
    expect(SkirmishTroupMovement.collateRawMove(lastCity, city)).toEqual(move)
  });

  describe('validates move click', function () {

    beforeEach(function () {
      this.city = {id:1}
      this.lastCity = {id:2}
      SkirmishTroupMovement.setClickCount(2);
    });

    it('as legal - different cities, last city clicked, clicks recorded, in range', function() {
      spyOn(SkirmishGeo,'checkRange').and.returnValue(true)
      expect(SkirmishTroupMovement.validMoveClick(this.lastCity, this.city)).toBe(true)
    })

    it('as illegal - same cities', function() {
      spyOn(SkirmishGeo,'checkRange').and.returnValue(true)
      this.lastCity = {id:1}
      expect(SkirmishTroupMovement.validMoveClick(this.lastCity, this.city)).toBe(false)
    })

    it('as illegal - no last city clicked', function() {
      spyOn(SkirmishGeo,'checkRange').and.returnValue(true)
      this.lastCity = null;
      expect(SkirmishTroupMovement.validMoveClick(this.lastCity, this.city)).toBe(false)
    })

    it('as illegal - no clicks recorded', function() {
      spyOn(SkirmishGeo,'checkRange').and.returnValue(true)
      SkirmishTroupMovement.setClickCount(0);
      expect(SkirmishTroupMovement.validMoveClick(this.lastCity, this.city)).toBe(false)
    })

    it('as illegal - out range', function() {
      spyOn(SkirmishGeo, 'checkRange').and.returnValue(false)
      expect(SkirmishTroupMovement.validMoveClick(this.lastCity, this.city)).toBe(false)
    })

  });

  describe('validates click count updater', function () {

    beforeEach(function () {
      this.city = {id:1, playerId: 2}
      this.lastCity = {id:1, playerId: 2, units:[{id:1},{id:2},{id:3},{id:4},{id:5}]}
      SkirmishTroupMovement.setClickCount(3);
      spyOn(SkirmishGameState, 'getCurrentPlayerId').and.returnValue(2)
    });

    it('increments clickCount for same city I own', function (){
      var result = SkirmishTroupMovement.clickCountUpdater(this.lastCity, this.city)
      expect(result).toEqual(4);
    });

    it('resets clickCount to zero when click on other cities twice', function (){
      this.city = {id:1, playerId: 3}
      this.lastCity = {id:2, playerId: 3, units:[{id:1},{id:2},{id:3},{id:4},{id:5}]}
      var result = SkirmishTroupMovement.clickCountUpdater(this.lastCity, this.city)
      expect(result).toEqual(0);
    });

    it('limits clickCount to 1 less than units on own same city', function (){
      SkirmishTroupMovement.setClickCount(5);
      var result = SkirmishTroupMovement.clickCountUpdater(this.lastCity, this.city)
      expect(result).toEqual(4);
    });

    it('resets clickCount for city I dont own', function() {
      this.city = {id:1, playerId: 3}
      var result = SkirmishTroupMovement.clickCountUpdater(this.lastCity, this.city)
      expect(result).toEqual(0);
    });

    it('resets clickCount to 1 when click on another own city', function () {
      this.city = {id:2, playerId: 2}
      var result = SkirmishTroupMovement.clickCountUpdater(this.lastCity, this.city)
      expect(result).toEqual(1);
    });

    it('resets clickCount to 1 when click on opening city', function () {
      this.city = {id:2, playerId: 2, units:[{id:1},{id:2},{id:3},{id:4},{id:5}]}
      this.lastCity = {id:-1, playerId: 2, units:[{id:1},{id:2},{id:3},{id:4},{id:5}]}
      var result = SkirmishTroupMovement.clickCountUpdater(this.lastCity, this.city)
      expect(result).toEqual(1);
    });

  });

  it('calculates Units from number of clicks', function() {
    var city = {units:{length:6}};
    expect(SkirmishTroupMovement.calculateUnits(city, 6)).toEqual(6)
  });

});
<|endoftext|>


<commit_before>var valid=false;
var prepare=true;

$('form#loginForm').submit(function () {
    validLoginForm();
    prepare=true;
    $('input[name=previousuri]').val(json.global.currentUri);
    return valid;
});

$('a#forgotPasswordLink').click(function () {
    midas.loadDialog("forgotpassword","/user/recoverpassword");
    midas.showDialog("Recover Password");
});

$('form#loginForm input[name=submit]').click(function () {
    $("form#loginForm div.loginError img").show();
    $("form#loginForm div.loginError span").hide();
});

$("a.registerLink").unbind('click').click(function(){;
    midas.showOrHideDynamicBar('register');
    midas.loadAjaxDynamicBar('register','/user/register');
});

function validLoginForm() {
    $.ajax({
        url: $('.webroot').val()+"/user/validentry",
        async:false,
        type: "POST",
        data: {entry: $('input[name=email]').val(),password:$('input[name=password]').val(), type: "login"},
        success: function (data) {
            $("form#loginForm div.loginError img").hide();
            if(data.search('true') != -1) {
                valid=true;
            }
            else {
                valid=false;
            }
            midas.createNotice($("form#loginForm div.loginError span").html(), 8000, 'error');
        }
    }
});
<commit_msg>BUG: refs #652. Fix syntax error in user.login.js<commit_after>var valid=false;
var prepare=true;

$('form#loginForm').submit(function () {
    validLoginForm();
    prepare=true;
    $('input[name=previousuri]').val(json.global.currentUri);
    return valid;
});

$('a#forgotPasswordLink').click(function () {
    midas.loadDialog("forgotpassword","/user/recoverpassword");
    midas.showDialog("Recover Password");
});

$('form#loginForm input[name=submit]').click(function () {
    $("form#loginForm div.loginError img").show();
    $("form#loginForm div.loginError span").hide();
});

$("a.registerLink").unbind('click').click(function(){;
    midas.showOrHideDynamicBar('register');
    midas.loadAjaxDynamicBar('register','/user/register');
});

function validLoginForm() {
    $.ajax({
        url: $('.webroot').val()+"/user/validentry",
        async:false,
        type: "POST",
        data: {entry: $('input[name=email]').val(),password:$('input[name=password]').val(), type: "login"},
        success: function (data) {
            $("form#loginForm div.loginError img").hide();
            if(data.search('true') != -1) {
                valid=true;
            }
            else {
                valid=false;
            }
            midas.createNotice($("form#loginForm div.loginError span").html(), 8000, 'error');
        }
    });
};
<|endoftext|>


<commit_before>/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */

/**
 * Tabzilla global navigation for Mozilla projects
 *
 * This code is licensed under the Mozilla Public License 1.1.
 *
 * Event handling portions adapted from the YUI Event component used under
 * the following license:
 *
 *   Copyright © 2012 Yahoo! Inc. All rights reserved.
 *
 *   Redistribution and use of this software in source and binary forms,
 *   with or without modification, are permitted provided that the following conditions
 *   are met:
 *
 *   - Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   - Neither the name of Yahoo! Inc. nor the names of YUI's contributors may
 *     be used to endorse or promote products derived from this software
 *     without specific prior written permission of Yahoo! Inc.
 *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 *   TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 *   PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 *   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 *   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Portions adapted from the jQuery Easing plugin written by Robert Penner and
 * used under the following license:
 *
 *   Copyright 2001 Robert Penner
 *   All rights reserved.
 *
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions are
 *   met:
 *
 *   - Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   - Neither the name of the author nor the names of contributors may be
 *     used to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 *   TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 *   PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 *   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 *   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * @copyright 2012 silverorange Inc.
 * @license   http://www.mozilla.org/MPL/MPL-1.1.html Mozilla Public License 1.1
 * @author    Michael Gauthier <mike@silverorange.com>
 * @author    Steven Garrity <steven@silverorange.com>
 */

function Tabzilla()
{
    if (typeof jQuery != 'undefined' && jQuery) {
        jQuery(document).ready(Tabzilla.init);
    } else {
        Tabzilla.run();
    }
}

Tabzilla.READY_POLL_INTERVAL = 40;
Tabzilla.readyInterval = null;
Tabzilla.jQueryCDNSrc =
    '//www.mozilla.org/media/js/libs/jquery-1.7.1.min.js';
Tabzilla.LINK_TITLE = {
    CLOSED: "Click to open up a section with even more interesting mozilla links",
    OPENED: "Click again or press the escape key to close"
}

Tabzilla.hasCSSTransitions = (function() {
    var div = document.createElement('div');
    div.innerHTML = '

';

    var hasTransitions = (
           (div.firstChild.style.webkitTransition !== undefined)
        || (div.firstChild.style.MozTransition !== undefined)
        || (div.firstChild.style.msTransition !== undefined)
        || (div.firstChild.style.OTransition !== undefined)
    );

    delete div;

    return hasTransitions;
})();

/**
 * Sets up the DOMReady event for Tabzilla
 *
 * Adapted from the YUI Event component. Defined in Tabzilla so we do not
 * depend on YUI or jQuery. The YUI DOMReady implementation is based on work
 * Dean Edwards, John Resig, Matthias Miller and Diego Perini.
 */
Tabzilla.run = function()
{
    var webkit = 0, isIE = false, ua = navigator.userAgent;
    var m = ua.match(/AppleWebKit\/([^\s]*)/);

    if (m && m[1]) {
        webkit = parseInt(m[1], 10);
    } else {
        m = ua.match(/Opera[\s\/]([^\s]*)/);
        if (!m || !m[1]) {
            m = ua.match(/MSIE\s([^;]*)/);
            if (m && m[1]) {
                isIE = true;
            }
        }
    }

    // Internet Explorer: use the readyState of a defered script.
    // This isolates what appears to be a safe moment to manipulate
    // the DOM prior to when the document's readyState suggests
    // it is safe to do so.
    if (isIE) {
        if (self !== self.top) {
            document.onreadystatechange = function() {
                if (document.readyState == 'complete') {
                    document.onreadystatechange = null;
                    Tabzilla.ready();
                }
            };
        } else {
            var n = document.createElement('p');
            Tabzilla.readyInterval = setInterval(function() {
                try {
                    // throws an error if doc is not ready
                    n.doScroll('left');
                    clearInterval(Tabzilla.readyInterval);
                    Tabzilla.readyInterval = null;
                    Tabzilla.ready();
                    n = null;
                } catch (ex) {
                }
            }, Tabzilla.READY_POLL_INTERVAL);
        }

    // The document's readyState in Safari currently will
    // change to loaded/complete before images are loaded.
    } else if (webkit && webkit < 525) {
        Tabzilla.readyInterval = setInterval(function() {
            var rs = document.readyState;
            if ('loaded' == rs || 'complete' == rs) {
                clearInterval(Tabzilla.readyInterval);
                Tabzilla.readyInterval = null;
                Tabzilla.ready();
            }
        }, Tabzilla.READY_POLL_INTERVAL);

    // FireFox and Opera: These browsers provide a event for this
    // moment.  The latest WebKit releases now support this event.
    } else {
        Tabzilla.addEventListener(document, 'DOMContentLoaded', Tabzilla.ready);
    }
};

Tabzilla.ready = function()
{
    if (!Tabzilla.DOMReady) {
        Tabzilla.DOMReady = true;

        var onLoad = function() {
            Tabzilla.init();
            Tabzilla.removeEventListener(
                document,
                'DOMContentLoaded',
                Tabzilla.ready
            );
        };

        // if we don't have jQuery, dynamically load jQuery from CDN
        if (typeof jQuery == 'undefined') {
            var script = document.createElement('script');
            script.type = 'text/javascript';
            script.src = Tabzilla.jQueryCDNSrc;
            document.getElementsByTagName('body')[0].appendChild(script);

            if (script.readyState) {
                // IE
                script.onreadystatechange = function() {
                    if (   script.readyState == 'loaded'
                        || script.readyState == 'complete'
                    ) {
                        onLoad();
                    }
                };
            } else {
                // Others
                script.onload = onLoad;
            }
        } else {
            onLoad();
        }
    }
};

Tabzilla.init = function()
{
    if (!Tabzilla.hasCSSTransitions) {
        // add easing functions
        jQuery.extend(jQuery.easing, {
            'easeInOut':  function (x, t, b, c, d) {
                if (( t /= d / 2) < 1) {
                    return c / 2 * t * t + b;
                }
                return -c / 2 * ((--t) * (t - 2) - 1) + b;
            }
        });
    }

    Tabzilla.link  = document.getElementById('tabzilla');
    Tabzilla.panel = Tabzilla.buildPanel();

    // add panel as first element of body element
    var body = document.getElementsByTagName('body')[0];
    body.insertBefore(Tabzilla.panel, body.firstChild);

    // set up event listeners for link
    Tabzilla.addEventListener(Tabzilla.link, 'click', function(e) {
        Tabzilla.preventDefault(e);
        Tabzilla.toggle();
    });

    Tabzilla.$panel = jQuery(Tabzilla.panel);
    Tabzilla.$link  = jQuery(Tabzilla.link);

    Tabzilla.$panel.addClass('tabzilla-closed');
    Tabzilla.$link.addClass('tabzilla-closed');
    Tabzilla.$panel.removeClass('tabzilla-opened');
    Tabzilla.$link.removeClass('tabzilla-opened');

    Tabzilla.$panel.attr("tabindex","-1");
    Tabzilla.$link.attr({
        "role": "button",
        "aria-expanded": "false",
        "aria-controls": Tabzilla.$panel.attr("id"),
        "title": Tabzilla.LINK_TITLE.CLOSED
    });

    Tabzilla.opened = false;

    jQuery(document).keydown(function(e) {
        if (e.which === 27 && Tabzilla.opened) {
            Tabzilla.toggle();
        }
    });
    Tabzilla.$link.keypress(function(e) {
        if (e.which === 32) {
            Tabzilla.toggle();
            Tabzilla.preventDefault(e);
        }
    });
    Tabzilla.$panel.keypress(function(e) {
        if (e.which === 13) {
            Tabzilla.toggle();
        }
    });
};

Tabzilla.buildPanel = function()
{
    var panel = document.createElement('div');
    panel.id = 'tabzilla-panel';
    panel.innerHTML = Tabzilla.content;
    return panel;
};

Tabzilla.addEventListener = function(el, ev, handler)
{
    if (typeof el.attachEvent != 'undefined') {
        el.attachEvent('on' + ev, handler);
    } else {
        el.addEventListener(ev, handler, false);
    }
};

Tabzilla.removeEventListener = function(el, ev, handler)
{
    if (typeof el.detachEvent != 'undefined') {
        el.detachEvent('on' + ev, handler);
    } else {
        el.removeEventListener(ev, handler, false);
    }
};

Tabzilla.toggle = function()
{
    if (Tabzilla.opened) {
        Tabzilla.close();
    } else {
        Tabzilla.open();
    }
};

Tabzilla.open = function()
{
    if (Tabzilla.opened) {
        return;
    }

    if (Tabzilla.hasCSSTransitions) {
        Tabzilla.$panel.addClass('tabzilla-opened');
        Tabzilla.$link.addClass('tabzilla-opened');
        Tabzilla.$panel.removeClass('tabzilla-closed');
        Tabzilla.$link.removeClass('tabzilla-closed');
    } else {
        // jQuery animation fallback
        jQuery(Tabzilla.panel).animate({ height: 200 }, 200, 'easeInOut').toggleClass("open");;
    }

    Tabzilla.$link.attr({
        "aria-expanded": "true",
        "title": Tabzilla.LINK_TITLE.OPENED
    });
    Tabzilla.$panel.focus();
    Tabzilla.opened = true;
};

Tabzilla.close = function()
{
    if (!Tabzilla.opened) {
        return;
    }

    if (Tabzilla.hasCSSTransitions) {
        Tabzilla.$panel.removeClass('tabzilla-opened');
        Tabzilla.$link.removeClass('tabzilla-opened');
        Tabzilla.$panel.addClass('tabzilla-closed');
        Tabzilla.$link.addClass('tabzilla-closed');
    } else {
        // jQuery animation fallback
        jQuery(Tabzilla.panel).animate({ height: 0 }, 200, 'easeInOut', function(){
            $(this).toggleClass("open");
        });

    }

    Tabzilla.$link.attr({
        "aria-expanded": "false",
        "title": Tabzilla.LINK_TITLE.CLOSED
    });
    Tabzilla.opened = false;
};

Tabzilla.preventDefault = function(ev)
{
    if (ev.preventDefault) {
        ev.preventDefault();
    } else {
        ev.returnValue = false;
    }
};

Tabzilla.content =
    '
'
    + '
'
    + '
'
    + '    '
    + '      Visit the Den for tips, tricks and other tidbits to help you get the most out of your Firefox browsing experience.'
    + '
'
    + '
'
    + '
'
    + '
'
    + '

Mozilla
'
    + '
'
    + '
Mission
'
    + '
About
'
    + '
Projects
'
    + '
Support
'
    + '
Developer Network
'
    + '
'
    + '
'
    + '

Products
'
    + '
'
    + '
Firefox
'
    + '
Thunderbird
'
    + '
'
    + '
'
    + '

Innovations
'
    + '
'
    + '
WebFWD
'
    + '
Labs
'
    + '
'
    + '
'
    + '

Get Involved
'
    + '
'
    + '
Volunteer
'
    + '
Careers
'
    + '
Find us
'
    + '
Join us
'
    + '
'
    + '
'
    + '
'
    + '        Website Directory'
    + '        <form title="Search Mozilla sites" action="http://www.google.com/cse">'
    + '          <input type="hidden" value="002443141534113389537:ysdmevkkknw" name="cx">'
    + '          <input type="hidden" value="FORID:0" name="cof">'
    + '          Search'
    + '          <input type="search" placeholder="Search" id="q" name="q">'
    + '        </form>'
    + '
'
    + '
'
    + '
'
    + '
';

Tabzilla();
<commit_msg>Put the focus back on link if enter is clicked<commit_after>/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */

/**
 * Tabzilla global navigation for Mozilla projects
 *
 * This code is licensed under the Mozilla Public License 1.1.
 *
 * Event handling portions adapted from the YUI Event component used under
 * the following license:
 *
 *   Copyright © 2012 Yahoo! Inc. All rights reserved.
 *
 *   Redistribution and use of this software in source and binary forms,
 *   with or without modification, are permitted provided that the following conditions
 *   are met:
 *
 *   - Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   - Neither the name of Yahoo! Inc. nor the names of YUI's contributors may
 *     be used to endorse or promote products derived from this software
 *     without specific prior written permission of Yahoo! Inc.
 *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 *   TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 *   PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 *   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 *   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Portions adapted from the jQuery Easing plugin written by Robert Penner and
 * used under the following license:
 *
 *   Copyright 2001 Robert Penner
 *   All rights reserved.
 *
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions are
 *   met:
 *
 *   - Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   - Neither the name of the author nor the names of contributors may be
 *     used to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 *   TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 *   PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 *   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 *   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * @copyright 2012 silverorange Inc.
 * @license   http://www.mozilla.org/MPL/MPL-1.1.html Mozilla Public License 1.1
 * @author    Michael Gauthier <mike@silverorange.com>
 * @author    Steven Garrity <steven@silverorange.com>
 */

function Tabzilla()
{
    if (typeof jQuery != 'undefined' && jQuery) {
        jQuery(document).ready(Tabzilla.init);
    } else {
        Tabzilla.run();
    }
}

Tabzilla.READY_POLL_INTERVAL = 40;
Tabzilla.readyInterval = null;
Tabzilla.jQueryCDNSrc =
    '//www.mozilla.org/media/js/libs/jquery-1.7.1.min.js';
Tabzilla.LINK_TITLE = {
    CLOSED: "Click to open up a section with even more interesting mozilla links",
    OPENED: "Click again or press the escape key to close"
}

Tabzilla.hasCSSTransitions = (function() {
    var div = document.createElement('div');
    div.innerHTML = '

';

    var hasTransitions = (
           (div.firstChild.style.webkitTransition !== undefined)
        || (div.firstChild.style.MozTransition !== undefined)
        || (div.firstChild.style.msTransition !== undefined)
        || (div.firstChild.style.OTransition !== undefined)
    );

    delete div;

    return hasTransitions;
})();

/**
 * Sets up the DOMReady event for Tabzilla
 *
 * Adapted from the YUI Event component. Defined in Tabzilla so we do not
 * depend on YUI or jQuery. The YUI DOMReady implementation is based on work
 * Dean Edwards, John Resig, Matthias Miller and Diego Perini.
 */
Tabzilla.run = function()
{
    var webkit = 0, isIE = false, ua = navigator.userAgent;
    var m = ua.match(/AppleWebKit\/([^\s]*)/);

    if (m && m[1]) {
        webkit = parseInt(m[1], 10);
    } else {
        m = ua.match(/Opera[\s\/]([^\s]*)/);
        if (!m || !m[1]) {
            m = ua.match(/MSIE\s([^;]*)/);
            if (m && m[1]) {
                isIE = true;
            }
        }
    }

    // Internet Explorer: use the readyState of a defered script.
    // This isolates what appears to be a safe moment to manipulate
    // the DOM prior to when the document's readyState suggests
    // it is safe to do so.
    if (isIE) {
        if (self !== self.top) {
            document.onreadystatechange = function() {
                if (document.readyState == 'complete') {
                    document.onreadystatechange = null;
                    Tabzilla.ready();
                }
            };
        } else {
            var n = document.createElement('p');
            Tabzilla.readyInterval = setInterval(function() {
                try {
                    // throws an error if doc is not ready
                    n.doScroll('left');
                    clearInterval(Tabzilla.readyInterval);
                    Tabzilla.readyInterval = null;
                    Tabzilla.ready();
                    n = null;
                } catch (ex) {
                }
            }, Tabzilla.READY_POLL_INTERVAL);
        }

    // The document's readyState in Safari currently will
    // change to loaded/complete before images are loaded.
    } else if (webkit && webkit < 525) {
        Tabzilla.readyInterval = setInterval(function() {
            var rs = document.readyState;
            if ('loaded' == rs || 'complete' == rs) {
                clearInterval(Tabzilla.readyInterval);
                Tabzilla.readyInterval = null;
                Tabzilla.ready();
            }
        }, Tabzilla.READY_POLL_INTERVAL);

    // FireFox and Opera: These browsers provide a event for this
    // moment.  The latest WebKit releases now support this event.
    } else {
        Tabzilla.addEventListener(document, 'DOMContentLoaded', Tabzilla.ready);
    }
};

Tabzilla.ready = function()
{
    if (!Tabzilla.DOMReady) {
        Tabzilla.DOMReady = true;

        var onLoad = function() {
            Tabzilla.init();
            Tabzilla.removeEventListener(
                document,
                'DOMContentLoaded',
                Tabzilla.ready
            );
        };

        // if we don't have jQuery, dynamically load jQuery from CDN
        if (typeof jQuery == 'undefined') {
            var script = document.createElement('script');
            script.type = 'text/javascript';
            script.src = Tabzilla.jQueryCDNSrc;
            document.getElementsByTagName('body')[0].appendChild(script);

            if (script.readyState) {
                // IE
                script.onreadystatechange = function() {
                    if (   script.readyState == 'loaded'
                        || script.readyState == 'complete'
                    ) {
                        onLoad();
                    }
                };
            } else {
                // Others
                script.onload = onLoad;
            }
        } else {
            onLoad();
        }
    }
};

Tabzilla.init = function()
{
    if (!Tabzilla.hasCSSTransitions) {
        // add easing functions
        jQuery.extend(jQuery.easing, {
            'easeInOut':  function (x, t, b, c, d) {
                if (( t /= d / 2) < 1) {
                    return c / 2 * t * t + b;
                }
                return -c / 2 * ((--t) * (t - 2) - 1) + b;
            }
        });
    }

    Tabzilla.link  = document.getElementById('tabzilla');
    Tabzilla.panel = Tabzilla.buildPanel();

    // add panel as first element of body element
    var body = document.getElementsByTagName('body')[0];
    body.insertBefore(Tabzilla.panel, body.firstChild);

    // set up event listeners for link
    Tabzilla.addEventListener(Tabzilla.link, 'click', function(e) {
        Tabzilla.preventDefault(e);
        Tabzilla.toggle();
    });

    Tabzilla.$panel = jQuery(Tabzilla.panel);
    Tabzilla.$link  = jQuery(Tabzilla.link);

    Tabzilla.$panel.addClass('tabzilla-closed');
    Tabzilla.$link.addClass('tabzilla-closed');
    Tabzilla.$panel.removeClass('tabzilla-opened');
    Tabzilla.$link.removeClass('tabzilla-opened');

    Tabzilla.$panel.attr("tabindex","-1");
    Tabzilla.$link.attr({
        "role": "button",
        "aria-expanded": "false",
        "aria-controls": Tabzilla.$panel.attr("id"),
        "title": Tabzilla.LINK_TITLE.CLOSED
    });

    Tabzilla.opened = false;

    jQuery(document).keydown(function(e) {
        if (e.which === 27 && Tabzilla.opened) {
            Tabzilla.toggle();
        }
    });
    Tabzilla.$link.keypress(function(e) {
        if (e.which === 32) {
            Tabzilla.toggle();
            Tabzilla.preventDefault(e);
        }
    });
    Tabzilla.$panel.keypress(function(e) {
        if (e.which === 13) {
            Tabzilla.toggle();
            Tabzilla.$link.focus();
        }
    });
};

Tabzilla.buildPanel = function()
{
    var panel = document.createElement('div');
    panel.id = 'tabzilla-panel';
    panel.innerHTML = Tabzilla.content;
    return panel;
};

Tabzilla.addEventListener = function(el, ev, handler)
{
    if (typeof el.attachEvent != 'undefined') {
        el.attachEvent('on' + ev, handler);
    } else {
        el.addEventListener(ev, handler, false);
    }
};

Tabzilla.removeEventListener = function(el, ev, handler)
{
    if (typeof el.detachEvent != 'undefined') {
        el.detachEvent('on' + ev, handler);
    } else {
        el.removeEventListener(ev, handler, false);
    }
};

Tabzilla.toggle = function()
{
    if (Tabzilla.opened) {
        Tabzilla.close();
    } else {
        Tabzilla.open();
    }
};

Tabzilla.open = function()
{
    if (Tabzilla.opened) {
        return;
    }

    if (Tabzilla.hasCSSTransitions) {
        Tabzilla.$panel.addClass('tabzilla-opened');
        Tabzilla.$link.addClass('tabzilla-opened');
        Tabzilla.$panel.removeClass('tabzilla-closed');
        Tabzilla.$link.removeClass('tabzilla-closed');
    } else {
        // jQuery animation fallback
        jQuery(Tabzilla.panel).animate({ height: 200 }, 200, 'easeInOut').toggleClass("open");;
    }

    Tabzilla.$link.attr({
        "aria-expanded": "true",
        "title": Tabzilla.LINK_TITLE.OPENED
    });
    Tabzilla.$panel.focus();
    Tabzilla.opened = true;
};

Tabzilla.close = function()
{
    if (!Tabzilla.opened) {
        return;
    }

    if (Tabzilla.hasCSSTransitions) {
        Tabzilla.$panel.removeClass('tabzilla-opened');
        Tabzilla.$link.removeClass('tabzilla-opened');
        Tabzilla.$panel.addClass('tabzilla-closed');
        Tabzilla.$link.addClass('tabzilla-closed');
    } else {
        // jQuery animation fallback
        jQuery(Tabzilla.panel).animate({ height: 0 }, 200, 'easeInOut', function(){
            $(this).toggleClass("open");
        });

    }

    Tabzilla.$link.attr({
        "aria-expanded": "false",
        "title": Tabzilla.LINK_TITLE.CLOSED
    });
    Tabzilla.opened = false;
};

Tabzilla.preventDefault = function(ev)
{
    if (ev.preventDefault) {
        ev.preventDefault();
    } else {
        ev.returnValue = false;
    }
};

Tabzilla.content =
    '
'
    + '
'
    + '
'
    + '    '
    + '      Visit the Den for tips, tricks and other tidbits to help you get the most out of your Firefox browsing experience.'
    + '
'
    + '
'
    + '
'
    + '
'
    + '

Mozilla
'
    + '
'
    + '
Mission
'
    + '
About
'
    + '
Projects
'
    + '
Support
'
    + '
Developer Network
'
    + '
'
    + '
'
    + '

Products
'
    + '
'
    + '
Firefox
'
    + '
Thunderbird
'
    + '
'
    + '
'
    + '

Innovations
'
    + '
'
    + '
WebFWD
'
    + '
Labs
'
    + '
'
    + '
'
    + '

Get Involved
'
    + '
'
    + '
Volunteer
'
    + '
Careers
'
    + '
Find us
'
    + '
Join us
'
    + '
'
    + '
'
    + '
'
    + '        Website Directory'
    + '        <form title="Search Mozilla sites" action="http://www.google.com/cse">'
    + '          <input type="hidden" value="002443141534113389537:ysdmevkkknw" name="cx">'
    + '          <input type="hidden" value="FORID:0" name="cof">'
    + '          Search'
    + '          <input type="search" placeholder="Search" id="q" name="q">'
    + '        </form>'
    + '
'
    + '
'
    + '
'
    + '
';

Tabzilla();
<|endoftext|>