<commit_before>define('payments', [], function() {
    'use strict';

    var currentPrice;
    var $regions = $('#region-list');
    var $regionsIsland = $('#regions');
    var $regionCheckboxes = $regions.find('input[type=checkbox]');

    var regionsData = $regions.data();

    var allPaidRegionIds = regionsData.allPaidRegionIds;
    var apiErrorMsg = regionsData.apiErrorMsg;
    var disabledRegions = regionsData.disabledRegions;
    var tierZeroId = regionsData.tierZeroId;
    var notApplicableMsg = regionsData.notApplicableMsg;
    var paymentMethods = regionsData.paymentMethods || {};
    var pricesApiEndpoint = regionsData.pricelistApiUrl + '{0}/';
    var $tdNodes = $('			');
    var $paidRegionTableTbody = $('#paid-regions tbody');

    function getOverlay(opts) {
        var id = opts;
        if (_.isObject(opts)) {
            id = opts.id;
        }
        $('.overlay').remove();
        z.body.addClass('overlayed');
        var overlay = makeOrGetOverlay(opts);
        overlay.html($('#' + id + '-template').html())
               .addClass('show')
               .on('click', '.close', _pd(function() {
                   overlay.trigger('overlay_dismissed').remove();
               }));
        return overlay;
    }

    function setupPaymentAccountOverlay($overlay, onsubmit) {
        $overlay.on('submit', 'form', _pd(function() {
            var $form = $(this);
            var $waiting_overlay = getOverlay('bango-waiting');
            var $old_overlay = $overlay.children('section');
            $old_overlay.detach();
            $form.find('.error').remove();

            $.post(
                $form.attr('action'), $form.serialize(),
                function(data) {
                    $waiting_overlay.trigger('dismiss');
                    onsubmit.apply($form, [data]);
                }
            ).error(function(error_data) {
                // If there's an error, revert to the form and reset the buttons.

                // We're recycling the variable $waiting_overlay to store
                // the old overlay. That gets cleaned up, though, when we
                // re-call setupBangoForm().
                $waiting_overlay.empty().append($old_overlay);

                try {
                    var parsed_errors = JSON.parse(error_data.responseText);
                    for (var field_error in parsed_errors) {
                        var field = $('#id_' + field_error);
                        $('
').addClass('error')
                                  .insertAfter(field)
                                  .text(parsed_errors[field_error]);
                    }
                    // If the error occurred on an unknown field,
                    // stick the error at the top. Maybe with more detail.
                    if (parsed_errors.__all__ !== null) {
                        var target = $old_overlay.find('#bango-account-errors');
                        $('
').addClass('error')
                                  .insertAfter(target)
                                  .text(parsed_errors.__all__);
                    }
                } catch(err) {
                    // There was a JSON parse error, just stick the error
                    // message on the form.
                    $old_overlay.find('#bango-account-errors')
                                .html(error_data.responseText);
                }

                // Re-initialize the form submit binding.
                setupPaymentAccountOverlay($waiting_overlay, onsubmit);
            });
        }));
    }

    function moveAnimate(element, newParent, $elmToRemove, zIndex) {
        zIndex = zIndex || 100;
        var $element = $(element);
        var $newParent = $(newParent);
        var oldOffset = $element.offset();

        $element.appendTo($newParent);
        var newOffset = $element.offset();
        var $temp = $element.clone().appendTo('body');
        $temp.css({'position': 'absolute',
                   'left': oldOffset.left,
                   'top': oldOffset.top,
                   'zIndex': zIndex});

        element.hide();
        $temp.animate({'top': parseInt(newOffset.top, 10), 'left': parseInt(newOffset.left, 10) }, 'slow', function(){
            $temp.remove();
            $element.show();
            if ($elmToRemove) {
                $elmToRemove.hide(500, function() { this.remove(); });
            }
        });
    }

    function createTableRow(checkBox, ident, localPriceText, localMethodText) {
        var $tds = $tdNodes.clone();
        var $tr = $paidRegionTableTbody.find('tr[data-region="' + ident + '"]');
        var $checkBoxContainer = $($tds[0]);
        var $localPriceContainer = $($tds[1]);
        var $localMethodContainer = $($tds[2]);
        $localMethodContainer.text(localMethodText);
        $localPriceContainer.text(localPriceText);
        $tr.append($tds);
        return $tr;
    }

    function disableCheckbox() {
        /*jshint validthis:true */
        var $this = $(this);

        $this.prop('disabled', true)
             .closest('label').addClass('disabled');

        // Remove the text, where it shouldn't be displayed.
        $this.closest('tr').find('.local-retail, .local-method').text('');
    }

    function updatePrices() {

        /*jshint validthis:true */
        var $this = $(this);
        var selectedPrice = $this.val();

        if (!selectedPrice) {
            return;
        }

        // If free with in-app is selected, check "Yes" then make the 'No' radio
        // disabled and hide it. Also hide upsell as that's not relevant.
        if (selectedPrice == 'free') {
            $('input[name=allow_inapp][value=True]').prop('checked', true);
            $('input[name=allow_inapp][value=False]').prop('disabled', true)
                                                     .parent('label').hide();
            $('#paid-upsell-island').hide();

            // For free apps we are using the same data for tier zero apps
            // to populate the region list.
            selectedPrice = tierZeroId;
        } else {
            $('#paid-upsell-island').show();
            $('input[name=allow_inapp][value=False]').prop('disabled', false)
                                                     .parent('label').show();
        }

        // From here on numbers should be used.
        selectedPrice = parseInt(selectedPrice, 10);

        // No-op if nothing else has changed.
        if (currentPrice === selectedPrice) {
            return;
        }

        $.ajax({
            url: format(pricesApiEndpoint, selectedPrice),
            beforeSend: function() {
                $regionsIsland.addClass('loading');
            },
            success: function(data) {
                var moveQueue = [];
                var prices = data.prices || [];
                var seen = [];
                var tierPrice = data.price;

                // Iterate over the prices for the regions
                for (var i=0, j=prices.length; i<j; i++) {
                    var price = prices[i];
                    var regionId = price.region;
                    var billingMethodText = paymentMethods[parseInt(price.method, 10)] || '';

                    var localPrice = price.price + ' ' + price.currency;
                    var localMethod = selectedPrice === tierZeroId ? notApplicableMsg : billingMethodText;

                    // If the checkbox we're interested is already in the table just update it.
                    // Otherwise we need to create a new tableRow and move it into position.
                    var $chkbox = $regions.find('input:checkbox[value=' + regionId + ']');
                    var $row = $('#paid-regions tr[data-region=' + regionId + ']');
                    if ($row.find('td').length) {
                        $row.find('.lp').text(localPrice);
                        $row.find('.lm').text(localMethod);
                    } else {
                        var $tr = createTableRow($chkbox.closest('label'), regionId, localPrice, localMethod);
                        moveQueue.push([$chkbox.closest('label'), $tr.find('.cb')]);
                        $chkbox.closest('li').hide(500);
                    }
                    seen.push($chkbox[0]);
                }

                for (var k=0, l=moveQueue.length; k<l; k++) {
                    var current = moveQueue[k];
                    moveAnimate(current[0], current[1]);
                }

                $('#paid-regions input[type=checkbox]').not(seen).each(function() {
                    // If the item we don't want here is in the table then we need to move it back
                    // out of the table and destroy the row contents.
                    var $chkbox = $(this);
                    var region = $chkbox.val();

                    // Lookup the location of the original checkbox so we know where to send this back to
                    var $newParent = $('.checkbox-choices li[data-region=' + region + ']');
                    $newParent.show(500);
                    var $label = $chkbox.closest('label');
                    if ($label.length) {
                        var $tds = $chkbox.closest('tr').find('td');
                        moveAnimate($label, $newParent, $tds);
                    }
                });
            },
            dataType: "json"
        }).fail(function() {
            z.doc.trigger('notify', { 'msg': apiErrorMsg });
        }).always(function() {
            $regionsIsland.removeClass('loading');
        });

        currentPrice = selectedPrice;
    }

    function init() {
        var $priceSelect = $('#id_price');
        $('#regions').trigger('editLoaded');

        $('.update-payment-type button').click(function() {
            $('input[name=toggle-paid]').val($(this).data('type'));
        });

        var $paid_island = $('#paid-island, #paid-upsell-island, #paid-regions-island');
        var $free_island = $('#regions-island');
        $('#submit-payment-type.hasappendix').on('tabs-changed', function(e, tab) {
            $paid_island.toggle(tab.id == 'paid-tab-header');
            $free_island.toggle(tab.id == 'free-tab-header');
        });

        // Only update if we can edit. If the user can't edit all fields will be disabled.
        if (!z.body.hasClass('no-edit')) {
            $priceSelect.on('change', updatePrices);
            updatePrices.call($priceSelect[0]);
        }
    }

    return {
        getOverlay: getOverlay,
        setupPaymentAccountOverlay: setupPaymentAccountOverlay,
        init: init
    };
});

if ($('.payments.devhub-form').length) {
    require('payments').init();
    require('payments-enroll').init();
    require('payments-manage').init();
}
<commit_msg>Don't move checkbox if no placeholder (bug 921593)<commit_after>define('payments', [], function() {
    'use strict';

    var currentPrice;
    var $regions = $('#region-list');
    var $regionsIsland = $('#regions');
    var $regionCheckboxes = $regions.find('input[type=checkbox]');

    var regionsData = $regions.data();

    var allPaidRegionIds = regionsData.allPaidRegionIds;
    var apiErrorMsg = regionsData.apiErrorMsg;
    var disabledRegions = regionsData.disabledRegions;
    var tierZeroId = regionsData.tierZeroId;
    var notApplicableMsg = regionsData.notApplicableMsg;
    var paymentMethods = regionsData.paymentMethods || {};
    var pricesApiEndpoint = regionsData.pricelistApiUrl + '{0}/';
    var $tdNodes = $('			');
    var $paidRegionTableTbody = $('#paid-regions tbody');

    function getOverlay(opts) {
        var id = opts;
        if (_.isObject(opts)) {
            id = opts.id;
        }
        $('.overlay').remove();
        z.body.addClass('overlayed');
        var overlay = makeOrGetOverlay(opts);
        overlay.html($('#' + id + '-template').html())
               .addClass('show')
               .on('click', '.close', _pd(function() {
                   overlay.trigger('overlay_dismissed').remove();
               }));
        return overlay;
    }

    function setupPaymentAccountOverlay($overlay, onsubmit) {
        $overlay.on('submit', 'form', _pd(function() {
            var $form = $(this);
            var $waiting_overlay = getOverlay('bango-waiting');
            var $old_overlay = $overlay.children('section');
            $old_overlay.detach();
            $form.find('.error').remove();

            $.post(
                $form.attr('action'), $form.serialize(),
                function(data) {
                    $waiting_overlay.trigger('dismiss');
                    onsubmit.apply($form, [data]);
                }
            ).error(function(error_data) {
                // If there's an error, revert to the form and reset the buttons.

                // We're recycling the variable $waiting_overlay to store
                // the old overlay. That gets cleaned up, though, when we
                // re-call setupBangoForm().
                $waiting_overlay.empty().append($old_overlay);

                try {
                    var parsed_errors = JSON.parse(error_data.responseText);
                    for (var field_error in parsed_errors) {
                        var field = $('#id_' + field_error);
                        $('
').addClass('error')
                                  .insertAfter(field)
                                  .text(parsed_errors[field_error]);
                    }
                    // If the error occurred on an unknown field,
                    // stick the error at the top. Maybe with more detail.
                    if (parsed_errors.__all__ !== null) {
                        var target = $old_overlay.find('#bango-account-errors');
                        $('
').addClass('error')
                                  .insertAfter(target)
                                  .text(parsed_errors.__all__);
                    }
                } catch(err) {
                    // There was a JSON parse error, just stick the error
                    // message on the form.
                    $old_overlay.find('#bango-account-errors')
                                .html(error_data.responseText);
                }

                // Re-initialize the form submit binding.
                setupPaymentAccountOverlay($waiting_overlay, onsubmit);
            });
        }));
    }

    function moveAnimate(element, newParent, $elmToRemove, zIndex) {
        zIndex = zIndex || 100;
        var $element = $(element);
        var $newParent = $(newParent);
        var oldOffset = $element.offset();

        $element.appendTo($newParent);
        var newOffset = $element.offset();
        var $temp = $element.clone().appendTo('body');
        $temp.css({'position': 'absolute',
                   'left': oldOffset.left,
                   'top': oldOffset.top,
                   'zIndex': zIndex});

        element.hide();
        $temp.animate({'top': parseInt(newOffset.top, 10), 'left': parseInt(newOffset.left, 10) }, 'slow', function(){
            $temp.remove();
            $element.show();
            if ($elmToRemove) {
                $elmToRemove.hide(500, function() { this.remove(); });
            }
        });
    }

    function createTableRow(checkBox, ident, localPriceText, localMethodText) {
        var $tds = $tdNodes.clone();
        var $tr = $paidRegionTableTbody.find('tr[data-region="' + ident + '"]');
        var $checkBoxContainer = $($tds[0]);
        var $localPriceContainer = $($tds[1]);
        var $localMethodContainer = $($tds[2]);
        $localMethodContainer.text(localMethodText);
        $localPriceContainer.text(localPriceText);
        $tr.append($tds);
        return $tr;
    }

    function disableCheckbox() {
        /*jshint validthis:true */
        var $this = $(this);

        $this.prop('disabled', true)
             .closest('label').addClass('disabled');

        // Remove the text, where it shouldn't be displayed.
        $this.closest('tr').find('.local-retail, .local-method').text('');
    }

    function updatePrices() {

        /*jshint validthis:true */
        var $this = $(this);
        var selectedPrice = $this.val();

        if (!selectedPrice) {
            return;
        }

        // If free with in-app is selected, check "Yes" then make the 'No' radio
        // disabled and hide it. Also hide upsell as that's not relevant.
        if (selectedPrice == 'free') {
            $('input[name=allow_inapp][value=True]').prop('checked', true);
            $('input[name=allow_inapp][value=False]').prop('disabled', true)
                                                     .parent('label').hide();
            $('#paid-upsell-island').hide();

            // For free apps we are using the same data for tier zero apps
            // to populate the region list.
            selectedPrice = tierZeroId;
        } else {
            $('#paid-upsell-island').show();
            $('input[name=allow_inapp][value=False]').prop('disabled', false)
                                                     .parent('label').show();
        }

        // From here on numbers should be used.
        selectedPrice = parseInt(selectedPrice, 10);

        // No-op if nothing else has changed.
        if (currentPrice === selectedPrice) {
            return;
        }

        $.ajax({
            url: format(pricesApiEndpoint, selectedPrice),
            beforeSend: function() {
                $regionsIsland.addClass('loading');
            },
            success: function(data) {
                var moveQueue = [];
                var prices = data.prices || [];
                var seen = [];
                var tierPrice = data.price;

                // Iterate over the prices for the regions
                for (var i=0, j=prices.length; i<j; i++) {
                    var price = prices[i];
                    var regionId = price.region;
                    var billingMethodText = paymentMethods[parseInt(price.method, 10)] || '';

                    var localPrice = price.price + ' ' + price.currency;
                    var localMethod = selectedPrice === tierZeroId ? notApplicableMsg : billingMethodText;

                    // If the checkbox we're interested is already in the table just update it.
                    // Otherwise we need to create a new tableRow and move it into position.
                    var $chkbox = $regions.find('input:checkbox[value=' + regionId + ']');
                    var $row = $('#paid-regions tr[data-region=' + regionId + ']');

                    if ($row.length) {
                        if ($row.find('td').length) {
                            $row.find('.lp').text(localPrice);
                            $row.find('.lm').text(localMethod);
                        } else {
                            var $tr = createTableRow($chkbox.closest('label'), regionId, localPrice, localMethod);
                            moveQueue.push([$chkbox.closest('label'), $tr.find('.cb')]);
                            $chkbox.closest('li').hide(500);
                        }
                        seen.push($chkbox[0]);
                    } else {
                        console.log('No row found with regionId "' + regionId + '" (noop)');
                    }
                }

                for (var k=0, l=moveQueue.length; k<l; k++) {
                    var current = moveQueue[k];
                    moveAnimate(current[0], current[1]);
                }

                $('#paid-regions input[type=checkbox]').not(seen).each(function() {
                    // If the item we don't want here is in the table then we need to move it back
                    // out of the table and destroy the row contents.
                    var $chkbox = $(this);
                    var region = $chkbox.val();

                    // Lookup the location of the original checkbox so we know where to send this back to
                    var $newParent = $('.checkbox-choices li[data-region=' + region + ']');
                    $newParent.show(500);
                    var $label = $chkbox.closest('label');
                    if ($label.length) {
                        var $tds = $chkbox.closest('tr').find('td');
                        moveAnimate($label, $newParent, $tds);
                    }
                });
            },
            dataType: "json"
        }).fail(function() {
            z.doc.trigger('notify', { 'msg': apiErrorMsg });
        }).always(function() {
            $regionsIsland.removeClass('loading');
        });

        currentPrice = selectedPrice;
    }

    function init() {
        var $priceSelect = $('#id_price');
        $('#regions').trigger('editLoaded');

        $('.update-payment-type button').click(function() {
            $('input[name=toggle-paid]').val($(this).data('type'));
        });

        var $paid_island = $('#paid-island, #paid-upsell-island, #paid-regions-island');
        var $free_island = $('#regions-island');
        $('#submit-payment-type.hasappendix').on('tabs-changed', function(e, tab) {
            $paid_island.toggle(tab.id == 'paid-tab-header');
            $free_island.toggle(tab.id == 'free-tab-header');
        });

        // Only update if we can edit. If the user can't edit all fields will be disabled.
        if (!z.body.hasClass('no-edit')) {
            $priceSelect.on('change', updatePrices);
            updatePrices.call($priceSelect[0]);
        }
    }

    return {
        getOverlay: getOverlay,
        setupPaymentAccountOverlay: setupPaymentAccountOverlay,
        init: init
    };
});

if ($('.payments.devhub-form').length) {
    require('payments').init();
    require('payments-enroll').init();
    require('payments-manage').init();
}
<|endoftext|>


<commit_before>/**
 *
 *  CS3283/4 storage.js
 *  This class acts as a facade for other developers to access to the database.
 *  The implementation is a non-SQL local storage to support the WebApp.
 *
 */

import constants from 'src/database/constants';
import entitiesCreator from 'src/database/create/entitiesCreator';
import query from 'src/database/retrieve/query';
import dataModifier from 'src/database/data-modifier/dataModifier';

let databaseName = constants.INDEXEDDB_NAME;

function createUserTable(isUserTableFirstInitialize) {
  return new Promise((resolve, reject) => {
    if (!isUserTableFirstInitialize) {
      entitiesCreator.createUserTable()
      .then(success => resolve(true))
      .catch(sqlError => reject(sqlError));
    } else {
      resolve('false');
    }
  });
}

function createOrganizationTable(isOrganizationTableFirstInitialize) {
  return new Promise((resolve, reject) => {
    if (!isOrganizationTableFirstInitialize) {
      entitiesCreator.createOrganizationTable()
      .then(success => resolve(true))
      .catch(sqlError => reject(sqlError));
    } else {
      resolve('false');
    }
  });
}

function createFolderTable(isFolderTableFirstInitialize) {
  return new Promise((resolve, reject) => {
    if (!isFolderTableFirstInitialize) {
      entitiesCreator.createFolderTable()
      .then(success => resolve(true))
      .catch(sqlError => reject(sqlError));
    } else {
      resolve('false');
    }
  });
}

function createFileManagerTable(isFileTableFirstInitialize) {
  return new Promise((resolve, reject) => {
    if (!isFileTableFirstInitialize) {
      entitiesCreator.createFileManagerTable()
      .then(success => resolve(true))
      .catch(sqlError => reject(sqlError));
    } else {
      resolve('false');
    }
  });
}

function creationOfTables() {
  return new Promise((resolve, reject) => {
    query.isTableExistsInDatabase(constants.ENTITIES_USER)
    .then(isUserTableFirstInitialize =>
      createUserTable(isUserTableFirstInitialize)
      .then(isSuccess => isSuccess)
      .catch(sqlError => reject(sqlError))
    )
    .then(() =>
      query.isTableExistsInDatabase(constants.ENTITIES_ORGANIZATION)
      .then(isOrganizationTableFirstInitialize =>
        createOrganizationTable(isOrganizationTableFirstInitialize))
        .then(isSuccess => isSuccess)
        .catch(sqlError => reject(sqlError))
      .catch(sqlError => reject(sqlError))
    )
    .then(() =>
      query.isTableExistsInDatabase(constants.ENTITIES_FOLDER)
      .then(isFolderTableFirstInitialize =>
        createFolderTable(isFolderTableFirstInitialize))
        .then(isSuccess => isSuccess)
        .catch(sqlError => reject(sqlError))
      .catch(sqlError => reject(sqlError))
    )
    .then(() =>
      query.isTableExistsInDatabase(constants.ENTITIES_FILE_MANAGER)
      .then(isFileTableFirstInitialize =>
        createFileManagerTable(isFileTableFirstInitialize)
        .then(isSuccess => isSuccess)
        .catch(sqlError => reject(sqlError))
      )
      .catch(sqlError => reject(sqlError))
    )
    .then(isSuccess => resolve(isSuccess))
    .catch(sqlError => reject(sqlError));
  });
}

export default class storage {
  static constructor() {}

  static initializeDatabase(newDatabaseName) {
    return new Promise((resolve, reject) => {
      databaseName = newDatabaseName || databaseName;
      return entitiesCreator.initializeDatabase(databaseName)
      .then(step1 =>
        creationOfTables()
        .then(isFirstInstance => isFirstInstance)
        .catch(sqlErr => reject(sqlErr)))
      /*
       * if table is first created, extra-checking that the data is boolean
       * it will fill up the table,
       * otherwise no data will be filled.
       */
      .then((isFirstInstance) => {
        if (typeof isFirstInstance === 'boolean') {
          return entitiesCreator.fillUpDefaultData()
          .then(isFilledUp => resolve(isFilledUp))
          .catch(sqlErr => reject(sqlErr));
        } else {
          return resolve(true);
        }
      })
      .catch(sqlError => reject(sqlError));
    });
  }

  static loadAllFilesAndFolders() {
    return query.getAllFilesAndFolders();
  }

  // Searching the filename and foldername ONLY
  static partialSearch(searchString) {
    return query.searchString(searchString);
  }

  static getList(folderId) {
    return query.loadFolder(folderId);
  }

  static loadFile(fileId) {
    return query.loadFile(fileId);
  }

  static saveFile(fileId, fileString) {
    return dataModifier.saveFile(fileId, fileString);
  }

  static createFile(organizationId, filePath, folderId) {
    return dataModifier.createNewFile(organizationId, filePath, folderId);
  }

  static moveFile(fileId, newPath) {
    return dataModifier.moveFile(fileId, newPath);
  }

  static copyFile(fileId) {
    return dataModifier.copyFile(fileId);
  }

  static renameFile(fileId, newFileName) {
    return dataModifier.renameFileName(fileId, newFileName);
  }

  static renameFolder(folderId, newFolderName) {
    return dataModifier.renameFolderName(folderId, newFolderName);
  }

  static deleteFile(fileId) {
    return dataModifier.deleteFile(fileId);
  }

  static createFolder(organizationId, folderPath, currentFolderId) {
    return dataModifier.createNewFolder(organizationId, folderPath, currentFolderId);
  }

  // only delete folder for now without cascade delete
  static deleteFolder(folderId) {
    return dataModifier.deleteFolder(folderId);
  }

  static deleteAll(newDatabaseName) {
    databaseName = newDatabaseName || databaseName;
    return dataModifier.deleteAllEntities(databaseName);
  }

  static exceptions;

}
<commit_msg>Update comments to storage.js and remove deleteAll function<commit_after>/**
 *
 *  CS3283/4 storage.js
 *  This class acts as a facade for other developers to access to the database.
 *  The implementation is a non-SQL local storage to support the WebApp.
 *
 */

import constants from 'src/database/constants';
import entitiesCreator from 'src/database/create/entitiesCreator';
import query from 'src/database/retrieve/query';
import dataModifier from 'src/database/data-modifier/dataModifier';

let databaseName = constants.INDEXEDDB_NAME;

function createUserTable(isUserTableFirstInitialize) {
  return new Promise((resolve, reject) => {
    if (!isUserTableFirstInitialize) {
      entitiesCreator.createUserTable()
      .then(success => resolve(true))
      .catch(sqlError => reject(sqlError));
    } else {
      resolve('false');
    }
  });
}

function createOrganizationTable(isOrganizationTableFirstInitialize) {
  return new Promise((resolve, reject) => {
    if (!isOrganizationTableFirstInitialize) {
      entitiesCreator.createOrganizationTable()
      .then(success => resolve(true))
      .catch(sqlError => reject(sqlError));
    } else {
      resolve('false');
    }
  });
}

function createFolderTable(isFolderTableFirstInitialize) {
  return new Promise((resolve, reject) => {
    if (!isFolderTableFirstInitialize) {
      entitiesCreator.createFolderTable()
      .then(success => resolve(true))
      .catch(sqlError => reject(sqlError));
    } else {
      resolve('false');
    }
  });
}

function createFileManagerTable(isFileTableFirstInitialize) {
  return new Promise((resolve, reject) => {
    if (!isFileTableFirstInitialize) {
      entitiesCreator.createFileManagerTable()
      .then(success => resolve(true))
      .catch(sqlError => reject(sqlError));
    } else {
      resolve('false');
    }
  });
}

function creationOfTables() {
  return new Promise((resolve, reject) => {
    query.isTableExistsInDatabase(constants.ENTITIES_USER)
    .then(isUserTableFirstInitialize =>
      createUserTable(isUserTableFirstInitialize)
      .then(isSuccess => isSuccess)
      .catch(sqlError => reject(sqlError))
    )
    .then(() =>
      query.isTableExistsInDatabase(constants.ENTITIES_ORGANIZATION)
      .then(isOrganizationTableFirstInitialize =>
        createOrganizationTable(isOrganizationTableFirstInitialize))
        .then(isSuccess => isSuccess)
        .catch(sqlError => reject(sqlError))
      .catch(sqlError => reject(sqlError))
    )
    .then(() =>
      query.isTableExistsInDatabase(constants.ENTITIES_FOLDER)
      .then(isFolderTableFirstInitialize =>
        createFolderTable(isFolderTableFirstInitialize))
        .then(isSuccess => isSuccess)
        .catch(sqlError => reject(sqlError))
      .catch(sqlError => reject(sqlError))
    )
    .then(() =>
      query.isTableExistsInDatabase(constants.ENTITIES_FILE_MANAGER)
      .then(isFileTableFirstInitialize =>
        createFileManagerTable(isFileTableFirstInitialize)
        .then(isSuccess => isSuccess)
        .catch(sqlError => reject(sqlError))
      )
      .catch(sqlError => reject(sqlError))
    )
    .then(isSuccess => resolve(isSuccess))
    .catch(sqlError => reject(sqlError));
  });
}

export default class storage {
  static constructor() {}

  /**
   * Initialize indexedDB with default database name
   *
   * @return {Promise}
  */
  static initializeDatabase(newDatabaseName) {
    return new Promise((resolve, reject) => {
      databaseName = newDatabaseName || databaseName;
      return entitiesCreator.initializeDatabase(databaseName)
      .then(step1 =>
        creationOfTables()
        .then(isFirstInstance => isFirstInstance)
        .catch(sqlErr => reject(sqlErr)))
      /*
       * if table is first created, extra-checking that the data is boolean
       * it will fill up the table,
       * otherwise no data will be filled.
       */
      .then((isFirstInstance) => {
        if (typeof isFirstInstance === 'boolean') {
          return entitiesCreator.fillUpDefaultData()
          .then(isFilledUp => resolve(isFilledUp))
          .catch(sqlErr => reject(sqlErr));
        } else {
          return resolve(true);
        }
      })
      .catch(sqlError => reject(sqlError));
    });
  }

  /**
   * retrieve all files and folders in database
   *
   * @return {Promise}
   * @return [[files],[folders]]
  */
  static loadAllFilesAndFolders() {
    return query.getAllFilesAndFolders();
  }

  /**
   * searching files and folders with partial phrase
   *
   * @param string searchString
   * @return {Promise}
   * @return [[files],[folders]]
  */
  static partialSearch(searchString) {
    return query.searchString(searchString);
  }

  /**
   * get all files and folders inside this folder
   *
   * @param number folderId
   * @return {Promise}
   * @return [[files],[folders]]
  */
  static getList(folderId) {
    return query.loadFolder(folderId);
  }

  /**
   * retrieve md string in file
   *
   * @param number fileId
   * @return {Promise}
   * @return string
  */
  static loadFile(fileId) {
    return query.loadFile(fileId);
  }

  /**
   * retrieve md string in file
   *
   * @param number fileId, string fileString
   * @return {Promise}
  */
  static saveFile(fileId, fileString) {
    return dataModifier.saveFile(fileId, fileString);
  }

  /**
   * create a new file
   *
   * @param number organizationId, string filePath, number folderId
   * @return {Promise}
  */
  static createFile(organizationId, filePath, folderId) {
    return dataModifier.createNewFile(organizationId, filePath, folderId);
  }

  /**
   * relocate a file to a new location
   *
   * @param number fileId, string newPath
   * @return {Promise}
  */
  static moveFile(fileId, newPath) {
    return dataModifier.moveFile(fileId, newPath);
  }

  /**
   * duplicate a file
   *
   * @param number fileId
   * @return {Promise}
  */
  static copyFile(fileId) {
    return dataModifier.copyFile(fileId);
  }

  /**
   * rename a file
   *
   * @param number fileId, string newFileName
   * @return {Promise}
  */
  static renameFile(fileId, newFileName) {
    return dataModifier.renameFileName(fileId, newFileName);
  }

  /**
   * rename a folder and all its children
   *
   * @param number folderId, string newFolderName
   * @return {Promise}
  */
  static renameFolder(folderId, newFolderName) {
    return dataModifier.renameFolderName(folderId, newFolderName);
  }

  /**
   * removes a file from database
   *
   * @param number fileId
   * @return {Promise}
  */
  static deleteFile(fileId) {
    return dataModifier.deleteFile(fileId);
  }

  /**
   * creates a new folder in database
   *
   * @param number organizationId, string folderPath, number currentFolderId
   * @return {Promise}
  */
  static createFolder(organizationId, folderPath, currentFolderId) {
    return dataModifier.createNewFolder(organizationId, folderPath, currentFolderId);
  }

  /**
   * removes a folder and cascade delete
   *
   * @param number folderId
   * @return {Promise}
  */
  static deleteFolder(folderId) {
    return dataModifier.deleteFolder(folderId);
  }

  /**
   * exceptions to check if called
   * @return exceptions
  */
  static exceptions;

}
<|endoftext|>


<commit_before>/**
 * Created by Thomas on 09.07.2015.
 */
var config = require('./../../config.js');

var events = require('events');

var mqtt = require('mqtt');
GLOBAL.mqttClient = mqtt.connect(config.MQTTHost);
mqttClient.on('connect', function(){
  mqttClient.publish('mi5/module/1101/busy','ready')
  console.log('Connected to MQTT: ',config.MQTTHost);

  fakeLevel('mi5/module/2101/value/orange/level');
  fakeLevel('mi5/module/2101/value/passion-fruit/level');
  fakeLevel('mi5/module/2101/value/grenadine/level');
  fakeLevel('mi5/module/2101/value/blue-curazao/level');
  fakeLevel('mi5/module/2101/value/water/level');

  fakePosition('mi5/module/1101/mover/1/position');
  fakePosition('mi5/module/1101/mover/2/position');
  fakePosition('mi5/module/1101/mover/3/position');

  function fakeLevel(topic){
    var level = 100 - Math.floor((Math.random() * 10) + 1)*10; //90 and 1
    setInterval(function(){
      level = level - Math.random()*2; //between 0 and 2;
      if(level <= 0){
        level = 100;
      }
      level = JSON.stringify(level); // needed for mqtt
      console.log('publish',topic, level);
      mqttClient.publish(topic, level);
    },1000);
  }

  function fakePosition(topic){
    var level = 1500 - Math.floor((Math.random() * 100) + 1); //1499 and 1399
    setInterval(function(){
      level = level + Math.random()*4; //between 0 and 2;
      if(level <= 0){
        level = 1500;
      }
      level = JSON.stringify(level); // needed for mqtt
      console.log('publish',topic, level);
      mqttClient.publish(topic, level);
    },500);
  }
});
<commit_msg>Bugfix JSON.stringify<commit_after>/**
 * Created by Thomas on 09.07.2015.
 */
var config = require('./../../config.js');

var events = require('events');

var mqtt = require('mqtt');
GLOBAL.mqttClient = mqtt.connect(config.MQTTHost);
mqttClient.on('connect', function(){
  mqttClient.publish('mi5/module/1101/busy','ready')
  console.log('Connected to MQTT: ',config.MQTTHost);

  fakeLevel('mi5/module/2101/value/orange/level');
  fakeLevel('mi5/module/2101/value/passion-fruit/level');
  fakeLevel('mi5/module/2101/value/grenadine/level');
  fakeLevel('mi5/module/2101/value/blue-curazao/level');
  fakeLevel('mi5/module/2101/value/water/level');

  fakePosition('mi5/module/1101/mover/1/position');
  fakePosition('mi5/module/1101/mover/2/position');
  fakePosition('mi5/module/1101/mover/3/position');

  function fakeLevel(topic){
    var level = 100 - Math.floor((Math.random() * 10) + 1)*10; //90 and 1
    setInterval(function(){
      level = level - Math.random()*2; //between 0 and 2;
      if(level <= 0){
        level = 100;
      }

      console.log('publish',topic, level);
      mqttClient.publish(topic, JSON.stringify(level));
    },1000);
  }

  function fakePosition(topic){
    var level = 1500 - Math.floor((Math.random() * 100) + 1); //1499 and 1399
    setInterval(function(){
      level = level + Math.random()*4; //between 0 and 2;
      if(level <= 0){
        level = 1500;
      }

      console.log('publish',topic, level);
      mqttClient.publish(topic, JSON.stringify(level));
    },500);
  }
});
<|endoftext|>


<commit_before>const fs = require('fs')
const path = require('path')
const http = require('http')
const jsdom = require('jsdom')

const urls = [
  'http://www.conwaylife.com/wiki/Category:Agars',
  'http://www.conwaylife.com/wiki/Category:Gardens_of_Eden',
  'http://www.conwaylife.com/wiki/Category:Guns',
  'http://www.conwaylife.com/wiki/Category:Methuselahs',
  'http://www.conwaylife.com/wiki/Category:Oscillators',
  'http://www.conwaylife.com/wiki/Category:Puffers',
  'http://www.conwaylife.com/wiki/Category:Sawtooths',
  'http://www.conwaylife.com/wiki/Category:Spaceships',
  'http://www.conwaylife.com/wiki/Category:Still_lifes',
  'http://www.conwaylife.com/wiki/Category:Wicks'
]

urls.forEach(url => {
  const fileName = url.split(':').reverse()[0].toLowerCase()
  const targetFile = fs.createWriteStream(path.join(__dirname, `/data/${ fileName }.js`))

  targetFile.on('open', () => {
    jsdom.env({
      url: url,
      done: (err, window) => {
        const categoryName = window.document.querySelector('h1').textContent.replace('Category:', '')
        targetFile.write(`'${ categoryName }': {\n`)
        console.log(`Gathering data from category ${ categoryName }`)

        const contentWrapper = window.document.querySelector('.mw-content-ltr')
        const links = [...contentWrapper.querySelectorAll('a')]

        links.forEach(link => {
          jsdom.env({
            url: link,
            done: (err, window) => {

              const linkToData = window.document.querySelector(
                '.infobox .infobox_table a[href$=".cells"]'
              )

              if (!linkToData) return
              const url = linkToData.href

              http.get(url, res => {
                if (res.statusCode !== 200) return
                var data = ''

                res.setEncoding('utf8')
                res.on('data', chunk => {
                  data += chunk
                })
                res.on('end', () => {
                  try {
                    var patternName = data.match(/^!Name: (.*)$/m)[1].trim()

                  } catch(e) {
                    console.log(data)
                  }
                  console.log(`Downloaded ${ categoryName } - ${ patternName }`)

                  const cleanData = data
                    .replace(/^!.*$/gm, '')
                    .split('\n')
                    .map(line => line.split('').join(' '))
                    .map(line => line.split('').map(char => char === 'O' ? '■' : char).join(''))
                    .map(line => line.split('').map(char => char === '.' ? ' ' : char).join(''))
                    .map(line => `    ${ line }`)

                  const firstNonEmptyLine = (lines) => {
                    var currentIndex = 0
                    for (var i = 0; i < lines.length; i++) {
                      if (lines[i].trim() !== '') return currentIndex
                      currentIndex++
                    }
                    return currentIndex
                  }

                  const startIndex = firstNonEmptyLine(cleanData)
                  const endIndex = firstNonEmptyLine([...cleanData].reverse())

                  const finalData = cleanData
                    .slice(startIndex, cleanData.length - endIndex)
                    .join('')

                  targetFile.write(`  '${ patternName }': \`\n`)
                  targetFile.write(`${ finalData }\n`)
                  targetFile.write(`  \`,\n`)
                })
              })
            }
          })
        })
      }
    })
  })
})
<commit_msg>add loggig and maxium patterns size to download script<commit_after>const fs = require('fs')
const path = require('path')
const http = require('http')
const jsdom = require('jsdom')

const urls = [
  'http://www.conwaylife.com/wiki/Category:Agars',
  'http://www.conwaylife.com/wiki/Category:Gardens_of_Eden',
  'http://www.conwaylife.com/wiki/Category:Guns',
  'http://www.conwaylife.com/wiki/Category:Methuselahs',
  'http://www.conwaylife.com/wiki/Category:Oscillators',
  'http://www.conwaylife.com/wiki/Category:Puffers',
  'http://www.conwaylife.com/wiki/Category:Sawtooths',
  'http://www.conwaylife.com/wiki/Category:Spaceships',
  'http://www.conwaylife.com/wiki/Category:Still_lifes',
  'http://www.conwaylife.com/wiki/Category:Wicks'
]

urls.forEach(url => {
  const fileName = url.split(':').reverse()[0].toLowerCase()
  const targetFile = fs.createWriteStream(path.join(__dirname, `/data/${ fileName }.js`))

  // category page
  targetFile.on('open', () => {
    jsdom.env({
      url: url,
      done: (err, window) => {
        const categoryName = window.document.querySelector('h1').textContent.replace('Category:', '')
        targetFile.write(`'${ categoryName }': {\n`)
        console.log(`Gathering data from category ${ categoryName }`)

        const links = [...window.document.querySelectorAll('#mw-pages .mw-content-ltr a')]
        const urls = links.map(link => link.href)

        // pattern page
        urls.forEach(url => {
          const currentUrl = url

          jsdom.env({
            url: url,
            done: (err, window) => {
              const patternSizeNode = window.document
                .querySelector('.infobox a[href^="/wiki/Bounding_box"]')

              if (patternSizeNode) {
                const patternSize = patternSizeNode
                  .parentNode
                  .nextElementSibling
                  .textContent
                  .trim()
                  .split('×')
                  .map(Number)

                if (patternSizeNode[0] > 50 || patternSizeNode[1] > 50) {
                  console.log(`pattern too big ${ categoryName } - ${ currentUrl }`)
                  rerturn
                }
              }

              const linkToData = window.document.querySelector(
                '.infobox a[href$=".cells"]'
              )

              if (!linkToData) {
                console.log(`missing link to data file ${ categoryName } - ${ currentUrl }`)
                return
              }
              const url = linkToData.href

              // pattern file
              http.get(url, res => {
                if (res.statusCode !== 200) {
                  console.log(`cant download data file ${ categoryName } - ${ url }`)
                  return
                }
                var data = ''

                res.setEncoding('utf8')
                res.on('data', chunk => {
                  data += chunk
                })
                res.on('end', () => {
                  var patternName = data.match(/^!Name: (.*)$/m)[1].trim()
                  console.log(`downloaded ${ categoryName } - ${ patternName }`)

                  const cleanData = data
                    .replace(/^!.*$/gm, '')
                    .split('\n')
                    .map(line => line.split('').join(' '))
                    .map(line => line.split('').map(char => char === 'O' ? '■' : char).join(''))
                    .map(line => line.split('').map(char => char === '.' ? ' ' : char).join(''))
                    .map(line => `    ${ line }`)

                  const firstNonEmptyLine = (lines) => {
                    var currentIndex = 0
                    for (var i = 0; i < lines.length; i++) {
                      if (lines[i].trim() !== '') return currentIndex
                      currentIndex++
                    }
                    return currentIndex
                  }

                  const startIndex = firstNonEmptyLine(cleanData)
                  const endIndex = firstNonEmptyLine([...cleanData].reverse())

                  const finalData = cleanData
                    .slice(startIndex, cleanData.length - endIndex)
                    .join('')

                  targetFile.write(`  '${ patternName }': \`\n`)
                  targetFile.write(`${ finalData }\n`)
                  targetFile.write(`  \`,\n`)
                })
              })
            }
          })
        })
      }
    })
  })
})
<|endoftext|>


<commit_before>JanusFireboxParser = function() {
}

JanusFireboxParser.prototype.parse = function(source, baseurl, datapath) {
  var xml = this.parseXML(source, false, true);
  var rooms = this.getAsArray(this.arrayget(xml, 'fireboxroom._children.room', {}));
  var room = {_children: {}};
  for (var i = 0; i < rooms.length; i++) {
    var attrs = Object.keys(rooms[i]).filter(function(k) { return (k[0] != '_'); });
    attrs.forEach(function(k) {
      room[k] = rooms[i][k];
    });
    if (rooms[i]._children) {
      Object.keys(rooms[i]._children).forEach(function(k) {
        room._children[k] = rooms[i]._children[k];
      });
    }
  }
  var roomdata = this.getRoomData(xml, room, baseurl, datapath);
  roomdata.source = source;
  return roomdata;
}
JanusFireboxParser.prototype.getRoomData = function(xml, room, baseurl, datapath) {
  var assets = this.parseAssets(xml, baseurl, datapath);

  var parseNode = this.parseNode.bind(this);

  var roomdata = {
    assets: assets,
    room: this.parseNode(room)
  };
  for (var k in room._children) {
    var objects = this.getAsArray(room._children[k]);
    roomdata[k] = objects.map(parseNode);
  }

  // TODO - some rooms have orphan objects, outside of the <Room> tag.
  // We should parse these too, but I don't have any examples to test with
/*
  var orphantypes = ['link', 'image', 'image3d', 'video', 'sound', 'text', 'paragraph', 'light', 'particle', 'ghost'];
  for (var i = 0; i < orphantypes.length; i++) {
    var type = orphantypes[i],
        orphans = this.getAsArray(this.arrayget(xml, 'fireboxroom._children.' + type));
    if (orphans && orphans.length > 0) {
      if (!roomdata[type]) roomdata[type] = [];
      roomdata[type].push.apply(roomdata[type], orphans);
    }
  };
*/
  return roomdata;
}
JanusFireboxParser.prototype.getAsArray = function(arr) {
  return (arr instanceof Array ? arr : [arr]);
}
JanusFireboxParser.prototype.parseAssets = function(xml, baseurl, datapath) {
  var assetxml = this.arrayget(xml, 'fireboxroom._children.assets.0', {});
  var objectassets = this.getAsArray(this.arrayget(assetxml, "_children.assetobject", []));
  var soundassets = this.getAsArray(this.arrayget(assetxml, "_children.assetsound", []));
  var imageassets = this.getAsArray(this.arrayget(assetxml, "_children.assetimage", []));
  var videoassets = this.getAsArray(this.arrayget(assetxml, "_children.assetvideo", []));
  var scriptassets = this.getAsArray(this.arrayget(assetxml, "_children.assetscript", []));
  var ghostassets = this.getAsArray(this.arrayget(assetxml, "_children.assetghost", []));
  var websurfaceassets = this.getAsArray(this.arrayget(assetxml, "_children.assetwebsurface", []));
  var assetlist = [];
  if (!datapath) {
    datapath = 'http://web.janusvr.com/media';
  }
  var fixURLEncoding = this.fixURLEncoding.bind(this);
  imageassets.forEach(function(n) {
    var src = fixURLEncoding(n.src.match(/^file:/) ? n.src.replace(/^file:/, datapath) : n.src);
    assetlist.push({
      assettype: 'image',
      name: n.id,
      src: src,
      baseurl: baseurl,
      hasalpha: n.hasalpha,
      proxy: n.proxy,
      sbs3d: n.sbs3d,
      ou3d: n.ou3d,
      reverse3d: n.reverse3d,
    });
  });
  videoassets.forEach(function(n) {
    var src = fixURLEncoding(n.src.match(/^file:/) ? n.src.replace(/^file:/, datapath) : n.src);
    assetlist.push({
      assettype:'video',
      name:n.id,
      src: src,
      loop: n.loop,
      sbs3d: n.sbs3d == 'true',
      ou3d: n.ou3d == 'true',
      auto_play: n.auto_play == 'true',
      baseurl: baseurl,
      proxy: n.proxy && n.proxy != 'false'
    });
  });
  soundassets.forEach(function(n) {
    var src = fixURLEncoding(n.src.match(/^file:/) ? n.src.replace(/^file:/, datapath) : n.src);
    assetlist.push({
      assettype:'sound',
      name:n.id,
      src: src,
      baseurl: baseurl
    });
  });
  var websurfaces = {};
  websurfaceassets.forEach(function(n) { websurfaces[n.id] = n; });
  scriptassets.forEach(function(n) {
    var src = fixURLEncoding(n.src.match(/^file:/) ? n.src.replace(/^file:/, datapath) : n.src);
    assetlist.push({
      assettype:'script',
      name: src,
      src: src,
      baseurl: baseurl
    });
  });

  var objlist = [];
  objectassets.forEach(function(n) {
    if (n.src) {
      var src = fixURLEncoding(n.src.match(/^file:/) ? n.src.replace(/^file:/, datapath) : n.src);
      var mtlsrc = (n.mtl && n.mtl.match(/^file:/) ? n.mtl.replace(/^file:/, datapath) : n.mtl);
      if (mtlsrc && !mtlsrc.match(/^(https?:)?\/\//)) mtlsrc = baseurl + mtlsrc;
      var srcparts = src.split(' ');
      src = srcparts[0];
      assetlist.push({assettype: 'model', name: n.id, src: src, mtl: mtlsrc, tex_linear: n.tex_linear, tex0: n.tex || n.tex0 || srcparts[1], tex1: n.tex1 || srcparts[2], tex2: n.tex2 || srcparts[3], tex3: n.tex3 || srcparts[4]});
    }
  });
  var assets = {
    object: objectassets,
    image: imageassets,
    sound: soundassets,
    video: videoassets,
    websurfaces: websurfaces,
    scripts: scriptassets,
    ghosts: ghostassets,
    assetlist: assetlist
  };
  return assets;
}

JanusFireboxParser.prototype.getVectorValue = function(vector, defaultvalue) {
  if (typeof defaultvalue == 'undefined') {
    defaultvalue = null;//[0,0,0];
  }
  if (typeof vector == 'string') {
    return vector.trim().split(' ').map(parseFloat);
  } else if (vector instanceof THREE.Vector3) {
    return vector.toArray();
  } else if (typeof vector == 'undefined') {
    return defaultvalue;
  }
  return vector;
}
JanusFireboxParser.prototype.parseNode = function(n) {
  var nodeinfo = {};
  var attrs = Object.keys(n);
  attrs.forEach(function(k) {
    nodeinfo[k] = (n[k] == 'false' ? false : n[k]);
  });

  nodeinfo.pos = this.getVectorValue(n.pos);
  nodeinfo.scale = this.getVectorValue(n.scale, [1,1,1]);
  nodeinfo.orientation = this.getOrientation(n.xdir, n.ydir || n.up, n.zdir || n.fwd);
  nodeinfo.col = (n.col ? (n.col[0] == '#' ? [parseInt(n.col.substr(1,2), 16)/255, parseInt(n.col.substr(3, 2), 16)/255, parseInt(n.col.substr(5, 2), 16)/255] : n.col) : null);

  var minscale = 1e-20;
/*
  nodeinfo.scale[0] = Math.max(minscale, nodeinfo.scale[0]);
  nodeinfo.scale[1] = Math.max(minscale, nodeinfo.scale[1]);
  nodeinfo.scale[2] = Math.max(minscale, nodeinfo.scale[2]);
*/
  if (nodeinfo.scale[0] < minscale && nodeinfo.scale[0] > -minscale) nodeinfo.scale[0] = minscale;
  if (nodeinfo.scale[1] < minscale && nodeinfo.scale[1] > -minscale) nodeinfo.scale[1] = minscale;
  if (nodeinfo.scale[2] < minscale && nodeinfo.scale[2] > -minscale) nodeinfo.scale[2] = minscale;

  return nodeinfo;
}

JanusFireboxParser.prototype.getOrientation = function(xdir, ydir, zdir) {
  if (xdir) {
    xdir = (xdir instanceof THREE.Vector3 ? xdir : new THREE.Vector3().fromArray(xdir.split(' '))).normalize();
  } else {
    xdir = new THREE.Vector3(1,0,0);
  }
  if (ydir) {
    ydir = (ydir instanceof THREE.Vector3 ? ydir : new THREE.Vector3().fromArray(ydir.split(' '))).normalize();
  } else {
    ydir = new THREE.Vector3(0,1,0);
  }
  if (zdir) {
    zdir = (zdir instanceof THREE.Vector3 ? zdir : new THREE.Vector3().fromArray(zdir.split(' '))).normalize();
  } else {
    zdir = new THREE.Vector3(0,0,1);
  }

  var newydir = ydir.clone().sub(zdir).multiplyScalar(ydir.dot(zdir)).normalize();
  xdir.crossVectors(ydir, zdir).normalize();

  var mat4 = new THREE.Matrix4().makeBasis(xdir, ydir, zdir);
  var quat = new THREE.Quaternion();
  var pos = new THREE.Vector3();
  var scale = new THREE.Vector3();
  //quat.setFromRotationMatrix(mat4);
  mat4.decompose(pos, quat, scale);
  quat.normalize();
  //quat.multiply(new THREE.Quaternion().setFromEuler(new THREE.Euler(0, Math.PI, 0)));
  return quat;
}
JanusFireboxParser.prototype.parseXML = function(imgxml, leaf, forceLower) {
  var node, root, parent, xmldoc;
  if (imgxml.nodeName) {
    xmldoc = node = imgxml;
  } else {
    try {
      if (window.DOMParser) {
        var parser = new DOMParser();
        xmldoc = parser.parseFromString(imgxml,"application/xml");
        node = xmldoc.firstChild;
      } else {
        node = new ActiveXObject("Microsoft.XMLDOM");
        node.async = "false";
        node.loadXML(imgxml).firstChild;
      }

      // Chrome doesn't throw an exception for malformed XML, so we look for a <parsererror> xml tag
      var parsererrors = node.getElementsByTagName("parsererror");
      if (parsererrors.length > 0) {
        // Extract the message from the first div child of the <parsererror> element
        var errorel = parsererrors[0].getElementsByTagName('div')[0] || parsererrors[0];
        throw new JanusFireboxParserException(errorel.innerHTML);
      }
    } catch (e) {
      throw new JanusFireboxParserException(e.message);
    }
  }
  root = {};
  if (!leaf) {
    var rootname = node.tagName;
    if (forceLower) rootname = rootname.toLowerCase();
    if (!root[rootname]) root[rootname] = {};
    parent = root[rootname];
    //node = parent[node.tagName];
  } else {
    parent = root;
  }
  if (node.attributes) {
    for (var i = 0; i < node.attributes.length; i++) {
      var name = node.attributes[i].nodeName;
      if (forceLower) name = name.toLowerCase();
      var value = node.attributes[i].nodeValue;
      parent[name] = value;
    }
  }
  if (node.childNodes) {
    for (var j = 0; j < node.childNodes.length; j++) {
      var child = node.childNodes[j];
      var nodename = child.nodeName;
      if (forceLower) nodename = nodename.toLowerCase();
      if (!parent._children) parent._children = {};
      if (!parent._children[nodename]) {
        parent._children[nodename] = [];
      } else if (parent._children[nodename].constructor !== Array) {
        parent._children[nodename] = [parent._children[nodename]];
      }
      if (node.getElementsByTagName(child.tagName).length > 0) {
        parent._children[nodename].push(this.parseXML(child, true, forceLower));
      } else if (child.nodeName) {
        if (child.nodeName == "#text" || child.nodeName == "#cdata-section") {
          // this gets confused if you have multiple text/cdata nodes...
          if (!child.nodeValue.match(/^[\s\n]*$/m)) {
            parent._content = child.nodeValue;
          }
        } else {
          parent._children[nodename] = parent._children[nodename].concat(this.parseXML(child, true, forceLower));
        }
      }
    }
  }
  return root;
};

/**
 * Retrieves specified dot-separated value from a multilevel object element
 *
 * @function JanusFireboxParser.arrayget
 * @param {object} obj
 * @param {string} name
 * @param {object|number|string} [defval] default value if none found
*/
JanusFireboxParser.prototype.arrayget = function(obj, name, defval) {
  var ptr = obj;
  var x = name.split(".");
  for (var i = 0; i < x.length; i++) {
    if (ptr==null || (!(ptr[x[i]] instanceof Array) && !(ptr[x[i]] instanceof Object) && i != x.length-1)) {
      ptr = null;
      break;
    }
    ptr = ptr[x[i]];
  }
  if (typeof ptr == "undefined" || ptr === null) {
    return (typeof defval == "undefined" ? null : defval);
  }
  return ptr;
};

/**
 * Fixes encoding of URL strings if they weren't properly encoded
 *
 * @function JanusFireboxParser.fixURLEncoding
 * @param {string} url
 * @returns {string} encoded URL
*/
JanusFireboxParser.prototype.fixURLEncoding = function(url) {
  var fixed = url;
  if (url.indexOf(' ') != -1) {
    fixed = encodeURI(url);
  }
  return fixed;
};

/**
 * Exception class for XML parse errors
 */
function JanusFireboxParserException(msg) {
  this.message = msg;
}
<commit_msg>flipy parameter support<commit_after>JanusFireboxParser = function() {
}

JanusFireboxParser.prototype.parse = function(source, baseurl, datapath) {
  var xml = this.parseXML(source, false, true);
  var rooms = this.getAsArray(this.arrayget(xml, 'fireboxroom._children.room', {}));
  var room = {_children: {}};
  for (var i = 0; i < rooms.length; i++) {
    var attrs = Object.keys(rooms[i]).filter(function(k) { return (k[0] != '_'); });
    attrs.forEach(function(k) {
      room[k] = rooms[i][k];
    });
    if (rooms[i]._children) {
      Object.keys(rooms[i]._children).forEach(function(k) {
        room._children[k] = rooms[i]._children[k];
      });
    }
  }
  var roomdata = this.getRoomData(xml, room, baseurl, datapath);
  roomdata.source = source;
  return roomdata;
}
JanusFireboxParser.prototype.getRoomData = function(xml, room, baseurl, datapath) {
  var assets = this.parseAssets(xml, baseurl, datapath);

  var parseNode = this.parseNode.bind(this);

  var roomdata = {
    assets: assets,
    room: this.parseNode(room)
  };
  for (var k in room._children) {
    var objects = this.getAsArray(room._children[k]);
    roomdata[k] = objects.map(parseNode);
  }

  // TODO - some rooms have orphan objects, outside of the <Room> tag.
  // We should parse these too, but I don't have any examples to test with
/*
  var orphantypes = ['link', 'image', 'image3d', 'video', 'sound', 'text', 'paragraph', 'light', 'particle', 'ghost'];
  for (var i = 0; i < orphantypes.length; i++) {
    var type = orphantypes[i],
        orphans = this.getAsArray(this.arrayget(xml, 'fireboxroom._children.' + type));
    if (orphans && orphans.length > 0) {
      if (!roomdata[type]) roomdata[type] = [];
      roomdata[type].push.apply(roomdata[type], orphans);
    }
  };
*/
  return roomdata;
}
JanusFireboxParser.prototype.getAsArray = function(arr) {
  return (arr instanceof Array ? arr : [arr]);
}
JanusFireboxParser.prototype.parseAssets = function(xml, baseurl, datapath) {
  var assetxml = this.arrayget(xml, 'fireboxroom._children.assets.0', {});
  var objectassets = this.getAsArray(this.arrayget(assetxml, "_children.assetobject", []));
  var soundassets = this.getAsArray(this.arrayget(assetxml, "_children.assetsound", []));
  var imageassets = this.getAsArray(this.arrayget(assetxml, "_children.assetimage", []));
  var videoassets = this.getAsArray(this.arrayget(assetxml, "_children.assetvideo", []));
  var scriptassets = this.getAsArray(this.arrayget(assetxml, "_children.assetscript", []));
  var ghostassets = this.getAsArray(this.arrayget(assetxml, "_children.assetghost", []));
  var websurfaceassets = this.getAsArray(this.arrayget(assetxml, "_children.assetwebsurface", []));
  var assetlist = [];
  if (!datapath) {
    datapath = 'http://web.janusvr.com/media';
  }
  var fixURLEncoding = this.fixURLEncoding.bind(this);
  imageassets.forEach(function(n) {
    var src = fixURLEncoding(n.src.match(/^file:/) ? n.src.replace(/^file:/, datapath) : n.src);
    assetlist.push({
      assettype: 'image',
      name: n.id,
      src: src,
      baseurl: baseurl,
      hasalpha: n.hasalpha,
      proxy: n.proxy,
      sbs3d: n.sbs3d,
      ou3d: n.ou3d,
      flipy: n.flipy,
      reverse3d: n.reverse3d,
    });
  });
  videoassets.forEach(function(n) {
    var src = fixURLEncoding(n.src.match(/^file:/) ? n.src.replace(/^file:/, datapath) : n.src);
    assetlist.push({
      assettype:'video',
      name:n.id,
      src: src,
      loop: n.loop,
      sbs3d: n.sbs3d == 'true',
      ou3d: n.ou3d == 'true',
      auto_play: n.auto_play == 'true',
      baseurl: baseurl,
      proxy: n.proxy && n.proxy != 'false'
    });
  });
  soundassets.forEach(function(n) {
    var src = fixURLEncoding(n.src.match(/^file:/) ? n.src.replace(/^file:/, datapath) : n.src);
    assetlist.push({
      assettype:'sound',
      name:n.id,
      src: src,
      baseurl: baseurl
    });
  });
  var websurfaces = {};
  websurfaceassets.forEach(function(n) { websurfaces[n.id] = n; });
  scriptassets.forEach(function(n) {
    var src = fixURLEncoding(n.src.match(/^file:/) ? n.src.replace(/^file:/, datapath) : n.src);
    assetlist.push({
      assettype:'script',
      name: src,
      src: src,
      baseurl: baseurl
    });
  });

  var objlist = [];
  objectassets.forEach(function(n) {
    if (n.src) {
      var src = fixURLEncoding(n.src.match(/^file:/) ? n.src.replace(/^file:/, datapath) : n.src);
      var mtlsrc = (n.mtl && n.mtl.match(/^file:/) ? n.mtl.replace(/^file:/, datapath) : n.mtl);
      if (mtlsrc && !mtlsrc.match(/^(https?:)?\/\//)) mtlsrc = baseurl + mtlsrc;
      var srcparts = src.split(' ');
      src = srcparts[0];
      assetlist.push({assettype: 'model', name: n.id, src: src, mtl: mtlsrc, tex_linear: n.tex_linear, tex0: n.tex || n.tex0 || srcparts[1], tex1: n.tex1 || srcparts[2], tex2: n.tex2 || srcparts[3], tex3: n.tex3 || srcparts[4]});
    }
  });
  var assets = {
    object: objectassets,
    image: imageassets,
    sound: soundassets,
    video: videoassets,
    websurfaces: websurfaces,
    scripts: scriptassets,
    ghosts: ghostassets,
    assetlist: assetlist
  };
  return assets;
}

JanusFireboxParser.prototype.getVectorValue = function(vector, defaultvalue) {
  if (typeof defaultvalue == 'undefined') {
    defaultvalue = null;//[0,0,0];
  }
  if (typeof vector == 'string') {
    return vector.trim().split(' ').map(parseFloat);
  } else if (vector instanceof THREE.Vector3) {
    return vector.toArray();
  } else if (typeof vector == 'undefined') {
    return defaultvalue;
  }
  return vector;
}
JanusFireboxParser.prototype.parseNode = function(n) {
  var nodeinfo = {};
  var attrs = Object.keys(n);
  attrs.forEach(function(k) {
    nodeinfo[k] = (n[k] == 'false' ? false : n[k]);
  });

  nodeinfo.pos = this.getVectorValue(n.pos);
  nodeinfo.scale = this.getVectorValue(n.scale, [1,1,1]);
  nodeinfo.orientation = this.getOrientation(n.xdir, n.ydir || n.up, n.zdir || n.fwd);
  nodeinfo.col = (n.col ? (n.col[0] == '#' ? [parseInt(n.col.substr(1,2), 16)/255, parseInt(n.col.substr(3, 2), 16)/255, parseInt(n.col.substr(5, 2), 16)/255] : n.col) : null);

  var minscale = 1e-20;
/*
  nodeinfo.scale[0] = Math.max(minscale, nodeinfo.scale[0]);
  nodeinfo.scale[1] = Math.max(minscale, nodeinfo.scale[1]);
  nodeinfo.scale[2] = Math.max(minscale, nodeinfo.scale[2]);
*/
  if (nodeinfo.scale[0] < minscale && nodeinfo.scale[0] > -minscale) nodeinfo.scale[0] = minscale;
  if (nodeinfo.scale[1] < minscale && nodeinfo.scale[1] > -minscale) nodeinfo.scale[1] = minscale;
  if (nodeinfo.scale[2] < minscale && nodeinfo.scale[2] > -minscale) nodeinfo.scale[2] = minscale;

  return nodeinfo;
}

JanusFireboxParser.prototype.getOrientation = function(xdir, ydir, zdir) {
  if (xdir) {
    xdir = (xdir instanceof THREE.Vector3 ? xdir : new THREE.Vector3().fromArray(xdir.split(' '))).normalize();
  } else {
    xdir = new THREE.Vector3(1,0,0);
  }
  if (ydir) {
    ydir = (ydir instanceof THREE.Vector3 ? ydir : new THREE.Vector3().fromArray(ydir.split(' '))).normalize();
  } else {
    ydir = new THREE.Vector3(0,1,0);
  }
  if (zdir) {
    zdir = (zdir instanceof THREE.Vector3 ? zdir : new THREE.Vector3().fromArray(zdir.split(' '))).normalize();
  } else {
    zdir = new THREE.Vector3(0,0,1);
  }

  var newydir = ydir.clone().sub(zdir).multiplyScalar(ydir.dot(zdir)).normalize();
  xdir.crossVectors(ydir, zdir).normalize();

  var mat4 = new THREE.Matrix4().makeBasis(xdir, ydir, zdir);
  var quat = new THREE.Quaternion();
  var pos = new THREE.Vector3();
  var scale = new THREE.Vector3();
  //quat.setFromRotationMatrix(mat4);
  mat4.decompose(pos, quat, scale);
  quat.normalize();
  //quat.multiply(new THREE.Quaternion().setFromEuler(new THREE.Euler(0, Math.PI, 0)));
  return quat;
}
JanusFireboxParser.prototype.parseXML = function(imgxml, leaf, forceLower) {
  var node, root, parent, xmldoc;
  if (imgxml.nodeName) {
    xmldoc = node = imgxml;
  } else {
    try {
      if (window.DOMParser) {
        var parser = new DOMParser();
        xmldoc = parser.parseFromString(imgxml,"application/xml");
        node = xmldoc.firstChild;
      } else {
        node = new ActiveXObject("Microsoft.XMLDOM");
        node.async = "false";
        node.loadXML(imgxml).firstChild;
      }

      // Chrome doesn't throw an exception for malformed XML, so we look for a <parsererror> xml tag
      var parsererrors = node.getElementsByTagName("parsererror");
      if (parsererrors.length > 0) {
        // Extract the message from the first div child of the <parsererror> element
        var errorel = parsererrors[0].getElementsByTagName('div')[0] || parsererrors[0];
        throw new JanusFireboxParserException(errorel.innerHTML);
      }
    } catch (e) {
      throw new JanusFireboxParserException(e.message);
    }
  }
  root = {};
  if (!leaf) {
    var rootname = node.tagName;
    if (forceLower) rootname = rootname.toLowerCase();
    if (!root[rootname]) root[rootname] = {};
    parent = root[rootname];
    //node = parent[node.tagName];
  } else {
    parent = root;
  }
  if (node.attributes) {
    for (var i = 0; i < node.attributes.length; i++) {
      var name = node.attributes[i].nodeName;
      if (forceLower) name = name.toLowerCase();
      var value = node.attributes[i].nodeValue;
      parent[name] = value;
    }
  }
  if (node.childNodes) {
    for (var j = 0; j < node.childNodes.length; j++) {
      var child = node.childNodes[j];
      var nodename = child.nodeName;
      if (forceLower) nodename = nodename.toLowerCase();
      if (!parent._children) parent._children = {};
      if (!parent._children[nodename]) {
        parent._children[nodename] = [];
      } else if (parent._children[nodename].constructor !== Array) {
        parent._children[nodename] = [parent._children[nodename]];
      }
      if (node.getElementsByTagName(child.tagName).length > 0) {
        parent._children[nodename].push(this.parseXML(child, true, forceLower));
      } else if (child.nodeName) {
        if (child.nodeName == "#text" || child.nodeName == "#cdata-section") {
          // this gets confused if you have multiple text/cdata nodes...
          if (!child.nodeValue.match(/^[\s\n]*$/m)) {
            parent._content = child.nodeValue;
          }
        } else {
          parent._children[nodename] = parent._children[nodename].concat(this.parseXML(child, true, forceLower));
        }
      }
    }
  }
  return root;
};

/**
 * Retrieves specified dot-separated value from a multilevel object element
 *
 * @function JanusFireboxParser.arrayget
 * @param {object} obj
 * @param {string} name
 * @param {object|number|string} [defval] default value if none found
*/
JanusFireboxParser.prototype.arrayget = function(obj, name, defval) {
  var ptr = obj;
  var x = name.split(".");
  for (var i = 0; i < x.length; i++) {
    if (ptr==null || (!(ptr[x[i]] instanceof Array) && !(ptr[x[i]] instanceof Object) && i != x.length-1)) {
      ptr = null;
      break;
    }
    ptr = ptr[x[i]];
  }
  if (typeof ptr == "undefined" || ptr === null) {
    return (typeof defval == "undefined" ? null : defval);
  }
  return ptr;
};

/**
 * Fixes encoding of URL strings if they weren't properly encoded
 *
 * @function JanusFireboxParser.fixURLEncoding
 * @param {string} url
 * @returns {string} encoded URL
*/
JanusFireboxParser.prototype.fixURLEncoding = function(url) {
  var fixed = url;
  if (url.indexOf(' ') != -1) {
    fixed = encodeURI(url);
  }
  return fixed;
};

/**
 * Exception class for XML parse errors
 */
function JanusFireboxParserException(msg) {
  this.message = msg;
}
<|endoftext|>


<commit_before>// Backbone.Validation v0.3.0
//
// Copyright (C)2011 Thomas Pedersen
// Distributed under MIT License
//
// Documentation and full license availabe at:
// http://github.com/thedersen/backbone.validation

Backbone.Validation = (function(Backbone, _, undefined) {
    var defaultSelector = 'name';
    var getValidatedAttrs = function(model){
        var validatedAttrs = {};
        for (var attr in model.validation) {
            if(model.validation.hasOwnProperty(attr)){
                validatedAttrs[attr] = undefined;
            }
        }
        return validatedAttrs;
    };

    var getValidators = function(model, attr) {
        var validation = model.validation[attr],
            validators = [];

        if (_.isFunction(validation)) {
            return validation;
        } else if(_.isString(validation)) {
            return model[validation];
        } else {
            for (var validator in validation) {
                if (validator !== 'msg' && validation.hasOwnProperty(validator)) {
                    validators.push({
                        fn: Backbone.Validation.validators[validator],
                        val: validation[validator],
                        msg: validation.msg
                    });
                }
            }
            return validators;
        }
    };

    var validateAttr = function(model, attr, value) {
        var validators = getValidators(model, attr),
            error = '',
            validator,
            result;

        if (_.isFunction(validators)) {
            return validators.call(model, value, attr);
        } else {
            for (var i = 0; i < validators.length; i++) {
                validator = validators[i];
                result = validator.fn(value, attr, validator.val, model);
                if(result === false) {
                    return;
                }
                else if (result) {
                    error += validator.msg || result;
                }
            };
            return error;
        }
    };

    return {
        version: '0.3.0',

        setDefaultSelector: function(selector){
            defaultSelector = selector;
        },

        bind: function(view, options) {
            options = options || {};
            var model = view.model,
                delayTrigger,
                selector = options.selector || defaultSelector,
                validFn = options.valid || Backbone.Validation.callbacks.valid,
                invalidFn = options.invalid || Backbone.Validation.callbacks.invalid,
                isValid = _.isUndefined(model.validation);

            model.validate = function(attrs) {
                if(!attrs){
                    return model.validate.call(model, _.extend(getValidatedAttrs(model), model.toJSON()));
                }

                var result = [], invalidAttrs = [];
                isValid = true;

                for (var changedAttr in attrs) {
                    var error = validateAttr(model, changedAttr, attrs[changedAttr]);
                    if (error) {
                        result.push(error);
                        invalidAttrs.push(changedAttr);
                        isValid = false;
                        invalidFn(view, changedAttr, error, selector);
                    } else {
                        validFn(view, changedAttr, selector);
                    }
                }

                if (isValid) {
                    for (var validatedAttr in model.validation) {
                        if (_.isUndefined(attrs[validatedAttr]) && validateAttr(model, validatedAttr, model.get(validatedAttr))) {
                            isValid = false;
                            break;
                        }
                    }
                }

                _.defer(function() {
                   model.trigger('validated', isValid, model, invalidAttrs);
                   model.trigger('validated:' + (isValid ? 'valid' : 'invalid'), model, invalidAttrs);
                });

                if (result.length === 1) {
                    return result[0];
                }
                if (result.length > 1) {
                    return result;
                }
            };

            model.isValid = function() {
                return isValid;
            };
        },

        unbind: function(view) {
            view.model.validate = undefined;
        }
    };
} (Backbone, _));

Backbone.Validation.callbacks = {
    valid: function(view, attr, selector) {
        view.$('[' + selector + '~=' + attr + ']')
            .removeClass('invalid')
            .removeAttr('data-error');
    },

    invalid: function(view, attr, error, selector) {
        view.$('[' + selector + '~=' + attr + ']')
            .addClass('invalid')
            .attr('data-error', error);
    }
};

Backbone.Validation.patterns = {
    digits: /^\d+$/,
    number: /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/,
    email: /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/,
    url: /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i
};

Backbone.Validation.messages = {
    required: '{0} is required',
    acceptance: '{0} must be accepted',
    min: '{0} must be grater than or equal to {1}',
    max: '{0} must be less than or equal to {1}',
    range: '{0} must be between {1} and {2}',
    length: '{0} must be {1} characters',
    minLength: '{0} must be at least {1} characters',
    maxLength: '{0} must be at most {1} characters',
    rangeLength: '{0} must be between {1} and {2} characters',
    oneOf: '{0} must be one of: {1}',
    equalTo: '{0} must be the same as {1}',
    pattern: '{0} must be a valid {1}'
};

Backbone.Validation.validators = (function(patterns, messages, _) {
    var trim = String.prototype.trim ?
        		function(text) {
        			return text == null ? '' : String.prototype.trim.call(text);
        		} :
        		function(text) {
        		    var trimLeft = /^\s+/,
                        trimRight = /\s+$/;

        			return text == null ? '' : text.toString().replace(trimLeft, '').replace(trimRight, '');
        		};
    var format = function() {
        var args = Array.prototype.slice.call(arguments);
        var text = args.shift();
        return text.replace(/{(\d+)}/g, function(match, number) {
            return typeof args[number] != 'undefined' ? args[number] : match;
        });
    };
    var isNumber = function(value){
        return _.isNumber(value) || (_.isString(value) && value.match(patterns.number));
    };
    var hasValue = function(value) {
        return !(_.isNull(value) || _.isUndefined(value) || (_.isString(value) && trim(value) === ''));
    };

    return {
        required: function(value, attr, required, model) {
            var isRequired = _.isFunction(required) ? required.call(model) : required;
            if(!isRequired && (!hasValue(value))) {
                return false; // overrides all other validators
            }
            if (isRequired && !hasValue(value)) {
                return format(messages.required, attr);
            }
        },
        acceptance: function(value, attr) {
            if(!_.isBoolean(value) || value === false){
                return format(messages.acceptance, attr);
            }
        },
        min: function(value, attr, minValue) {
            if (!isNumber(value) || value < minValue) {
                return format(messages.min, attr, minValue);
            }
        },
        max: function(value, attr, maxValue) {
            if (!isNumber(value) || value > maxValue) {
                return format(messages.max, attr, maxValue);
            }
        },
        range: function(value, attr, range) {
            if(!isNumber(value) || value < range[0] || value > range[1]) {
                return format(messages.range, attr, range[0], range[1]);
            }
        },
        length: function(value, attr, length) {
            if (!hasValue(value) || trim(value).length !== length) {
                return format(messages.length, attr, length);
            }
        },
        minLength: function(value, attr, minLength) {
            if (!hasValue(value) || trim(value).length < minLength) {
                return format(messages.minLength, attr, minLength);
            }
        },
        maxLength: function(value, attr, maxLength) {
            if (!hasValue(value) || trim(value).length > maxLength) {
                return format(messages.maxLength, attr, maxLength);
            }
        },
        rangeLength: function(value, attr, range) {
            var length = trim(value).length;
            if(!hasValue(value) || length < range[0] || length > range[1]) {
                return format(messages.rangeLength, attr, range[0], range[1]);
            }
        },
        oneOf: function(value, attr, values) {
            if(!_.include(values, value)){
                return format(messages.oneOf, attr, values.join(', '));
            }
        },
        equalTo: function(value, attr, equalTo, model) {
            if(value !== model.get(equalTo)) {
                return format(messages.equalTo, attr, equalTo);
            }
        },
        pattern: function(value, attr, pattern) {
            if (!hasValue(value) || !value.toString().match(patterns[pattern] || pattern)) {
                return format(messages.pattern, attr, pattern);
            }
        }
    };
} (Backbone.Validation.patterns, Backbone.Validation.messages, _));
<commit_msg>Bumped version number to 0.3.1<commit_after>// Backbone.Validation v0.3.1
//
// Copyright (C)2011 Thomas Pedersen
// Distributed under MIT License
//
// Documentation and full license availabe at:
// http://github.com/thedersen/backbone.validation

Backbone.Validation = (function(Backbone, _, undefined) {
    var defaultSelector = 'name';
    var getValidatedAttrs = function(model){
        var validatedAttrs = {};
        for (var attr in model.validation) {
            if(model.validation.hasOwnProperty(attr)){
                validatedAttrs[attr] = undefined;
            }
        }
        return validatedAttrs;
    };

    var getValidators = function(model, attr) {
        var validation = model.validation[attr],
            validators = [];

        if (_.isFunction(validation)) {
            return validation;
        } else if(_.isString(validation)) {
            return model[validation];
        } else {
            for (var validator in validation) {
                if (validator !== 'msg' && validation.hasOwnProperty(validator)) {
                    validators.push({
                        fn: Backbone.Validation.validators[validator],
                        val: validation[validator],
                        msg: validation.msg
                    });
                }
            }
            return validators;
        }
    };

    var validateAttr = function(model, attr, value) {
        var validators = getValidators(model, attr),
            error = '',
            validator,
            result;

        if (_.isFunction(validators)) {
            return validators.call(model, value, attr);
        } else {
            for (var i = 0; i < validators.length; i++) {
                validator = validators[i];
                result = validator.fn(value, attr, validator.val, model);
                if(result === false) {
                    return;
                }
                else if (result) {
                    error += validator.msg || result;
                }
            };
            return error;
        }
    };

    return {
        version: '0.3.1',

        setDefaultSelector: function(selector){
            defaultSelector = selector;
        },

        bind: function(view, options) {
            options = options || {};
            var model = view.model,
                delayTrigger,
                selector = options.selector || defaultSelector,
                validFn = options.valid || Backbone.Validation.callbacks.valid,
                invalidFn = options.invalid || Backbone.Validation.callbacks.invalid,
                isValid = _.isUndefined(model.validation);

            model.validate = function(attrs) {
                if(!attrs){
                    return model.validate.call(model, _.extend(getValidatedAttrs(model), model.toJSON()));
                }

                var result = [], invalidAttrs = [];
                isValid = true;

                for (var changedAttr in attrs) {
                    var error = validateAttr(model, changedAttr, attrs[changedAttr]);
                    if (error) {
                        result.push(error);
                        invalidAttrs.push(changedAttr);
                        isValid = false;
                        invalidFn(view, changedAttr, error, selector);
                    } else {
                        validFn(view, changedAttr, selector);
                    }
                }

                if (isValid) {
                    for (var validatedAttr in model.validation) {
                        if (_.isUndefined(attrs[validatedAttr]) && validateAttr(model, validatedAttr, model.get(validatedAttr))) {
                            isValid = false;
                            break;
                        }
                    }
                }

                _.defer(function() {
                   model.trigger('validated', isValid, model, invalidAttrs);
                   model.trigger('validated:' + (isValid ? 'valid' : 'invalid'), model, invalidAttrs);
                });

                if (result.length === 1) {
                    return result[0];
                }
                if (result.length > 1) {
                    return result;
                }
            };

            model.isValid = function() {
                return isValid;
            };
        },

        unbind: function(view) {
            view.model.validate = undefined;
        }
    };
} (Backbone, _));

Backbone.Validation.callbacks = {
    valid: function(view, attr, selector) {
        view.$('[' + selector + '~=' + attr + ']')
            .removeClass('invalid')
            .removeAttr('data-error');
    },

    invalid: function(view, attr, error, selector) {
        view.$('[' + selector + '~=' + attr + ']')
            .addClass('invalid')
            .attr('data-error', error);
    }
};

Backbone.Validation.patterns = {
    digits: /^\d+$/,
    number: /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/,
    email: /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/,
    url: /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i
};

Backbone.Validation.messages = {
    required: '{0} is required',
    acceptance: '{0} must be accepted',
    min: '{0} must be grater than or equal to {1}',
    max: '{0} must be less than or equal to {1}',
    range: '{0} must be between {1} and {2}',
    length: '{0} must be {1} characters',
    minLength: '{0} must be at least {1} characters',
    maxLength: '{0} must be at most {1} characters',
    rangeLength: '{0} must be between {1} and {2} characters',
    oneOf: '{0} must be one of: {1}',
    equalTo: '{0} must be the same as {1}',
    pattern: '{0} must be a valid {1}'
};

Backbone.Validation.validators = (function(patterns, messages, _) {
    var trim = String.prototype.trim ?
        		function(text) {
        			return text == null ? '' : String.prototype.trim.call(text);
        		} :
        		function(text) {
        		    var trimLeft = /^\s+/,
                        trimRight = /\s+$/;

        			return text == null ? '' : text.toString().replace(trimLeft, '').replace(trimRight, '');
        		};
    var format = function() {
        var args = Array.prototype.slice.call(arguments);
        var text = args.shift();
        return text.replace(/{(\d+)}/g, function(match, number) {
            return typeof args[number] != 'undefined' ? args[number] : match;
        });
    };
    var isNumber = function(value){
        return _.isNumber(value) || (_.isString(value) && value.match(patterns.number));
    };
    var hasValue = function(value) {
        return !(_.isNull(value) || _.isUndefined(value) || (_.isString(value) && trim(value) === ''));
    };

    return {
        required: function(value, attr, required, model) {
            var isRequired = _.isFunction(required) ? required.call(model) : required;
            if(!isRequired && (!hasValue(value))) {
                return false; // overrides all other validators
            }
            if (isRequired && !hasValue(value)) {
                return format(messages.required, attr);
            }
        },
        acceptance: function(value, attr) {
            if(!_.isBoolean(value) || value === false){
                return format(messages.acceptance, attr);
            }
        },
        min: function(value, attr, minValue) {
            if (!isNumber(value) || value < minValue) {
                return format(messages.min, attr, minValue);
            }
        },
        max: function(value, attr, maxValue) {
            if (!isNumber(value) || value > maxValue) {
                return format(messages.max, attr, maxValue);
            }
        },
        range: function(value, attr, range) {
            if(!isNumber(value) || value < range[0] || value > range[1]) {
                return format(messages.range, attr, range[0], range[1]);
            }
        },
        length: function(value, attr, length) {
            if (!hasValue(value) || trim(value).length !== length) {
                return format(messages.length, attr, length);
            }
        },
        minLength: function(value, attr, minLength) {
            if (!hasValue(value) || trim(value).length < minLength) {
                return format(messages.minLength, attr, minLength);
            }
        },
        maxLength: function(value, attr, maxLength) {
            if (!hasValue(value) || trim(value).length > maxLength) {
                return format(messages.maxLength, attr, maxLength);
            }
        },
        rangeLength: function(value, attr, range) {
            var length = trim(value).length;
            if(!hasValue(value) || length < range[0] || length > range[1]) {
                return format(messages.rangeLength, attr, range[0], range[1]);
            }
        },
        oneOf: function(value, attr, values) {
            if(!_.include(values, value)){
                return format(messages.oneOf, attr, values.join(', '));
            }
        },
        equalTo: function(value, attr, equalTo, model) {
            if(value !== model.get(equalTo)) {
                return format(messages.equalTo, attr, equalTo);
            }
        },
        pattern: function(value, attr, pattern) {
            if (!hasValue(value) || !value.toString().match(patterns[pattern] || pattern)) {
                return format(messages.pattern, attr, pattern);
            }
        }
    };
} (Backbone.Validation.patterns, Backbone.Validation.messages, _));
<|endoftext|>