<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

jQuery.support.cors = true;
var app = {
    // Application Constructor
    initialize: function() {
        this.bindEvents();
    },
    // Bind Event Listeners
    //
    // Bind any events that are required on startup. Common events are:
    // 'load', 'deviceready', 'offline', and 'online'.
    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
    },
    // deviceready Event Handler
    //
    // The scope of 'this' is the event. In order to call the 'receivedEvent'
    // function, we must explicitly call 'app.receivedEvent(...);'
    onDeviceReady: function() {
        app.receivedEvent('deviceready');
    },
    // Update DOM on a Received Event
    receivedEvent: function(id) {
        var parentElement = document.getElementById(id);
        var listeningElement = parentElement.querySelector('.listening');
        var receivedElement = parentElement.querySelector('.received');

        listeningElement.setAttribute('style', 'display:none;');
        receivedElement.setAttribute('style', 'display:block;');

        console.log('Received Event: ' + id);
    }
};

function scan() {
    var url = 'http://ec2-54-191-94-161.us-west-2.compute.amazonaws.com/post-validateb64.php'

    cordova.plugins.barcodeScanner.scan(
      function (result) {
        console.log(result)
        alert(result.text)
        var request = $.ajax({
          url: url,
          type: "POST",
          data: btoa(result.text)
        });

        request.done(function(result) {
          alert(result)
        })
          // alert("We got a barcode\n" +
          //       "Result: " + result.text + "\n" +
          //       "Format: " + result.format + "\n" +
          //       "Cancelled: " + result.cancelled);
      },
      function (error) {
          alert("Scanning failed: " + error);
      }
   );
}
<commit_msg>scan only QR codes<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

jQuery.support.cors = true;
var app = {
    // Application Constructor
    initialize: function() {
        this.bindEvents();
    },
    // Bind Event Listeners
    //
    // Bind any events that are required on startup. Common events are:
    // 'load', 'deviceready', 'offline', and 'online'.
    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
    },
    // deviceready Event Handler
    //
    // The scope of 'this' is the event. In order to call the 'receivedEvent'
    // function, we must explicitly call 'app.receivedEvent(...);'
    onDeviceReady: function() {
        app.receivedEvent('deviceready');
    },
    // Update DOM on a Received Event
    receivedEvent: function(id) {
        var parentElement = document.getElementById(id);
        var listeningElement = parentElement.querySelector('.listening');
        var receivedElement = parentElement.querySelector('.received');

        listeningElement.setAttribute('style', 'display:none;');
        receivedElement.setAttribute('style', 'display:block;');

        console.log('Received Event: ' + id);
    }
};

function scan() {
    var url = 'http://ec2-54-191-94-161.us-west-2.compute.amazonaws.com/post-validateb64.php'

    cordova.plugins.barcodeScanner.scan(
      function (result) {
        console.log(result)
        alert(result.text)
        var request = $.ajax({
          url: url,
          type: "POST",
          data: btoa(result.text)
        });

        request.done(function(result) {
          alert(result)
        })
          // alert("We got a barcode\n" +
          //       "Result: " + result.text + "\n" +
          //       "Format: " + result.format + "\n" +
          //       "Cancelled: " + result.cancelled);
      },
      function (error) {
          alert("Scanning failed: " + error);
      },
      {'SCAN_MODE': 'QR_CODE_MODE'}
   );
}
<|endoftext|>


<commit_before>/*===============================================================================
************   Fast Clicks   ************
************   Inspired by https://github.com/ftlabs/fastclick   ************
===============================================================================*/
app.initFastClicks = function () {
    if (app.params.activeState) {
        $('html').addClass('watch-active-state');
    }
    if (app.device.ios && app.device.webView) {
        // Strange hack required for iOS 8 webview to work on inputs
        window.addEventListener('touchstart', function () {});
    }

    var touchStartX, touchStartY, touchStartTime, targetElement, trackClick, activeSelection, scrollParent, lastClickTime, isMoved;
    var activableElement, activeTimeout, needsFastClick;

    function findActivableElement(e) {
        var target = $(e.target);
        var parents = target.parents(app.params.activeStateElements);
        var activable;
        if (target.is(app.params.activeStateElements)) {
            activable = target;
        }
        if (parents.length > 0) {
            activable = activable ? activable.add(parents) : parents;
        }
        return activable ? activable : target;
    }
    function isInsideScrollableView() {
        var pageContent = activableElement.parents('.page-content, .panel');

        if (pageContent.length === 0) {
            return false;
        }

        // This event handler covers the "tap to stop scrolling".
        if (pageContent.prop('scrollHandlerSet') !== 'yes') {
            pageContent.on('scroll', function() {
              clearTimeout(activeTimeout);
            });
            pageContent.prop('scrollHandlerSet', 'yes');
        }

        return true;
    }
    function addActive() {
        activableElement.addClass('active-state');
    }
    function removeActive(el) {
        activableElement.removeClass('active-state');
    }

    function androidNeedsBlur(el) {
        var noBlur = ('button checkbox file image radio submit input textarea').split(' ');
        if (document.activeElement && el !== document.activeElement && document.activeElement !== document.body) {
            if (noBlur.indexOf(el.nodeName.toLowerCase()) >= 0) {
                return false;
            }
            else {
                return true;
            }
        }
        else {
            return false;
        }
    }
    function targetNeedsFastClick(el) {
        var $el = $(el);
        if (el.nodeName.toLowerCase() === 'input' && el.type === 'file') return false;
        if ($el.hasClass('no-fastclick') || $el.parents('.no-fastclick').length > 0) return false;
        return true;
    }
    function targetNeedsFocus(el) {
        if (document.activeElement === el) {
            return false;
        }
        var tag = el.nodeName.toLowerCase();
        var skipInputs = ('button checkbox file image radio submit').split(' ');
        if (el.disabled || el.readOnly) return false;
        if (tag === 'textarea') return true;
        if (tag === 'select') {
            if (app.device.android) return false;
            else return true;
        }
        if (tag === 'input' && skipInputs.indexOf(el.type) < 0) return true;
    }
    function targetNeedsPrevent(el) {
        el = $(el);
        var prevent = true;
        if (el.is('label') || el.parents('label').length > 0) {
            if (app.device.android) {
                prevent = false;
            }
            else if (app.device.ios && el.is('input')) {
                prevent = true;
            }
            else prevent = false;
        }
        return prevent;
    }

    // Mouse Handlers
    function handleMouseDown (e) {
        findActivableElement(e).addClass('active-state');
        if ('which' in e && e.which === 3) {
            setTimeout(function () {
                $('.active-state').removeClass('active-state');
            }, 0);
        }
    }
    function handleMouseMove (e) {
        $('.active-state').removeClass('active-state');
    }
    function handleMouseUp (e) {
        $('.active-state').removeClass('active-state');
    }

    // Touch Handlers
    function handleTouchStart(e) {
        isMoved = false;
        if (e.targetTouches.length > 1) {
            return true;
        }
        needsFastClick = targetNeedsFastClick(e.target);

        if (!needsFastClick) {
            trackClick = false;
            return true;
        }
        if (app.device.ios) {
            var selection = window.getSelection();
            if (selection.rangeCount && selection.focusNode !== document.body && (!selection.isCollapsed || document.activeElement === selection.focusNode)) {
                activeSelection = true;
                return true;
            }
            else {
                activeSelection = false;
            }
        }
        if (app.device.android)  {
            if (androidNeedsBlur(e.target)) {
                document.activeElement.blur();
            }
        }

        trackClick = true;
        targetElement = e.target;
        touchStartTime = (new Date()).getTime();
        touchStartX = e.targetTouches[0].pageX;
        touchStartY = e.targetTouches[0].pageY;

        // Detect scroll parent
        if (app.device.ios) {
            scrollParent = undefined;
            $(targetElement).parents().each(function () {
                var parent = this;
                if (parent.scrollHeight > parent.offsetHeight && !scrollParent) {
                    scrollParent = parent;
                    scrollParent.f7ScrollTop = scrollParent.scrollTop;
                }
            });
        }
        if ((e.timeStamp - lastClickTime) < app.params.fastClicksDelayBetweenClicks) {
            e.preventDefault();
        }
        if (app.params.activeState) {
            activableElement = findActivableElement(e);
            // If it's inside a scrollable view, we don't trigger active-state yet,
            // because it can be a scroll instead. Based on the link:
            // http://labnote.beedesk.com/click-scroll-and-pseudo-active-on-mobile-webk
            if (!isInsideScrollableView(e)) {
                addActive();
            } else {
                activeTimeout = setTimeout(addActive, 80);
            }
        }
    }
    function handleTouchMove(e) {
        if (!trackClick) return;
        var _isMoved = false;
        var distance = app.params.fastClicksDistanceThreshold;
        if (distance) {
            var pageX = e.targetTouches[0].pageX;
            var pageY = e.targetTouches[0].pageY;
            if (Math.abs(pageX - touchStartX) > distance ||  Math.abs(pageY - touchStartY) > distance) {
                _isMoved = true;
            }
        }
        else {
            _isMoved = true;
        }
        if (_isMoved) {
            trackClick = false;
            targetElement = null;
            isMoved = true;
        }

        if (app.params.activeState) {
            clearTimeout(activeTimeout);
            removeActive();
        }
    }
    function handleTouchEnd(e) {
        clearTimeout(activeTimeout);

        if (!trackClick) {
            if (!activeSelection && needsFastClick) {
                if (!(app.device.android && !e.cancelable)) {
                    e.preventDefault();
                }
            }
            return true;
        }

        if (document.activeElement === e.target) {
            return true;
        }

        if (!activeSelection) {
            e.preventDefault();
        }

        if ((e.timeStamp - lastClickTime) < app.params.fastClicksDelayBetweenClicks) {
            setTimeout(removeActive, 0);
            return true;
        }

        lastClickTime = e.timeStamp;

        trackClick = false;

        if (app.device.ios && scrollParent) {
            if (scrollParent.scrollTop !== scrollParent.f7ScrollTop) {
                return false;
            }
        }

        // Add active-state here because, in a very fast tap, the timeout didn't
        // have the chance to execute. Removing active-state in a timeout gives
        // the chance to the animation execute.
        if (app.params.activeState) {
            addActive();
            setTimeout(removeActive, 0);
        }

        // Trigger focus when required
        if (targetNeedsFocus(targetElement)) {
            targetElement.focus();
        }

        // Blur active elements
        if (document.activeElement && targetElement !== document.activeElement && document.activeElement !== document.body && targetElement.nodeName.toLowerCase() !== 'label') {
            document.activeElement.blur();
        }

        // Send click
        e.preventDefault();
        var touch = e.changedTouches[0];
        var evt = document.createEvent('MouseEvents');
        var eventType = 'click';
        if (app.device.android && targetElement.nodeName.toLowerCase() === 'select') {
            eventType = 'mousedown';
        }
        evt.initMouseEvent(eventType, true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
        evt.forwardedTouchEvent = true;
        targetElement.dispatchEvent(evt);
        return false;
    }
    function handleTouchCancel(e) {
        trackClick = false;
        targetElement = null;
    }

    function handleClick(e) {
        var allowClick = false;
        if (trackClick) {
            targetElement = null;
            trackClick = false;
            return true;
        }

        if (e.target.type === 'submit' && e.detail === 0) {
            return true;
        }
        // if (!targetElement) {
        //     allowClick =  true;
        // }
        if (!needsFastClick) {
            allowClick = true;
        }
        if (document.activeElement === targetElement) {
            allowClick =  true;
        }
        if (e.forwardedTouchEvent) {
            allowClick =  true;
        }
        if (!e.cancelable) {
            allowClick =  true;
        }
        if (!allowClick) {
            e.stopImmediatePropagation();
            e.stopPropagation();
            if (targetElement) {
                if (targetNeedsPrevent(targetElement) || isMoved) {
                    e.preventDefault();
                }
            }
            else {
                e.preventDefault();
            }
            targetElement = null;
        }
        return allowClick;
    }
    if (app.support.touch) {
        document.addEventListener('click', handleClick, true);

        document.addEventListener('touchstart', handleTouchStart);
        document.addEventListener('touchmove', handleTouchMove);
        document.addEventListener('touchend', handleTouchEnd);
        document.addEventListener('touchcancel', handleTouchCancel);
    }
    else {
        if (app.params.activeState) {
            document.addEventListener('mousedown', handleMouseDown);
            document.addEventListener('mousemove', handleMouseMove);
            document.addEventListener('mouseup', handleMouseUp);
        }
    }

};
<commit_msg>needsFastClick TimeOut<commit_after>/*===============================================================================
************   Fast Clicks   ************
************   Inspired by https://github.com/ftlabs/fastclick   ************
===============================================================================*/
app.initFastClicks = function () {
    if (app.params.activeState) {
        $('html').addClass('watch-active-state');
    }
    if (app.device.ios && app.device.webView) {
        // Strange hack required for iOS 8 webview to work on inputs
        window.addEventListener('touchstart', function () {});
    }

    var touchStartX, touchStartY, touchStartTime, targetElement, trackClick, activeSelection, scrollParent, lastClickTime, isMoved;
    var activableElement, activeTimeout, needsFastClick, needsFastClickTimeOut;

    function findActivableElement(e) {
        var target = $(e.target);
        var parents = target.parents(app.params.activeStateElements);
        var activable;
        if (target.is(app.params.activeStateElements)) {
            activable = target;
        }
        if (parents.length > 0) {
            activable = activable ? activable.add(parents) : parents;
        }
        return activable ? activable : target;
    }
    function isInsideScrollableView() {
        var pageContent = activableElement.parents('.page-content, .panel');

        if (pageContent.length === 0) {
            return false;
        }

        // This event handler covers the "tap to stop scrolling".
        if (pageContent.prop('scrollHandlerSet') !== 'yes') {
            pageContent.on('scroll', function() {
              clearTimeout(activeTimeout);
            });
            pageContent.prop('scrollHandlerSet', 'yes');
        }

        return true;
    }
    function addActive() {
        activableElement.addClass('active-state');
    }
    function removeActive(el) {
        activableElement.removeClass('active-state');
    }

    function androidNeedsBlur(el) {
        var noBlur = ('button checkbox file image radio submit input textarea').split(' ');
        if (document.activeElement && el !== document.activeElement && document.activeElement !== document.body) {
            if (noBlur.indexOf(el.nodeName.toLowerCase()) >= 0) {
                return false;
            }
            else {
                return true;
            }
        }
        else {
            return false;
        }
    }
    function targetNeedsFastClick(el) {
        var $el = $(el);
        if (el.nodeName.toLowerCase() === 'input' && el.type === 'file') return false;
        if ($el.hasClass('no-fastclick') || $el.parents('.no-fastclick').length > 0) return false;
        return true;
    }
    function targetNeedsFocus(el) {
        if (document.activeElement === el) {
            return false;
        }
        var tag = el.nodeName.toLowerCase();
        var skipInputs = ('button checkbox file image radio submit').split(' ');
        if (el.disabled || el.readOnly) return false;
        if (tag === 'textarea') return true;
        if (tag === 'select') {
            if (app.device.android) return false;
            else return true;
        }
        if (tag === 'input' && skipInputs.indexOf(el.type) < 0) return true;
    }
    function targetNeedsPrevent(el) {
        el = $(el);
        var prevent = true;
        if (el.is('label') || el.parents('label').length > 0) {
            if (app.device.android) {
                prevent = false;
            }
            else if (app.device.ios && el.is('input')) {
                prevent = true;
            }
            else prevent = false;
        }
        return prevent;
    }

    // Mouse Handlers
    function handleMouseDown (e) {
        findActivableElement(e).addClass('active-state');
        if ('which' in e && e.which === 3) {
            setTimeout(function () {
                $('.active-state').removeClass('active-state');
            }, 0);
        }
    }
    function handleMouseMove (e) {
        $('.active-state').removeClass('active-state');
    }
    function handleMouseUp (e) {
        $('.active-state').removeClass('active-state');
    }

    // Touch Handlers
    function handleTouchStart(e) {
        isMoved = false;
        if (e.targetTouches.length > 1) {
            return true;
        }
        if (needsFastClickTimeOut) clearTimeout(needsFastClickTimeOut);
        needsFastClick = targetNeedsFastClick(e.target);

        if (!needsFastClick) {
            trackClick = false;
            return true;
        }
        if (app.device.ios) {
            var selection = window.getSelection();
            if (selection.rangeCount && selection.focusNode !== document.body && (!selection.isCollapsed || document.activeElement === selection.focusNode)) {
                activeSelection = true;
                return true;
            }
            else {
                activeSelection = false;
            }
        }
        if (app.device.android)  {
            if (androidNeedsBlur(e.target)) {
                document.activeElement.blur();
            }
        }

        trackClick = true;
        targetElement = e.target;
        touchStartTime = (new Date()).getTime();
        touchStartX = e.targetTouches[0].pageX;
        touchStartY = e.targetTouches[0].pageY;

        // Detect scroll parent
        if (app.device.ios) {
            scrollParent = undefined;
            $(targetElement).parents().each(function () {
                var parent = this;
                if (parent.scrollHeight > parent.offsetHeight && !scrollParent) {
                    scrollParent = parent;
                    scrollParent.f7ScrollTop = scrollParent.scrollTop;
                }
            });
        }
        if ((e.timeStamp - lastClickTime) < app.params.fastClicksDelayBetweenClicks) {
            e.preventDefault();
        }
        if (app.params.activeState) {
            activableElement = findActivableElement(e);
            // If it's inside a scrollable view, we don't trigger active-state yet,
            // because it can be a scroll instead. Based on the link:
            // http://labnote.beedesk.com/click-scroll-and-pseudo-active-on-mobile-webk
            if (!isInsideScrollableView(e)) {
                addActive();
            } else {
                activeTimeout = setTimeout(addActive, 80);
            }
        }
    }
    function handleTouchMove(e) {
        if (!trackClick) return;
        var _isMoved = false;
        var distance = app.params.fastClicksDistanceThreshold;
        if (distance) {
            var pageX = e.targetTouches[0].pageX;
            var pageY = e.targetTouches[0].pageY;
            if (Math.abs(pageX - touchStartX) > distance ||  Math.abs(pageY - touchStartY) > distance) {
                _isMoved = true;
            }
        }
        else {
            _isMoved = true;
        }
        if (_isMoved) {
            trackClick = false;
            targetElement = null;
            isMoved = true;
        }

        if (app.params.activeState) {
            clearTimeout(activeTimeout);
            removeActive();
        }
    }
    function handleTouchEnd(e) {
        clearTimeout(activeTimeout);

        if (!trackClick) {
            if (!activeSelection && needsFastClick) {
                if (!(app.device.android && !e.cancelable)) {
                    e.preventDefault();
                }
            }
            return true;
        }

        if (document.activeElement === e.target) {
            return true;
        }

        if (!activeSelection) {
            e.preventDefault();
        }

        if ((e.timeStamp - lastClickTime) < app.params.fastClicksDelayBetweenClicks) {
            setTimeout(removeActive, 0);
            return true;
        }

        lastClickTime = e.timeStamp;

        trackClick = false;

        if (app.device.ios && scrollParent) {
            if (scrollParent.scrollTop !== scrollParent.f7ScrollTop) {
                return false;
            }
        }

        // Add active-state here because, in a very fast tap, the timeout didn't
        // have the chance to execute. Removing active-state in a timeout gives
        // the chance to the animation execute.
        if (app.params.activeState) {
            addActive();
            setTimeout(removeActive, 0);
        }

        // Trigger focus when required
        if (targetNeedsFocus(targetElement)) {
            targetElement.focus();
        }

        // Blur active elements
        if (document.activeElement && targetElement !== document.activeElement && document.activeElement !== document.body && targetElement.nodeName.toLowerCase() !== 'label') {
            document.activeElement.blur();
        }

        // Send click
        e.preventDefault();
        var touch = e.changedTouches[0];
        var evt = document.createEvent('MouseEvents');
        var eventType = 'click';
        if (app.device.android && targetElement.nodeName.toLowerCase() === 'select') {
            eventType = 'mousedown';
        }
        evt.initMouseEvent(eventType, true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
        evt.forwardedTouchEvent = true;
        targetElement.dispatchEvent(evt);
        return false;
    }
    function handleTouchCancel(e) {
        trackClick = false;
        targetElement = null;
    }

    function handleClick(e) {
        var allowClick = false;
        if (trackClick) {
            targetElement = null;
            trackClick = false;
            return true;
        }
        if (e.target.type === 'submit' && e.detail === 0) {
            return true;
        }
        // if (!targetElement) {
        //     allowClick =  true;
        // }
        if (!needsFastClick) {
            allowClick = true;
        }
        if (document.activeElement === targetElement) {
            allowClick =  true;
        }
        if (e.forwardedTouchEvent) {
            allowClick =  true;
        }
        if (!e.cancelable) {
            allowClick =  true;
        }
        if (!allowClick) {
            e.stopImmediatePropagation();
            e.stopPropagation();
            if (targetElement) {
                if (targetNeedsPrevent(targetElement) || isMoved) {
                    e.preventDefault();
                }
            }
            else {
                e.preventDefault();
            }
            targetElement = null;
        }
        needsFastClickTimeOut = setTimeout(function () {
            needsFastClick = false;
        }, 100);
        return allowClick;
    }
    if (app.support.touch) {
        document.addEventListener('click', handleClick, true);

        document.addEventListener('touchstart', handleTouchStart);
        document.addEventListener('touchmove', handleTouchMove);
        document.addEventListener('touchend', handleTouchEnd);
        document.addEventListener('touchcancel', handleTouchCancel);
    }
    else {
        if (app.params.activeState) {
            document.addEventListener('mousedown', handleMouseDown);
            document.addEventListener('mousemove', handleMouseMove);
            document.addEventListener('mouseup', handleMouseUp);
        }
    }

};
<|endoftext|>


<commit_before>/* General settings modification here.
 ** Copyright © 2014 Ngine Networks.
 */

function get_player_seat_by_nickname(seats, nick) {
    if (seats.seat1.player.nickname == nick) {
        seats.seat1.state = "available";
        return seats.seat1.player.seat_nb;
    } else if (seats.seat2.player.nickname == nick) {
        seats.seat2.state = "available";
        return seats.seat2.player.seat_nb;
    } else if (seats.seat3.player.nickname == nick) {
        seats.seat3.state = "available";
        return seats.seat3.player.seat_nb;
    } else if (seats.seat4.player.nickname == nick) {
        seats.seat4.state = "available";
        return seats.seat4.player.seat_nb;
    } else if (seats.seat5.player.nickname == nick) {
        seats.seat5.state = "available";
        return seats.seat5.player.seat_nb;
    } else if (seats.seat6.player.nickname == nick) {
        seats.seat6.state = "available";
        return seats.seat6.player.seat_nb;
    }
    return (-1);
}

function remove_from_seat_array(table, nick) {
    var idx = 0;

    console.log(nick + " will be kicked");
    while (idx < table.nicknames.length) {
        if (table.nicknames[idx] == nick) {
            table.nicknames.splice(idx, 1);
            return (1);
        }
        ++idx;
    }
    return (-1);
}

function remove_from_playing_seats(playing_seats, idx) {
    var i = 0;

    if (playing_seats.indexOf(idx) == -1)
        return (-1);
    while (i < playing_seats.length) {
        if (playing_seats[i] == idx) {
            playing_seats.splice(i, 1);
            return (1);
        }
        ++i;
    }
    return (-1);
}

function socket_listens_global_settings(socket, table, nb_seat) {
    var socket_nickname;
    var player_seat_idx;
    var private_idx = get_table(table.id, tables).private_ids;
    var private_channelx;
    var i = 0;
    var j = null;
    var seatPlayer;

    socket.emit("player name info");
    socket.on("socket nickname on table", function(nickname) {
        if (nickname && socket_nickname != nickname)
            socket_nickname = nickname;
    });
    socket.on("disconnect", function() {
		re_qr(table, seat_nb);
        if (socket_nickname) {
            player_seat_idx = get_player_seat_by_nickname(get_table(table.id, tables).seats, socket_nickname);
            private_channelx = get_table(table.id, tables).id + player_seat_idx;
            for (; i < private_idx.length; i++) {
                j = private_idx[i];
                if (j == private_channelx)
                    private_idx.splice(i, 1);
            }
            console.log(private_idx);
            if (player_seat_idx && (table.game.moment == "waiting" || table.game.moment == "waiting end game")) {
                console.log('in waiting dc');
                remove_from_seat_array(table, socket_nickname);
                remove_from_playing_seats(table.playing_seats, player_seat_idx);
                console.log("table playing seats " + table.playing_seats);
                io.to(table.id).emit("bet", player_seat_idx, 0);
                io.to(table.id).emit("kick player", player_seat_idx);
                if (table.players_nb > 0)
                    --table.players_nb;
            } else {
                console.log('in game dc');
                remove_from_seat_array(table, socket_nickname);
                remove_from_playing_seats(table.playing_seats, player_seat_idx);
                seatPlayer = get_seat(table.seats, player_seat_idx);
                seatPlayer.player = -1;
                seatPlayer.bet = 0;
                console.log("table playing seats " + table.playing_seats);
                if (player_seat_idx == table.game.highlights_pos) {
                    io.to(table.id).emit("highlights", table.game.highlights_pos, "off");
                    table.game.highlights_pos = 0;
                    if (table.playing_seats.length == 1)
                        console.log('return one player left');
                    else if (table.game.round_nb > table.playing_seats.length)
                        next_moment(table, table.game);
                    else
                        switch_next_player(table)
                }
                io.to(table.id).emit("bet", player_seat_idx, 0);
                io.to(table.id).emit("kick player", player_seat_idx);
                if (table.players_nb > 0)
                    --table.players_nb;
                if (table.playing_seats.length == 1) {
                    return one_playing_player_left(table);
                }
            }
            socket.leave(private_channelx);
            socket.disconnect();
        } else {
			private_channelx = get_table(table.id, tables).id + seat_nb;

            console.log("Seat 'waiting' disconnect");
        	for (; i < private_idx.length; i++) {
                j = private_idx[i];
                if (j == private_channelx) {
                    private_idx.splice(i, 1);
					io.to(table.id).emit("kick player", seat_nb);
				}
            }
        }
    });
}
<commit_msg>fix <commit_after>/* General settings modification here.
 ** Copyright © 2014 Ngine Networks.
 */

function get_player_seat_by_nickname(seats, nick) {
    if (seats.seat1.player.nickname == nick) {
        seats.seat1.state = "available";
        return seats.seat1.player.seat_nb;
    } else if (seats.seat2.player.nickname == nick) {
        seats.seat2.state = "available";
        return seats.seat2.player.seat_nb;
    } else if (seats.seat3.player.nickname == nick) {
        seats.seat3.state = "available";
        return seats.seat3.player.seat_nb;
    } else if (seats.seat4.player.nickname == nick) {
        seats.seat4.state = "available";
        return seats.seat4.player.seat_nb;
    } else if (seats.seat5.player.nickname == nick) {
        seats.seat5.state = "available";
        return seats.seat5.player.seat_nb;
    } else if (seats.seat6.player.nickname == nick) {
        seats.seat6.state = "available";
        return seats.seat6.player.seat_nb;
    }
    return (-1);
}

function remove_from_seat_array(table, nick) {
    var idx = 0;

    console.log(nick + " will be kicked");
    while (idx < table.nicknames.length) {
        if (table.nicknames[idx] == nick) {
            table.nicknames.splice(idx, 1);
            return (1);
        }
        ++idx;
    }
    return (-1);
}

function remove_from_playing_seats(playing_seats, idx) {
    var i = 0;

    if (playing_seats.indexOf(idx) == -1)
        return (-1);
    while (i < playing_seats.length) {
        if (playing_seats[i] == idx) {
            playing_seats.splice(i, 1);
            return (1);
        }
        ++i;
    }
    return (-1);
}

function socket_listens_global_settings(socket, table, nb_seat) {
    var socket_nickname;
    var player_seat_idx;
    var private_idx = get_table(table.id, tables).private_ids;
    var private_channelx;
    var i = 0;
    var j = null;
    var seatPlayer;

    socket.emit("player name info");
    socket.on("socket nickname on table", function(nickname) {
        if (nickname && socket_nickname != nickname)
            socket_nickname = nickname;
    });
    socket.on("disconnect", function() {
		re_qr(table, nb_seat);
        if (socket_nickname) {
            player_seat_idx = get_player_seat_by_nickname(get_table(table.id, tables).seats, socket_nickname);
            private_channelx = get_table(table.id, tables).id + player_seat_idx;
            for (; i < private_idx.length; i++) {
                j = private_idx[i];
                if (j == private_channelx)
                    private_idx.splice(i, 1);
            }
            console.log(private_idx);
            if (player_seat_idx && (table.game.moment == "waiting" || table.game.moment == "waiting end game")) {
                console.log('in waiting dc');
                remove_from_seat_array(table, socket_nickname);
                remove_from_playing_seats(table.playing_seats, player_seat_idx);
                console.log("table playing seats " + table.playing_seats);
                io.to(table.id).emit("bet", player_seat_idx, 0);
                io.to(table.id).emit("kick player", player_seat_idx);
                if (table.players_nb > 0)
                    --table.players_nb;
            } else {
                console.log('in game dc');
                remove_from_seat_array(table, socket_nickname);
                remove_from_playing_seats(table.playing_seats, player_seat_idx);
                seatPlayer = get_seat(table.seats, player_seat_idx);
                seatPlayer.player = -1;
                seatPlayer.bet = 0;
                console.log("table playing seats " + table.playing_seats);
                if (player_seat_idx == table.game.highlights_pos) {
                    io.to(table.id).emit("highlights", table.game.highlights_pos, "off");
                    table.game.highlights_pos = 0;
                    if (table.playing_seats.length == 1)
                        console.log('return one player left');
                    else if (table.game.round_nb > table.playing_seats.length)
                        next_moment(table, table.game);
                    else
                        switch_next_player(table)
                }
                io.to(table.id).emit("bet", player_seat_idx, 0);
                io.to(table.id).emit("kick player", player_seat_idx);
                if (table.players_nb > 0)
                    --table.players_nb;
                if (table.playing_seats.length == 1) {
                    return one_playing_player_left(table);
                }
            }
            socket.leave(private_channelx);
            socket.disconnect();
        } else {
			private_channelx = get_table(table.id, tables).id + seat_nb;

            console.log("Seat 'waiting' disconnect");
        	for (; i < private_idx.length; i++) {
                j = private_idx[i];
                if (j == private_channelx) {
                    private_idx.splice(i, 1);
					io.to(table.id).emit("kick player", seat_nb);
				}
            }
        }
    });
}
<|endoftext|>


<commit_before>'use strict';
var fs = require('fs');

var angular = require('camunda-bpm-sdk-js/vendor/angular'),
    $ = require('jquery'),

    template = fs.readFileSync(__dirname + '/cam-widget-inline-field.html', 'utf8');

module.exports = [
  '$timeout',
  '$filter',
  '$document',
  function(
    $timeout,
    $filter,
    $document
  ) {

    return {
      scope: {
        varValue:       '=value',
        varType:        '@type',
        validator:      '&validate',
        change:         '&',
        onStart:        '&onStartEditing',
        onCancel:       '&onCancelEditing',

        placeholder:    '@',
        options:        '=?',
        allowNonOptions:'@',
        flexible:       '@'
      },

      template: template,

      link: function(scope, element) {
        var $bdyEl = $('body');
        var $btnsEl;
        var $ctrlsEl;

        var dateFilter = $filter('date'),
            dateFormat = 'yyyy-MM-dd\'T\'HH:mm:ss';

        var dateRegex = /(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)(?:.(\d\d\d)| )?$/;

        scope.editing = false;

        scope.isNumber = scope.varType === 'number';

        scope.$on('$locationChangeSuccess', function() {
          scope.cancelChange();
        });
        scope.$on('$destroy', function() {
          removeFromBody(true);
        });

        function convertDateStringToObject(date) {
          var YEAR, MONTH, DAY, HOURS, MINUTES, SECONDS, MILLISECONDS;

          var match = dateRegex.exec(date);

          if (match) {
            YEAR = parseInt(match[1] || 0, 10);
            MONTH = parseInt(match[2] || 0, 10) - 1;
            DAY = parseInt(match[3] || 0, 10);
            HOURS = parseInt(match[4] || 0, 10);
            MINUTES = parseInt(match[5] || 0, 10);
            SECONDS = parseInt(match[6] || 0, 10);
            MILLISECONDS = parseInt(match[7] || 0, 10);
            return new Date(YEAR, MONTH, DAY, HOURS, MINUTES, SECONDS, MILLISECONDS);
          }
        }

        function isDate() {
          return ['datetime', 'date', 'time'].indexOf(scope.varType) > -1;
        }

        function isSimpleField() {
          return ['color', 'email', 'month', 'number', 'range', 'tel', 'text', 'time', 'url', 'week'].indexOf(scope.varType) > -1;
        }

        function reset() {
          scope.editing =       false;
          scope.invalid =       false;
          scope.editValue =     scope.varValue;

          scope.validator =     scope.validator ||     function() {};
          scope.onStart =       scope.onStart ||       function() {};
          scope.onCancel =      scope.onCancel ||      function() {};
          scope.change =        scope.change ||        function() {};
          scope.options =       scope.options ||       [];
          scope.allowNonOptions=scope.allowNonOptions || false;
          scope.flexible =      scope.flexible ||      false;

          scope.varType =       scope.varType ? scope.varType : 'text';

          scope.simpleField = isSimpleField();

          if (isDate()) {
            var dateStr = scope.varValue,
                dateObj = null;

            if (dateStr) {
              dateObj = convertDateStringToObject(dateStr);
            } else {
              dateObj = Date.now();
            }

            scope.dateValue = dateObj;
          }
        }

        function bodyDirectChild($el) {
          if (!$el || !$el.length) {
            return false;
          }

          var $parent = $el.parent();
          if (!$parent || !$parent.length) {
            return false;
          }

          return $parent[0].tagName.toLowerCase() === 'body';
        }

        function positionElements() {
          var offset = $(element).offset();

          if(isDate()) {
            $btnsEl
              .addClass('datepicker-control')
              .show()
              .css({
                left: offset.left + ($ctrlsEl.outerWidth() - $btnsEl.outerWidth()),
                top: offset.top + $ctrlsEl.outerHeight()
              });
          } else {
            $btnsEl
              .removeClass('datepicker-control')
              .show()
              .css({
                left: offset.left + ($(element).outerWidth() - $btnsEl.outerWidth()),
                top: offset.top - $btnsEl.outerHeight()
              });
          }

          $ctrlsEl
            .show()
            .css({
              left: offset.left,
              top: offset.top + $(element).outerHeight()
            });

          if (isDate()) {
            var $datepickerEl = $ctrlsEl[0].querySelector('.uib-daypicker');

            if ($datepickerEl) {
              $datepickerEl.focus();
            }
          }

        }

        function appendToBody() {
          $ctrlsEl = (($ctrlsEl && $ctrlsEl.length) ? $ctrlsEl : $(element[0].querySelector('.field-control')))
                    .hide();
          if (!bodyDirectChild($ctrlsEl)) {
            $bdyEl
              .append($ctrlsEl);
          }

          $btnsEl = (($btnsEl && $btnsEl.length) ? $btnsEl : $(element[0].querySelector('.btn-group')))
                    .hide();
          if (!bodyDirectChild($btnsEl)) {
            $bdyEl
              .append($btnsEl);
          }

          $timeout(positionElements, 50);
        }

        function removeFromBody(force) {
          $timeout(function() {
            if (scope.editing && !force) { return; }

            if ($btnsEl && $btnsEl.remove) {
              $btnsEl.remove();
            }
            $btnsEl = null;

            if ($ctrlsEl && $ctrlsEl.remove) {
              $ctrlsEl.remove();
            }
            $ctrlsEl = null;
          }, 50);
        }

        function bodyClicked(evt) {
          return element[0].contains(evt.target) ||
            ($btnsEl && $btnsEl.length && $btnsEl[0].contains(evt.target)) ||
            ($ctrlsEl && $ctrlsEl.length && $ctrlsEl[0].contains(evt.target));
        }

        scope.$watch('editing', function(after, before) {
          if (after === before) { return; }

          if (scope.editing) {
            appendToBody();
            element.addClass('inline-editing');
          }
          else {
            removeFromBody();
            element.removeClass('inline-editing');
          }
        });

        function stopEditing(evt) {
          if(!scope.editing) {
            return;
          }

          if(bodyClicked(evt)) {
            return;
          }

          var targetElement = $(evt.target),
              expectedClasses = 'ng-binding text-muted';

          if (targetElement.hasClass(expectedClasses)) {
            return;
          }

          var children = targetElement.children();
          if (children.hasClass(expectedClasses)) {
            return;
          }

          scope.$apply(scope.cancelChange);
        }

        scope.changeType = function() {
          if(scope.varType === 'datetime') {
            scope.varType = 'text';
          } else {
            scope.varType = 'datetime';
          }
          reset();
          scope.editing = true;
          element[0].attributes.type.value = scope.varType;
          scope.simpleField = isSimpleField();
        };

        scope.startEditing = function() {
          if(!scope.editing) {
            reset();

            scope.editing = true;
            scope.onStart(scope);

            // save the current edit value to restore later
            var savedEditValue = scope.editValue;

            // clear the edit value so that typeahead shows all options
            // should not apply to simpleFields
            if (!scope.simpleField) {
              scope.editValue = '';
            }

            // Here is where it gets ugly: We have to wait for angular to apply the changes to the edit value, so that the
            // input field is rendered and we can open the typeahead dropdown (first timeout). However, the instantTypeahead directive does
            // not immediately register its functions to prevent IE from going crazy. Therefore, we have to wait for another cycle
            // (second timeout), before triggering an input event on the input field can open the instant typeahead dropdown.
            $timeout(function() {
              $timeout(function() {
                // and reset the edit value, so that the user can type
                scope.editValue = savedEditValue;

                if (savedEditValue.value) {
                  angular.element(element[0].querySelector('[ng-model="editValue"]')).val(savedEditValue.value);
                }

                // in the next apply cycle, the dropdown is open. We now have to
                // select the default value, if it exists:
                if(savedEditValue) {
                  $timeout(function() {
                    var compareValue = typeof savedEditValue === 'object' ? savedEditValue.value : savedEditValue;
                    var elems = $(element[0].querySelector('li[ng-mouseenter]'));

                    for(var i = 0; i < elems.length; i++) {
                      var elem = elems[i];
                      if(elem.innerText.indexOf(compareValue) === 0) {
                        $(elem).trigger('mouseenter');
                        return;
                      }
                    }
                  });
                }
              });
            });

            $timeout(function() {
              angular.element(element[0].querySelector('[ng-model="editValue"]')).triggerHandler('click');

              $timeout(function() {
                $('[ng-model="editValue"]').focus();
                $('[ng-model="editValue"]').select();
                $document.bind('click', stopEditing);
              }, 50);
            });
          }
        };

        scope.applyChange = function(selection, evt) {
          scope.invalid = scope.validator(scope);

          if (!scope.invalid) {

            if(scope.simpleField) {
              scope.editValue = parseValue(
                $('[ng-model="editValue"]').val()
              );

              scope.varValue = scope.editValue;
            }
            else if (scope.varType === 'option') {
              if(scope.options.indexOf(selection) === -1 && !scope.allowNonOptions) {
                scope.cancelChange();
                return;
              }
              scope.editValue = selection || $('[ng-model="editValue"]').val();
              scope.varValue = scope.editValue;
            }
            else if (isDate()) {
              scope.varValue = dateFilter(scope.dateValue, dateFormat);
            }

            scope.$event = evt;
            scope.change(scope);

            scope.editing = false;

            $document.unbind('click', stopEditing);
          }
        };

        function parseValue(value) {
          if (scope.varType === 'number') {
            if (value.length > 0) {
              return +value;
            }

            return null;
          }

          return value;
        }

        scope.cancelChange = function() {
          if(scope.editing) {
            scope.editing = false;
            scope.onCancel(scope);

            $document.unbind('click', stopEditing);
          }
        };

        scope.changeDate = function(pickerScope) {
          scope.editValue = scope.dateValue = pickerScope.dateValue;
        };

        scope.selectNextInlineField = function(reversed) {
          var allFields = $('[cam-widget-inline-field][type=\'text\'], [cam-widget-inline-field][type=\'option\']');
          for(var i = reversed*(allFields.length - 1); i !== !reversed*(allFields.length-1); i += !reversed*2-1) {

            if(allFields[i] === element[0]) {
              $timeout(function() {
                var element = $(allFields[i+!reversed*2-1]);
                element.find('.view-value').click();
                // after clicking the next inline field, wait for the input to appear, then select all
                $timeout(function() {
                  element.find('input').select();
                });
              });
              return;
            }
          }
          // take the first/last one
          $timeout(function() {
            $(allFields[reversed*allFields.length - 1]).find('.view-value').click();
          });
        };

        scope.trapKeyboard = function(evt, reverse) {
          if(isDate() && evt.keyCode === 9) {
            // only trap tab key on date inputs
            if(!reverse && !evt.shiftKey) {
              // focus the date picker
              $('.cam-widget-inline-field.field-control > .datepicker > table')[0].focus();
              evt.preventDefault();
            } else if(reverse && evt.shiftKey) {
              // focus the cancel button
              $('.cam-widget-inline-field.btn-group > button[ng-click="cancelChange($event)"]')[0].focus();
              evt.preventDefault();
            }
          }
        };

        scope.cancelOnEsc = function(evt) {
          if(isDate() && evt.keyCode === 27) {
            scope.cancelChange();
          }
        };

        scope.handleKeydown = function(evt) {
          if(evt.keyCode === 13) {
            scope.applyChange(scope.selection, evt);
            evt.preventDefault();
          } else if(evt.keyCode === 27) {
            scope.cancelChange(evt);
            evt.preventDefault();
          } else if(evt.keyCode === 9) {
            scope.applyChange(scope.selection, evt);
            scope.selectNextInlineField(evt.shiftKey);
            evt.preventDefault();
          }
          scope.selection = null;
        };

        scope.selection = null;
        scope.saveSelection = function(selection) {
          scope.selection = selection;
          $timeout(function() {
            // check if the selection has been applied
            if(scope.selection === selection) {
              scope.applyChange(selection);
            }
          });
        };

      },

      transclude: true
    };
  }];
<commit_msg>fix(tasklist): fix error when seeting a due date or follow-up date<commit_after>'use strict';
var fs = require('fs');

var angular = require('camunda-bpm-sdk-js/vendor/angular'),
    $ = require('jquery'),

    template = fs.readFileSync(__dirname + '/cam-widget-inline-field.html', 'utf8');

module.exports = [
  '$timeout',
  '$filter',
  '$document',
  function(
    $timeout,
    $filter,
    $document
  ) {

    return {
      scope: {
        varValue:       '=value',
        varType:        '@type',
        validator:      '&validate',
        change:         '&',
        onStart:        '&onStartEditing',
        onCancel:       '&onCancelEditing',

        placeholder:    '@',
        options:        '=?',
        allowNonOptions:'@',
        flexible:       '@'
      },

      template: template,

      link: function(scope, element) {
        var $bdyEl = $('body');
        var $btnsEl;
        var $ctrlsEl;

        var dateFilter = $filter('date'),
            dateFormat = 'yyyy-MM-dd\'T\'HH:mm:ss';

        var dateRegex = /(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)(?:.(\d\d\d)| )?$/;

        scope.editing = false;

        scope.isNumber = scope.varType === 'number';

        scope.$on('$locationChangeSuccess', function() {
          scope.cancelChange();
        });
        scope.$on('$destroy', function() {
          removeFromBody(true);
        });

        function convertDateStringToObject(date) {
          var YEAR, MONTH, DAY, HOURS, MINUTES, SECONDS, MILLISECONDS;

          var match = dateRegex.exec(date);

          if (match) {
            YEAR = parseInt(match[1] || 0, 10);
            MONTH = parseInt(match[2] || 0, 10) - 1;
            DAY = parseInt(match[3] || 0, 10);
            HOURS = parseInt(match[4] || 0, 10);
            MINUTES = parseInt(match[5] || 0, 10);
            SECONDS = parseInt(match[6] || 0, 10);
            MILLISECONDS = parseInt(match[7] || 0, 10);
            return new Date(YEAR, MONTH, DAY, HOURS, MINUTES, SECONDS, MILLISECONDS);
          }
        }

        function isDate() {
          return ['datetime', 'date', 'time'].indexOf(scope.varType) > -1;
        }

        function isSimpleField() {
          return ['color', 'email', 'month', 'number', 'range', 'tel', 'text', 'time', 'url', 'week'].indexOf(scope.varType) > -1;
        }

        function reset() {
          scope.editing =       false;
          scope.invalid =       false;
          scope.editValue =     scope.varValue;

          scope.validator =     scope.validator ||     function() {};
          scope.onStart =       scope.onStart ||       function() {};
          scope.onCancel =      scope.onCancel ||      function() {};
          scope.change =        scope.change ||        function() {};
          scope.options =       scope.options ||       [];
          scope.allowNonOptions=scope.allowNonOptions || false;
          scope.flexible =      scope.flexible ||      false;

          scope.varType =       scope.varType ? scope.varType : 'text';

          scope.simpleField = isSimpleField();

          if (isDate()) {
            var dateStr = scope.varValue,
                dateObj = null;

            if (dateStr) {
              dateObj = convertDateStringToObject(dateStr);
            } else {
              dateObj = Date.now();
            }

            scope.dateValue = dateObj;
          }
        }

        function bodyDirectChild($el) {
          if (!$el || !$el.length) {
            return false;
          }

          var $parent = $el.parent();
          if (!$parent || !$parent.length) {
            return false;
          }

          return $parent[0].tagName.toLowerCase() === 'body';
        }

        function positionElements() {
          var offset = $(element).offset();

          if(isDate()) {
            $btnsEl
              .addClass('datepicker-control')
              .show()
              .css({
                left: offset.left + ($ctrlsEl.outerWidth() - $btnsEl.outerWidth()),
                top: offset.top + $ctrlsEl.outerHeight()
              });
          } else {
            $btnsEl
              .removeClass('datepicker-control')
              .show()
              .css({
                left: offset.left + ($(element).outerWidth() - $btnsEl.outerWidth()),
                top: offset.top - $btnsEl.outerHeight()
              });
          }

          $ctrlsEl
            .show()
            .css({
              left: offset.left,
              top: offset.top + $(element).outerHeight()
            });

          if (isDate()) {
            var $datepickerEl = $ctrlsEl[0].querySelector('.uib-daypicker');

            if ($datepickerEl) {
              $datepickerEl.focus();
            }
          }

        }

        function appendToBody() {
          $ctrlsEl = (($ctrlsEl && $ctrlsEl.length) ? $ctrlsEl : $(element[0].querySelector('.field-control')))
                    .hide();
          if (!bodyDirectChild($ctrlsEl)) {
            $bdyEl
              .append($ctrlsEl);
          }

          $btnsEl = (($btnsEl && $btnsEl.length) ? $btnsEl : $(element[0].querySelector('.btn-group')))
                    .hide();
          if (!bodyDirectChild($btnsEl)) {
            $bdyEl
              .append($btnsEl);
          }

          $timeout(positionElements, 50);
        }

        function removeFromBody(force) {
          $timeout(function() {
            if (scope.editing && !force) { return; }

            if ($btnsEl && $btnsEl.remove) {
              $btnsEl.remove();
            }
            $btnsEl = null;

            if ($ctrlsEl && $ctrlsEl.remove) {
              $ctrlsEl.remove();
            }
            $ctrlsEl = null;
          }, 50);
        }

        function bodyClicked(evt) {
          return element[0].contains(evt.target) ||
            ($btnsEl && $btnsEl.length && $btnsEl[0].contains(evt.target)) ||
            ($ctrlsEl && $ctrlsEl.length && $ctrlsEl[0].contains(evt.target));
        }

        scope.$watch('editing', function(after, before) {
          if (after === before) { return; }

          if (scope.editing) {
            appendToBody();
            element.addClass('inline-editing');
          }
          else {
            removeFromBody();
            element.removeClass('inline-editing');
          }
        });

        function stopEditing(evt) {
          if(!scope.editing) {
            return;
          }

          if(bodyClicked(evt)) {
            return;
          }

          var targetElement = $(evt.target),
              expectedClasses = 'ng-binding text-muted';

          if (targetElement.hasClass(expectedClasses)) {
            return;
          }

          var children = targetElement.children();
          if (children.hasClass(expectedClasses)) {
            return;
          }

          scope.$apply(scope.cancelChange);
        }

        scope.changeType = function() {
          if(scope.varType === 'datetime') {
            scope.varType = 'text';
          } else {
            scope.varType = 'datetime';
          }
          reset();
          scope.editing = true;
          element[0].attributes.type.value = scope.varType;
          scope.simpleField = isSimpleField();
        };

        scope.startEditing = function() {
          if(!scope.editing) {
            reset();

            scope.editing = true;
            scope.onStart(scope);

            // save the current edit value to restore later
            var savedEditValue = scope.editValue;

            // clear the edit value so that typeahead shows all options
            // should not apply to simpleFields
            if (!scope.simpleField) {
              scope.editValue = '';
            }

            // Here is where it gets ugly: We have to wait for angular to apply the changes to the edit value, so that the
            // input field is rendered and we can open the typeahead dropdown (first timeout). However, the instantTypeahead directive does
            // not immediately register its functions to prevent IE from going crazy. Therefore, we have to wait for another cycle
            // (second timeout), before triggering an input event on the input field can open the instant typeahead dropdown.
            $timeout(function() {
              $timeout(function() {
                // and reset the edit value, so that the user can type
                scope.editValue = savedEditValue;

                if (savedEditValue) {
                  angular.element(element[0].querySelector('[ng-model="editValue"]')).val(savedEditValue);
                }

                // in the next apply cycle, the dropdown is open. We now have to
                // select the default value, if it exists:
                if(savedEditValue) {
                  $timeout(function() {
                    var compareValue = typeof savedEditValue === 'object' ? savedEditValue.value : savedEditValue;
                    var elems = $(element[0].querySelector('li[ng-mouseenter]'));

                    for(var i = 0; i < elems.length; i++) {
                      var elem = elems[i];
                      if(elem.innerText.indexOf(compareValue) === 0) {
                        $(elem).trigger('mouseenter');
                        return;
                      }
                    }
                  });
                }
              });
            });

            $timeout(function() {
              angular.element(element[0].querySelector('[ng-model="editValue"]')).triggerHandler('click');

              $timeout(function() {
                $('[ng-model="editValue"]').focus();
                $('[ng-model="editValue"]').select();
                $document.bind('click', stopEditing);
              }, 50);
            });
          }
        };

        scope.applyChange = function(selection, evt) {
          scope.invalid = scope.validator(scope);

          if (!scope.invalid) {

            if(scope.simpleField) {
              scope.editValue = parseValue(
                $('[ng-model="editValue"]').val()
              );

              scope.varValue = scope.editValue;
            }
            else if (scope.varType === 'option') {
              if(scope.options.indexOf(selection) === -1 && !scope.allowNonOptions) {
                scope.cancelChange();
                return;
              }
              scope.editValue = selection || $('[ng-model="editValue"]').val();
              scope.varValue = scope.editValue;
            }
            else if (isDate()) {
              scope.varValue = dateFilter(scope.dateValue, dateFormat);
            }

            scope.$event = evt;
            scope.change(scope);

            scope.editing = false;

            $document.unbind('click', stopEditing);
          }
        };

        function parseValue(value) {
          if (scope.varType === 'number') {
            if (value.length > 0) {
              return +value;
            }

            return null;
          }

          return value;
        }

        scope.cancelChange = function() {
          if(scope.editing) {
            scope.editing = false;
            scope.onCancel(scope);

            $document.unbind('click', stopEditing);
          }
        };

        scope.changeDate = function(pickerScope) {
          scope.editValue = scope.dateValue = pickerScope.dateValue;
        };

        scope.selectNextInlineField = function(reversed) {
          var allFields = $('[cam-widget-inline-field][type=\'text\'], [cam-widget-inline-field][type=\'option\']');
          for(var i = reversed*(allFields.length - 1); i !== !reversed*(allFields.length-1); i += !reversed*2-1) {

            if(allFields[i] === element[0]) {
              $timeout(function() {
                var element = $(allFields[i+!reversed*2-1]);
                element.find('.view-value').click();
                // after clicking the next inline field, wait for the input to appear, then select all
                $timeout(function() {
                  element.find('input').select();
                });
              });
              return;
            }
          }
          // take the first/last one
          $timeout(function() {
            $(allFields[reversed*allFields.length - 1]).find('.view-value').click();
          });
        };

        scope.trapKeyboard = function(evt, reverse) {
          if(isDate() && evt.keyCode === 9) {
            // only trap tab key on date inputs
            if(!reverse && !evt.shiftKey) {
              // focus the date picker
              $('.cam-widget-inline-field.field-control > .datepicker > table')[0].focus();
              evt.preventDefault();
            } else if(reverse && evt.shiftKey) {
              // focus the cancel button
              $('.cam-widget-inline-field.btn-group > button[ng-click="cancelChange($event)"]')[0].focus();
              evt.preventDefault();
            }
          }
        };

        scope.cancelOnEsc = function(evt) {
          if(isDate() && evt.keyCode === 27) {
            scope.cancelChange();
          }
        };

        scope.handleKeydown = function(evt) {
          if(evt.keyCode === 13) {
            scope.applyChange(scope.selection, evt);
            evt.preventDefault();
          } else if(evt.keyCode === 27) {
            scope.cancelChange(evt);
            evt.preventDefault();
          } else if(evt.keyCode === 9) {
            scope.applyChange(scope.selection, evt);
            scope.selectNextInlineField(evt.shiftKey);
            evt.preventDefault();
          }
          scope.selection = null;
        };

        scope.selection = null;
        scope.saveSelection = function(selection) {
          scope.selection = selection;
          $timeout(function() {
            // check if the selection has been applied
            if(scope.selection === selection) {
              scope.applyChange(selection);
            }
          });
        };

      },

      transclude: true
    };
  }];
<|endoftext|>


<commit_before>$(function () {
    var questionList;
    $(document).ready(function () {
        $.ajax({
            url: "http://localhost:3001/newgame/add/1"
        }).then(function (data) {
            var questionList = data;
            Object.entries(questionList).forEach(
                ([key, value]) => {
                    console.log(key, value)
                    var questionStr = `#question${key}`;
                    console.log(questionStr);
                    console.log(value.q)
                    console.log(value.a)
                    $(questionStr).text(value.q);
                }
            );
        });
        $("#answer0").focus();
    });

    $("form").submit(function (e) {
        var errors = false;
        e.preventDefault();

        if ($('#answer0').val() != 1) {
            errors = true;
            $("#result0").text("  Incorrect");
        }

        // if ($('#answer1').val() != 2) {
        //     errors = true;
        //     $("#question1").append("  Incorrect");
        // }

        // if ($('#answer2').val() != 3) {
        //     errors = true;
        //     $('#question2').append("  Incorrect");
        // }

        // // validate answer3
        // if ($('#answer3').val() != 4) {
        //     errors = true;
        //     $('#question3').append("  Incorrect");
        // }

        // // validate answer4
        // if ($('#answer4').val() != 5) {
        //     errors = true;
        //     $('#question4').append("  Incorrect");
        // }

        // // validate answer5
        // if ($('#answer5').val() != 6) {
        //     errors = true;
        //     $('#question5').append("  Incorrect");
        // }

        // // validate answer6
        // if ($('#answer6').val() != 7) {
        //     errors = true;
        //     $('#question6').append("  Incorrect");
        // }

        // // validate answer7
        // if ($('#answer7').val() != 8) {
        //     errors = true;
        //     $('#question7').append("  Incorrect");
        // }

        // // validate answer8
        // if ($('#answer8').val() != 9) {
        //     errors = true;
        //     $('#question8').append("  Incorrect");
        // }

        // // validate answer9
        // if ($('#answer8').val() != 9) {
        //     errors = true;
        //     $('#question9').append("  Incorrect");
        // }

        // // validate answer10
        // if ($('#answer10').val() != 11) {
        //     errors = true;
        //     $('#question10').append("  Incorrect");
        // }

        /* validate the answers 3 through 10 */
        if (errors) {
            return false;
        }
        alert("All answers are correct.")
        $(this).unbind('submit').submit()
    });

    // $("form").submit(function () {
    //     var errors = [];
    //     if ($('#answer1').val() != 1) {
    //         errors.push({
    //             error: 'Invalid answer for 1 + 0'
    //         });
    //     }

    //     if(errors.length>0){
    //         $('#errors').text = errors;

    //         return;
    //     }
    // });
});<commit_msg>remove localhost<commit_after>$(function () {
    var questionList;
    $(document).ready(function () {
        $.ajax({
            url: "/newgame/add/1"
        }).then(function (data) {
            var questionList = data;
            Object.entries(questionList).forEach(
                ([key, value]) => {
                    console.log(key, value)
                    var questionStr = `#question${key}`;
                    console.log(questionStr);
                    console.log(value.q)
                    console.log(value.a)
                    $(questionStr).text(value.q);
                }
            );
        });
        $("#answer0").focus();
    });

    $("form").submit(function (e) {
        var errors = false;
        e.preventDefault();

        if ($('#answer0').val() != 1) {
            errors = true;
            $("#result0").text("  Incorrect");
        }

        // if ($('#answer1').val() != 2) {
        //     errors = true;
        //     $("#question1").append("  Incorrect");
        // }

        // if ($('#answer2').val() != 3) {
        //     errors = true;
        //     $('#question2').append("  Incorrect");
        // }

        // // validate answer3
        // if ($('#answer3').val() != 4) {
        //     errors = true;
        //     $('#question3').append("  Incorrect");
        // }

        // // validate answer4
        // if ($('#answer4').val() != 5) {
        //     errors = true;
        //     $('#question4').append("  Incorrect");
        // }

        // // validate answer5
        // if ($('#answer5').val() != 6) {
        //     errors = true;
        //     $('#question5').append("  Incorrect");
        // }

        // // validate answer6
        // if ($('#answer6').val() != 7) {
        //     errors = true;
        //     $('#question6').append("  Incorrect");
        // }

        // // validate answer7
        // if ($('#answer7').val() != 8) {
        //     errors = true;
        //     $('#question7').append("  Incorrect");
        // }

        // // validate answer8
        // if ($('#answer8').val() != 9) {
        //     errors = true;
        //     $('#question8').append("  Incorrect");
        // }

        // // validate answer9
        // if ($('#answer8').val() != 9) {
        //     errors = true;
        //     $('#question9').append("  Incorrect");
        // }

        // // validate answer10
        // if ($('#answer10').val() != 11) {
        //     errors = true;
        //     $('#question10').append("  Incorrect");
        // }

        /* validate the answers 3 through 10 */
        if (errors) {
            return false;
        }
        alert("All answers are correct.")
        $(this).unbind('submit').submit()
    });

    // $("form").submit(function () {
    //     var errors = [];
    //     if ($('#answer1').val() != 1) {
    //         errors.push({
    //             error: 'Invalid answer for 1 + 0'
    //         });
    //     }

    //     if(errors.length>0){
    //         $('#errors').text = errors;

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


<commit_before>/*
 * Prana Application.
 *
 * The Prana application class.
 */

/*
 * Module dependencies.
 */
var util = require('util');
var fs = require('fs');
var path = require('path');
var async = require('async');
var EventEmitter = require('events').EventEmitter;
var utils = require('./lib/utils');

/**
 * Prana constructor.
 *
 * @constructor
 * @inherits EventEmitter
 * @param {Object} settings Application settings.
 */
var Prana = module.exports = function(settings) {
  EventEmitter.call(this);

  this.settings = {
    extensionFileSuffix: '.extension.json',
    commonDependencies: [],
    extensions: null
  };

  if (settings) {
    utils.extend(this.settings, settings);
  }

  // Types container.
  this.types = {};

  // Extensions container.
  this.extensions = {};

  var self = this;

  // Add 'type' core type.
  this.type('type', {
    title: 'Type',
    description: 'Types are the smallest things on the system.',
    storageSettings: {
      // Set data as reference to this.types.
      data: this.types
    },
    process: function(name, settings) {
      var controller = settings.controller || Prana.Type;
      var typeObject = new controller(self, name, settings);
      return Prana.Model.compile(self, typeObject);
    },
    key: 'name'
  });

  // Add 'extension' core type.
  this.type('extension', {
    title: 'Extension',
    description: 'Extensions can extend every type on the system, as well as react on events.',
    storageSettings: {
      // Set data as reference to this.extensions.
      data: this.extensions
    },
    process: function(name, settings) {
      var controller = settings.controller || Prana.Extension;
      return new controller(self, name, settings);
    },
    key: 'name'
  });
};

util.inherits(Prana, EventEmitter);

// Add components to Prana namespace.
Prana.utils = require('./lib/utils');
Prana.MemoryStorage = require('./lib/memory');
Prana.Type = require('./lib/type');
Prana.Model = require('./lib/model');
Prana.Extension = require('./lib/extension');

/**
 * Make sure all extensions and types are loaded.
 *
 * @param {Function} callback Callback to run after prana was initialized.
 */
Prana.prototype.init = function(callback) {
  var self = this;
  async.mapSeries(['extension', 'type'], function(typeName, next) {
    var TypeModel = self.type(typeName);
    // Just calling this will set this.extension and this.type vars since those
    // vars were passed to storage settings.
    TypeModel.list({}, next);
  },
  function(err, results) {
    if (err) {
      return callback(err);
    }

    // Run init hook on all modules.
    self.invoke('init', self, function() {
      callback.apply(self, results);
    })
  });
};

/**
 * Create new type specific objects.
 *
 * @param {String} type A type name string.
 * @param {Object} values Initial object values.
 * @return {Model} Actually an instance of a subclass of Model, the type
 *   specific model class instance.
 */
Prana.prototype.new = function(type, values) {
  var TypeModel = this.type(type);
  return new TypeModel(values);
};

/**
 * Set or get a type.
 *
 * @param {String} name A string to be used to identify the type.
 * @param {Object} settings The settings for for the type to be created.
 * @return {Model} A Model subclass created exclusivelly for this type.
 */
Prana.prototype.type = function(name, settings) {
  // If there's no settings we want to get a type.
  if (!settings) {
    // Return this as earlier as possible.
    return this.types[name];
  }
  else {
    if (name === 'type') {
      // We are creating the 'type' type, use process from settings.
      return this.types[name] = settings.process(name, settings);
    }
    else {
      // Run the process function from the 'type' type that's actually a model
      // class instead of a type specific model instance.
      return this.types[name] = this.types['type'].type.process(name, settings);
    }
  }
};

/**
 * Set or get an extension.
 *
 * @param {String} extension A string to be used to identify the extension.
 * @param {Object} settings Extension settings.
 * @return {Extension} Extension instance.
 */
Prana.prototype.extension = function(name, settings) {
  // If there's no settings we want to get a extension.
  if (!settings) {
    // Return this as earlier as possible.
    return this.extensions[name];
  }
  else {
    // Run the process function from the 'extension' type.
    return this.extensions[name] = this.types['extension'].type.process(name, settings);
  }
};

/**
 * Scan the file system for extensions.
 *
 * @param {String} dir Path to extensions container directory.
 * @param {Function} callback Function to run when data is returned.
 */
Prana.prototype.loadExtensions = function(dir, callback) {
  var self = this;
  var foundExtensions = {};

  // Scan directory for JSON files matching settings.extensionFileSuffix.
  Prana.Extension.scan(dir, this.settings.extensionFileSuffix, function(file, data, next) {
    // Build extension info.
    var settings = JSON.parse(data);
    settings.path = path.dirname(file);
    settings.name = settings.name || path.basename(file, self.settings.extensionFileSuffix);

    // Set dependency chain merging dependencies and common dependencies if any.
    // Clone settings.commonDependencies array to avoid affecting other
    // applications.
    settings.dependencyChain = self.settings.commonDependencies.concat([]);
    if (settings.dependencies) {
      settings.dependencies.forEach(function(dependency) {
        if (settings.dependencyChain.indexOf(dependency) === -1) {
          settings.dependencyChain.push(dependency);
        }
      });
    }

    // In the case we are enabling a module that's in setting.commonDependencies
    // we need to remove the extension from it's own list of dependencies.
    var index = settings.dependencyChain.indexOf(settings.name);
    if (index !== -1) {
      settings.dependencyChain.splice(index, 1);
    }

    // Add to found extensions.
    foundExtensions[settings.name] = settings;

    next();
  }, function(err) {
    if (err) {
      return callback(err);
    }

    // If settings.extensions is not set, enabled all extensions found.
    var enabled = self.settings.extensions ? Object.keys(self.settings.extensions) : Object.keys(foundExtensions);
    var chains = {};
    var enabledExtensions = {};
    async.each(enabled, function(extensionName, next) {
      if (!foundExtensions[extensionName]) {
        return next(new Error('Extension not found ' + extensionName + '.'));
      }

      var settings = foundExtensions[extensionName];

      // Check if there are missing dependecies.
      var missingDependencies = settings.dependencyChain.filter(function(dependency) {
        return !foundExtensions[dependency];
      });

      // Fail if there are missing dependencies.
      if (missingDependencies.length > 0) {
        return next(new Error('Missing dependecies for ' + extensionName + ' extension: ' + missingDependencies.join(', ') + '.'));
      }

      // Build extension prototype.
      var prototypeFile = settings.path + '/' + settings.name + '.js';
      fs.exists(prototypeFile, function(exists) {
        // Allow extensions without a prototype. E.g. for feature extensions
        // that just have JSON files with some resources.
        settings.prototype = exists ? require(prototypeFile) : {};
        foundExtensions[settings.name] = settings;

        // Add extension dependencies and initialization function to the
        // dependency chains, without changing settings.dependencyChain.
        chains[settings.name] = settings.dependencyChain.concat([function(next) {
          // Get module settings from application settings and add module info
          // to that.
          var instanceSettings = self.settings.extensions ? self.settings.extensions[settings.name] : {};
          utils.extend(settings, instanceSettings);

          // Initialize and add the module instance to the application.
          enabledExtensions[settings.name] = self.extension(settings.name, settings);

          next();
        }]);
        next();
      });
    }, function (err) {
      if (err) {
        return callback(err);
      }

      // Execute all initialization functions declared above taking into
      // account module dependencies.
      async.auto(chains, function(err) {
        callback(err, enabledExtensions);
      });
    });
  });
};

/**
 * Invoke a hook in all extensions that implement it.
 *
 * @param {String} hook Name of the hook to run.
 * @param {...Mixed} arg Variable number of arguments to pass the hook
 *   implementation.
 * @param {Function} callback Callback to run after all implementations have
 *   been invoked.
 */
Prana.prototype.invoke = function() {
  var args = Array.prototype.slice.call(arguments);
  var callback = args.pop();
  var self = this;
  var chains = {};

  async.map(Object.keys(this.extensions), function(extensionName, next) {
    var extension = self.extensions[extensionName];
    var settings = extension.settings;
    var extensionInvoke = function(next) {
      // Make a copy of arguments to avoid appending all next callbacks to the
      // main arguments object.
      var argsCopy = args.slice();
      argsCopy.push(next);

      // Invoke extension hook.
      extension.invoke.apply(extension, argsCopy);
    };

    if (settings.dependencyChain) {
      // Clone extension dependency chain and add invocation function to the
      // dependency chains, without changing settings.dependencyChain.
      chains[extensionName] = settings.dependencyChain.concat([extensionInvoke]);
    }
    else {
      // Module doesn't have any dependencies.
      chains[extensionName] = extensionInvoke;
    }

    next();
  }, function (err) {
    if (err) {
      return callback(err);
    }

    // Execute all hooks taking into account module dependencies.
    async.auto(chains, callback);
  });
};

/**
 * Collect data from JSON files and type specific hooks and add them to the data
 * container object.
 *
 * @param {Type} type Type object to collect data for.
 * @param {Object} data Data container object.
 * @param {Function} callback Callback to run after all data have been
 *   collected.
 */
Prana.prototype.collect = function(type, data, callback) {
  // The extension type don't allow collecting.
  if (type.name == 'extension') {
    return callback();
  }

  var result = {};
  var self = this;

  // Loop over all extensions to collect their items.
  async.each(Object.keys(this.extensions), function(extensionName, next) {
    var extension = self.extensions[extensionName];

    if (extension.settings.path) {
      // Scan for JSON files for this type.
      Prana.Extension.scanItems(extension.settings.path, type.name, function(err, foundItems) {
        if (err) {
          return next(err);
        }

        if (foundItems) {
          // If items was found add them to the result.
          utils.extend(result, foundItems);
        }

        next();
      });
    }
    else {
      next();
    }
  }, function(err) {
    // Process all items from JSON files.
    Prana.Type.processAll(type, result, data);

    // Invoke type hooks on all modules.
    self.invoke(type.name, data, function(err, hookData) {
      if (err) {
        return callback(err);
      }

      if (hookData) {
        // Each hook implemention can return many items.
        for (var extensionName in hookData) {
          // Process each set of items.
          Prana.Type.processAll(type, hookData[extensionName], data);
        }
      }

      callback(err);
    });

  });
};
<commit_msg>Making possible to pass an array of directories to prana.loadExtensions().<commit_after>/*
 * Prana Application.
 *
 * The Prana application class.
 */

/*
 * Module dependencies.
 */
var util = require('util');
var fs = require('fs');
var path = require('path');
var async = require('async');
var EventEmitter = require('events').EventEmitter;
var utils = require('./lib/utils');

/**
 * Prana constructor.
 *
 * @constructor
 * @inherits EventEmitter
 * @param {Object} settings Application settings.
 */
var Prana = module.exports = function(settings) {
  EventEmitter.call(this);

  this.settings = {
    extensionFileSuffix: '.extension.json',
    commonDependencies: [],
    extensions: null
  };

  if (settings) {
    utils.extend(this.settings, settings);
  }

  // Types container.
  this.types = {};

  // Extensions container.
  this.extensions = {};

  var self = this;

  // Add 'type' core type.
  this.type('type', {
    title: 'Type',
    description: 'Types are the smallest things on the system.',
    storageSettings: {
      // Set data as reference to this.types.
      data: this.types
    },
    process: function(name, settings) {
      var controller = settings.controller || Prana.Type;
      var typeObject = new controller(self, name, settings);
      return Prana.Model.compile(self, typeObject);
    },
    key: 'name'
  });

  // Add 'extension' core type.
  this.type('extension', {
    title: 'Extension',
    description: 'Extensions can extend every type on the system, as well as react on events.',
    storageSettings: {
      // Set data as reference to this.extensions.
      data: this.extensions
    },
    process: function(name, settings) {
      var controller = settings.controller || Prana.Extension;
      return new controller(self, name, settings);
    },
    key: 'name'
  });
};

util.inherits(Prana, EventEmitter);

// Add components to Prana namespace.
Prana.utils = require('./lib/utils');
Prana.MemoryStorage = require('./lib/memory');
Prana.Type = require('./lib/type');
Prana.Model = require('./lib/model');
Prana.Extension = require('./lib/extension');

/**
 * Make sure all extensions and types are loaded.
 *
 * @param {Function} callback Callback to run after prana was initialized.
 */
Prana.prototype.init = function(callback) {
  var self = this;
  async.mapSeries(['extension', 'type'], function(typeName, next) {
    var TypeModel = self.type(typeName);
    // Just calling this will set this.extension and this.type vars since those
    // vars were passed to storage settings.
    TypeModel.list({}, next);
  },
  function(err, results) {
    if (err) {
      return callback(err);
    }

    // Run init hook on all modules.
    self.invoke('init', self, function() {
      callback.apply(self, results);
    })
  });
};

/**
 * Create new type specific objects.
 *
 * @param {String} type A type name string.
 * @param {Object} values Initial object values.
 * @return {Model} Actually an instance of a subclass of Model, the type
 *   specific model class instance.
 */
Prana.prototype.new = function(type, values) {
  var TypeModel = this.type(type);
  return new TypeModel(values);
};

/**
 * Set or get a type.
 *
 * @param {String} name A string to be used to identify the type.
 * @param {Object} settings The settings for for the type to be created.
 * @return {Model} A Model subclass created exclusivelly for this type.
 */
Prana.prototype.type = function(name, settings) {
  // If there's no settings we want to get a type.
  if (!settings) {
    // Return this as earlier as possible.
    return this.types[name];
  }
  else {
    if (name === 'type') {
      // We are creating the 'type' type, use process from settings.
      return this.types[name] = settings.process(name, settings);
    }
    else {
      // Run the process function from the 'type' type that's actually a model
      // class instead of a type specific model instance.
      return this.types[name] = this.types['type'].type.process(name, settings);
    }
  }
};

/**
 * Set or get an extension.
 *
 * @param {String} extension A string to be used to identify the extension.
 * @param {Object} settings Extension settings.
 * @return {Extension} Extension instance.
 */
Prana.prototype.extension = function(name, settings) {
  // If there's no settings we want to get a extension.
  if (!settings) {
    // Return this as earlier as possible.
    return this.extensions[name];
  }
  else {
    // Run the process function from the 'extension' type.
    return this.extensions[name] = this.types['extension'].type.process(name, settings);
  }
};

/**
 * Scan the file system for extensions.
 *
 * @param {String|Array} dir Path to extensions container directory this can be
 *   also an array of paths.
 * @param {Function} callback Function to run when data is returned.
 */
Prana.prototype.loadExtensions = function(dir, callback) {
  var self = this;
  var foundExtensions = {};

  // If dir is a string convert ir to an array.
  var dirs = (typeof dir === 'string') ? [dir] : dir;

  // We loop dirs in series since order is important.
  async.eachSeries(dirs, function(dir, next) {

    // Scan directory for JSON files matching settings.extensionFileSuffix.
    Prana.Extension.scan(dir, self.settings.extensionFileSuffix, function(file, data, next) {

      // Parse data and build extension settings.
      try {
        var settings = JSON.parse(data);
        settings.path = path.dirname(file);
        settings.name = settings.name || path.basename(file, self.settings.extensionFileSuffix);
      } catch (err) {
        return next(err);
      }

      // Set dependency chain merging dependencies and common dependencies if any.
      // Clone settings.commonDependencies array to avoid affecting other
      // applications.
      settings.dependencyChain = self.settings.commonDependencies.concat([]);
      if (settings.dependencies) {
        settings.dependencies.forEach(function(dependency) {
          if (settings.dependencyChain.indexOf(dependency) === -1) {
            settings.dependencyChain.push(dependency);
          }
        });
      }

      // In the case we are enabling a module that's in setting.commonDependencies
      // we need to remove the extension from it's own list of dependencies.
      var index = settings.dependencyChain.indexOf(settings.name);
      if (index !== -1) {
        settings.dependencyChain.splice(index, 1);
      }

      // Add to found extensions.
      foundExtensions[settings.name] = settings;
      next();
    }, next);
  }, function (err) {
    if (err) {
      return callback(err);
    }

    // If settings.extensions is not set, enabled all extensions found.
    var enabled = self.settings.extensions ? Object.keys(self.settings.extensions) : Object.keys(foundExtensions);
    var chains = {};
    var enabledExtensions = {};
    async.each(enabled, function(extensionName, next) {
      if (!foundExtensions[extensionName]) {
        return next(new Error('Extension not found ' + extensionName + '.'));
      }

      var settings = foundExtensions[extensionName];

      // Check if there are missing dependecies.
      var missingDependencies = settings.dependencyChain.filter(function(dependency) {
        return !foundExtensions[dependency];
      });

      // Fail if there are missing dependencies.
      if (missingDependencies.length > 0) {
        return next(new Error('Missing dependecies for ' + extensionName + ' extension: ' + missingDependencies.join(', ') + '.'));
      }

      // Build extension prototype.
      var prototypeFile = settings.path + '/' + settings.name + '.js';
      fs.exists(prototypeFile, function(exists) {
        // Allow extensions without a prototype. E.g. for feature extensions
        // that just have JSON files with some resources.
        settings.prototype = exists ? require(prototypeFile) : {};

        // Add extension dependencies and initialization function to the
        // dependency chains, without changing settings.dependencyChain.
        chains[settings.name] = settings.dependencyChain.concat([function(next) {
          // Get module settings from application settings and add module info
          // to that.
          var instanceSettings = self.settings.extensions ? self.settings.extensions[settings.name] : {};
          utils.extend(settings, instanceSettings);

          // Initialize and add the module instance to the application.
          enabledExtensions[settings.name] = self.extension(settings.name, settings);

          next();
        }]);
        next();
      });
    }, function (err) {
      if (err) {
        return callback(err);
      }

      // Execute all initialization functions declared above taking into
      // account module dependencies.
      async.auto(chains, function(err) {
        callback(err, enabledExtensions);
      });
    });
  });
};

/**
 * Invoke a hook in all extensions that implement it.
 *
 * @param {String} hook Name of the hook to run.
 * @param {...Mixed} arg Variable number of arguments to pass the hook
 *   implementation.
 * @param {Function} callback Callback to run after all implementations have
 *   been invoked.
 */
Prana.prototype.invoke = function() {
  var args = Array.prototype.slice.call(arguments);
  var callback = args.pop();
  var self = this;
  var chains = {};

  async.map(Object.keys(this.extensions), function(extensionName, next) {
    var extension = self.extensions[extensionName];
    var settings = extension.settings;
    var extensionInvoke = function(next) {
      // Make a copy of arguments to avoid appending all next callbacks to the
      // main arguments object.
      var argsCopy = args.slice();
      argsCopy.push(next);

      // Invoke extension hook.
      extension.invoke.apply(extension, argsCopy);
    };

    if (settings.dependencyChain) {
      // Clone extension dependency chain and add invocation function to the
      // dependency chains, without changing settings.dependencyChain.
      chains[extensionName] = settings.dependencyChain.concat([extensionInvoke]);
    }
    else {
      // Module doesn't have any dependencies.
      chains[extensionName] = extensionInvoke;
    }

    next();
  }, function (err) {
    if (err) {
      return callback(err);
    }

    // Execute all hooks taking into account module dependencies.
    async.auto(chains, callback);
  });
};

/**
 * Collect data from JSON files and type specific hooks and add them to the data
 * container object.
 *
 * @param {Type} type Type object to collect data for.
 * @param {Object} data Data container object.
 * @param {Function} callback Callback to run after all data have been
 *   collected.
 */
Prana.prototype.collect = function(type, data, callback) {
  // The extension type don't allow collecting.
  if (type.name == 'extension') {
    return callback();
  }

  var result = {};
  var self = this;

  // Loop over all extensions to collect their items.
  async.each(Object.keys(this.extensions), function(extensionName, next) {
    var extension = self.extensions[extensionName];

    if (extension.settings.path) {
      // Scan for JSON files for this type.
      Prana.Extension.scanItems(extension.settings.path, type.name, function(err, foundItems) {
        if (err) {
          return next(err);
        }

        if (foundItems) {
          // If items was found add them to the result.
          utils.extend(result, foundItems);
        }

        next();
      });
    }
    else {
      next();
    }
  }, function(err) {
    // Process all items from JSON files.
    Prana.Type.processAll(type, result, data);

    // Invoke type hooks on all modules.
    self.invoke(type.name, data, function(err, hookData) {
      if (err) {
        return callback(err);
      }

      if (hookData) {
        // Each hook implemention can return many items.
        for (var extensionName in hookData) {
          // Process each set of items.
          Prana.Type.processAll(type, hookData[extensionName], data);
        }
      }

      callback(err);
    });

  });
};
<|endoftext|>