<commit_before>/**
 * VIE - Vienna IKS Editables
 * (c) 2011 Henri Bergius, IKS Consortium
 */
(function() {
    // Export the VIE namespace for both CommonJS and the browser
    var VIE;
    if (typeof exports !== 'undefined') {
        VIE = exports;
    } else {
        VIE = this.VIE = {};
    }

    // Require underscore using CommonJS require if it isn't yet loaded
    var _ = this._;
    if (!_ && (typeof require !== 'undefined')) _ = require('underscore')._;
    if (!_) {
        throw 'VIE requires underscore.js to be available';
    }

    // Require Backbone using CommonJS require if it isn't yet loaded
    var Backbone = this.Backbone;
    if (!Backbone && (typeof require !== 'undefined')) Backbone = require('backbone');
    if (!Backbone) {
        throw 'VIE requires Backbone.js to be available';
    }

    // Require jQuery using CommonJS require if it isn't yet loaded
    var jQuery = this.jQuery;
    if (!jQuery && (typeof require !== 'undefined')) jQuery = require('jquery');
    if (!jQuery) {
        throw 'VIE requires jQuery to be available';
    }

    // Backbone Model for RDF entities
    VIE.RDFEntity = Backbone.Model.extend({
        namespaces: {},
        type: '',

        toJSONLD: function() {
            var instance = this;
            var instanceLD = {"@":"<" + instance.id + ">"};

            instanceLD['#'] = instance.namespaces;

            if (instance.type) {
                instanceLD.a = instance.type;
            }

            for (var property in instance.attributes) if(instance.attributes.hasOwnProperty(property)) { //  && typeof instance.attributes[property] != "function"
                if (["id"].indexOf(property) == -1)
                    instanceLD[property] = instance.attributes[property];
            }
            return instanceLD;
        }
    });

    // Backbone View for RDF entities represented in RDFa
    VIE.RDFaView = Backbone.View.extend({
        // Ensure view gets updated when properties of the entity change
        initialize: function() {
            _.bindAll(this, 'render');
            this.model.bind('change', this.render);
        },

        render: function() {
            VIE.RDFa.writeEntity(this.el, this.model.toJSONLD());
            return this;
        }
    });

    // Entity Manager keeps track of all RDFa entities loaded via VIE
    VIE.EntityManager = {
        Entities: {},
        allEntities: [],

        Types: {},

        getById: function(id) {
            if (typeof VIE.EntityManager.Entities[id] === 'undefined') {
                return null;
            }
            return VIE.EntityManager.Entities[id];
        },

        getByJSONLD: function(jsonld) {
            var entityInstance;
            var properties;

            properties = VIE.EntityManager._JSONtoProperties(jsonld);

            if (typeof jsonld['@'] !== 'undefined') {
                entityInstance = VIE.EntityManager.getById(jsonld['@']);
                if (entityInstance) {
                    entityInstance.set(properties);
                    return entityInstance;
                }
            }

            entityInstance = new VIE.RDFEntity(properties);

            if (typeof jsonld['#'] !== 'undefined') {
                entityInstance.namespaces = jsonld['#'];
            }

            if (typeof jsonld.a !== 'undefined') {
                entityInstance.type = jsonld.a;
            }

            if (typeof jsonld['@'] !== 'undefined') {
                entityInstance.id = jsonld['@'];
                VIE.EntityManager.Entities[entityInstance.id] = entityInstance;
            }

            VIE.EntityManager.allEntities.push(entityInstance);

            return entityInstance;
        },

        // Clean up JSON-LD so that it can be used as properties in a Backbone Model
        _JSONtoProperties: function(jsonld) {
            var properties;
            properties = jQuery.extend({}, jsonld);
            delete properties['@'];
            delete properties.a;
            delete properties['#'];
            return properties;
        }
    };

    // Mapping between RDFa and Backbone models
    VIE.RDFaEntities = {
        Views: [],

        // Create a Backbone model instance for a RDFa-marked element
        getInstance: function(element) {
            element = jQuery(element);
            var entityInstance;
            var viewInstance;
            var jsonld;

            jsonld = VIE.RDFa.readEntity(element);
            if (!jsonld) {
                return null;
            }

            entityInstance = VIE.EntityManager.getByJSONLD(jsonld);

            jQuery.each(VIE.RDFaEntities.Views, function() {
                // Check whether we already have this view instantiated for the element
                if (this.el.get(0) == element.get(0)) {
                    viewInstance = this;
                    return false;
                }
            });

            if (!viewInstance) {
                // Create a view for the RDFa
                viewInstance = new VIE.RDFaView({
                    model: entityInstance,
                    el: element,
                    tagName: element.get(0).nodeName
                });
                VIE.RDFaEntities.Views.push(viewInstance);
            }

            return entityInstance;
        },

        // Get a list of Backbone model instances for all RDFa-marked content in an element
        getInstances: function(element) {
            var entities = [];
            var entity;

            if (typeof element === 'undefined') {
                element = jQuery(document);

                // We're working with the full document scope, add the document itself as an entity
                jQuery(VIE.RDFa.subjectSelector, element).andSelf().each(function() {
                    entity = VIE.RDFaEntities.getInstance(this);
                    if (entity) {
                        entities.push(entity);
                    }
                });
            } else {
                jQuery(VIE.RDFa.subjectSelector, element).each(function() {
                    entity = VIE.RDFaEntities.getInstance(this);
                    if (entity) {
                        entities.push(entity);
                    }
                });
            }

            return entities;
        }
    },

    // RDFa reading and writing utilities
    VIE.RDFa = {
        // Resolved prefix->namespace pairs
        Namespaces: {},

        subjectSelector: '[about],[typeof],[src]',

        // Get a JSON-LD en
        readEntity: function(element) {
            var entity;
            var subject;
            var namespaces = {};

            subject = VIE.RDFa.getSubject(element);

            // Read properties from element
            entity = VIE.RDFa._getElementProperties(subject, element, false);
            if (jQuery.isEmptyObject(entity)) {
                // No properties, skip creating entity
                return null;
            }

            // Resolve namespaces
            for (var propertyName in entity) {
                var propertyParts = propertyName.split(':');
                if (propertyParts.length === 1) {
                    // No namespace for element
                    continue;
                }
                namespaces[propertyParts[0]] = VIE.RDFa._resolveNamespace(propertyParts[0], element);
            }
            if (!jQuery.isEmptyObject(namespaces)) {
                entity['#'] = namespaces;
            }

            // Read typeof from element
            entity.a = VIE.RDFa._getElementValue(element, 'typeof');

            if (typeof subject == 'string') {
                entity['@'] = subject;
            }
            return entity;
        },

        readEntities: function(element) {
            var entities = [];
            var entity;

            if (typeof element === 'undefined') {
                element = jQuery(document);

                // We're working with the full document scope, add the document itself as an entity
                jQuery(VIE.RDFa.subjectSelector, element).andSelf().each(function() {
                    entity = VIE.RDFa.readEntity(this);
                    if (entity) {
                        entities.push(entity);
                    }
                });
            } else {
                jQuery(VIE.RDFa.subjectSelector, element).each(function() {
                    entity = VIE.RDFa.readEntity(this);
                    if (entity) {
                        entities.push(entity);
                    }
                });
            }

            return entities;
        },

        writeEntity: function(element, jsonld) {
            VIE.RDFa.findElementProperties(VIE.RDFa.getSubject(element), element, true).each(function() {
                var propertyElement = jQuery(this);
                var propertyName = propertyElement.attr('property');

                if (typeof jsonld[propertyName] === 'undefined') {
                    // Entity doesn't contain this property
                    return true;
                }

                if (jsonld[propertyName] instanceof Array) {
                    // For now we don't deal with multivalued properties in Views
                    return true;
                }

                if (VIE.RDFa._readPropertyValue(propertyElement) !== jsonld[propertyName]) {
                    VIE.RDFa._writePropertyValue(propertyElement, jsonld[propertyName]);
                }
            });
            return this;
        },

        _readPropertyValue: function(element) {
            // Property has machine-readable content value
            var content = element.attr('content');
            if (content) {
                return content;
            }

            // Property has inline value
            return element.html();
        },

        _writePropertyValue: function(element, value) {
            // Property has machine-readable content value
            var content = element.attr('content');
            if (content) {
                element.attr('content', value);
                return;
            }

            // Property has inline value
            element.html(value);
        },

        getSubject: function(element) {
            if (typeof document !== 'undefined') {
                if (element === document) {
                    return document.baseURI;
                }
            }
            var subject;
            jQuery(element).closest(VIE.RDFa.subjectSelector).each(function() {
                if (jQuery(this).attr('about')) {
                    subject = jQuery(this).attr('about');
                    return true;
                }
                if (jQuery(this).attr('src')) {
                    subject = jQuery(this).attr('src');
                    return true;
                }
                if (jQuery(this).attr('typeof')) {
                    subject = this;
                }
            });

            return subject;
        },

        _resolveNamespace: function(prefix, element) {
            if (typeof VIE.RDFa.Namespaces[prefix] !== 'undefined') {
                return VIE.RDFa.Namespaces[prefix];
            }

            jQuery('[xmlns\\:' + prefix + ']').each(function() {
                VIE.RDFa.Namespaces[prefix] = jQuery(this).attr('xmlns:' + prefix);
            });

            return VIE.RDFa.Namespaces[prefix];
        },

        // Get the value of attribute from the element or from one of its children
        _getElementValue: function(element, propertyName) {
            element = jQuery(element);
            if (typeof element.attr(propertyName) !== 'undefined')
            {
                // Direct match with container
                return element.attr(propertyName);
            }
            return element.find('[' + propertyName + ']').attr(propertyName);
        },

        findElementProperties: function(subject, element, allowPropertiesInProperties) {
            return jQuery(element).find('[property]').add(jQuery(element).filter('[property]')).filter(function() {
                if (VIE.RDFa.getSubject(this) !== subject) {
                    // The property is under another entity, skip
                    return false;
                }

                if (!allowPropertiesInProperties) {
                    if (!jQuery(this).parents('[property]').length) {
                        return true;
                    }
                    // This property is under another property, skip
                    return false;
                }

                return true;
            });
        },

        _getElementProperties: function(subject, element, emptyValues) {
            var containerProperties = {};

            VIE.RDFa.findElementProperties(subject, element, true).each(function() {
                var propertyName;
                var propertyValue;
                var objectProperty = jQuery(this);
                propertyName = objectProperty.attr('property');
                propertyValue = VIE.RDFa._readPropertyValue(objectProperty);

                if (typeof containerProperties[propertyName] !== 'undefined') {
                    if (containerProperties[propertyName] instanceof Array) {
                        if (emptyValues) {
                            return;
                        }
                        containerProperties[propertyName].push(propertyValue);
                        return;
                    }
                    // Multivalued property, convert to Array
                    var previousValue = containerProperties[propertyName];
                    containerProperties[propertyName] = [];

                    if (emptyValues) {
                        return;
                    }

                    containerProperties[propertyName].push(previousValue);
                    containerProperties[propertyName].push(propertyValue);
                    return;
                }

                if (emptyValues) {
                    containerProperties[propertyName] = '';
                    return;
                }

                containerProperties[propertyName] = propertyValue;
            });

            return containerProperties;
        }
    };

    VIE.cleanup = function() {
        VIE.EntityManager.Entities = {};
        VIE.EntityManager.allEntities = [];
        VIE.EntityManager.Types = {};
        VIE.RDFaEntities.Views = [];
        VIE.RDFa.Namespaces = {};
    };

}).call(this);
<commit_msg>Support for rel - resource properties<commit_after>/**
 * VIE - Vienna IKS Editables
 * (c) 2011 Henri Bergius, IKS Consortium
 */
(function() {
    // Export the VIE namespace for both CommonJS and the browser
    var VIE;
    if (typeof exports !== 'undefined') {
        VIE = exports;
    } else {
        VIE = this.VIE = {};
    }

    // Require underscore using CommonJS require if it isn't yet loaded
    var _ = this._;
    if (!_ && (typeof require !== 'undefined')) _ = require('underscore')._;
    if (!_) {
        throw 'VIE requires underscore.js to be available';
    }

    // Require Backbone using CommonJS require if it isn't yet loaded
    var Backbone = this.Backbone;
    if (!Backbone && (typeof require !== 'undefined')) Backbone = require('backbone');
    if (!Backbone) {
        throw 'VIE requires Backbone.js to be available';
    }

    // Require jQuery using CommonJS require if it isn't yet loaded
    var jQuery = this.jQuery;
    if (!jQuery && (typeof require !== 'undefined')) jQuery = require('jquery');
    if (!jQuery) {
        throw 'VIE requires jQuery to be available';
    }

    // Backbone Model for RDF entities
    VIE.RDFEntity = Backbone.Model.extend({
        namespaces: {},
        type: '',

        toJSONLD: function() {
            var instance = this;
            var instanceLD = {"@":"<" + instance.id + ">"};

            instanceLD['#'] = instance.namespaces;

            if (instance.type) {
                instanceLD.a = instance.type;
            }

            for (var property in instance.attributes) if(instance.attributes.hasOwnProperty(property)) { //  && typeof instance.attributes[property] != "function"
                if (["id"].indexOf(property) == -1)
                    instanceLD[property] = instance.attributes[property];
            }
            return instanceLD;
        }
    });

    // Backbone View for RDF entities represented in RDFa
    VIE.RDFaView = Backbone.View.extend({
        // Ensure view gets updated when properties of the entity change
        initialize: function() {
            _.bindAll(this, 'render');
            this.model.bind('change', this.render);
        },

        render: function() {
            VIE.RDFa.writeEntity(this.el, this.model.toJSONLD());
            return this;
        }
    });

    // Entity Manager keeps track of all RDFa entities loaded via VIE
    VIE.EntityManager = {
        Entities: {},
        allEntities: [],

        Types: {},

        getById: function(id) {
            if (typeof VIE.EntityManager.Entities[id] === 'undefined') {
                return null;
            }
            return VIE.EntityManager.Entities[id];
        },

        getByJSONLD: function(jsonld) {
            var entityInstance;
            var properties;

            properties = VIE.EntityManager._JSONtoProperties(jsonld);

            if (typeof jsonld['@'] !== 'undefined') {
                entityInstance = VIE.EntityManager.getById(jsonld['@']);
                if (entityInstance) {
                    entityInstance.set(properties);
                    return entityInstance;
                }
            }

            entityInstance = new VIE.RDFEntity(properties);

            if (typeof jsonld['#'] !== 'undefined') {
                entityInstance.namespaces = jsonld['#'];
            }

            if (typeof jsonld.a !== 'undefined') {
                entityInstance.type = jsonld.a;
            }

            if (typeof jsonld['@'] !== 'undefined') {
                entityInstance.id = jsonld['@'];
                VIE.EntityManager.Entities[entityInstance.id] = entityInstance;
            }

            VIE.EntityManager.allEntities.push(entityInstance);

            return entityInstance;
        },

        // Clean up JSON-LD so that it can be used as properties in a Backbone Model
        _JSONtoProperties: function(jsonld) {
            var properties;
            properties = jQuery.extend({}, jsonld);
            delete properties['@'];
            delete properties.a;
            delete properties['#'];
            return properties;
        }
    };

    // Mapping between RDFa and Backbone models
    VIE.RDFaEntities = {
        Views: [],

        // Create a Backbone model instance for a RDFa-marked element
        getInstance: function(element) {
            element = jQuery(element);
            var entityInstance;
            var viewInstance;
            var jsonld;

            jsonld = VIE.RDFa.readEntity(element);
            if (!jsonld) {
                return null;
            }

            entityInstance = VIE.EntityManager.getByJSONLD(jsonld);

            jQuery.each(VIE.RDFaEntities.Views, function() {
                // Check whether we already have this view instantiated for the element
                if (this.el.get(0) == element.get(0)) {
                    viewInstance = this;
                    return false;
                }
            });

            if (!viewInstance) {
                // Create a view for the RDFa
                viewInstance = new VIE.RDFaView({
                    model: entityInstance,
                    el: element,
                    tagName: element.get(0).nodeName
                });
                VIE.RDFaEntities.Views.push(viewInstance);
            }

            return entityInstance;
        },

        // Get a list of Backbone model instances for all RDFa-marked content in an element
        getInstances: function(element) {
            var entities = [];
            var entity;

            if (typeof element === 'undefined') {
                element = jQuery(document);

                // We're working with the full document scope, add the document itself as an entity
                jQuery(VIE.RDFa.subjectSelector, element).andSelf().each(function() {
                    entity = VIE.RDFaEntities.getInstance(this);
                    if (entity) {
                        entities.push(entity);
                    }
                });
            } else {
                jQuery(VIE.RDFa.subjectSelector, element).each(function() {
                    entity = VIE.RDFaEntities.getInstance(this);
                    if (entity) {
                        entities.push(entity);
                    }
                });
            }

            return entities;
        }
    },

    // RDFa reading and writing utilities
    VIE.RDFa = {
        // Resolved prefix->namespace pairs
        Namespaces: {},

        subjectSelector: '[about],[typeof],[src]',

        // Get a JSON-LD en
        readEntity: function(element) {
            var entity;
            var subject;
            var namespaces = {};
            var namespace;

            subject = VIE.RDFa.getSubject(element);

            // Read properties from element
            entity = VIE.RDFa._getElementProperties(subject, element, false);
            if (jQuery.isEmptyObject(entity)) {
                // No properties, skip creating entity
                return null;
            }

            // Resolve namespaces
            for (var propertyName in entity) {
                var propertyParts = propertyName.split(':');
                if (propertyParts.length === 1) {
                    // No namespace for element
                    continue;
                }
                namespace = VIE.RDFa._resolveNamespace(propertyParts[0], element);
                if (namespace) {
                    namespaces[propertyParts[0]] = namespace;
                }
            }
            if (!jQuery.isEmptyObject(namespaces)) {
                entity['#'] = namespaces;
            }

            // Read typeof from element
            entity.a = VIE.RDFa._getElementValue(element, 'typeof');

            if (typeof subject == 'string') {
                entity['@'] = subject;
            }
            return entity;
        },

        readEntities: function(element) {
            var entities = [];
            var entity;

            if (typeof element === 'undefined') {
                element = jQuery(document);

                // We're working with the full document scope, add the document itself as an entity
                jQuery(VIE.RDFa.subjectSelector, element).andSelf().each(function() {
                    entity = VIE.RDFa.readEntity(this);
                    if (entity) {
                        entities.push(entity);
                    }
                });
            } else {
                jQuery(VIE.RDFa.subjectSelector, element).each(function() {
                    entity = VIE.RDFa.readEntity(this);
                    if (entity) {
                        entities.push(entity);
                    }
                });
            }

            return entities;
        },

        writeEntity: function(element, jsonld) {
            VIE.RDFa.findElementProperties(VIE.RDFa.getSubject(element), element, true).each(function() {
                var propertyElement = jQuery(this);
                var propertyName = propertyElement.attr('property');

                if (typeof jsonld[propertyName] === 'undefined') {
                    // Entity doesn't contain this property
                    return true;
                }

                if (jsonld[propertyName] instanceof Array) {
                    // For now we don't deal with multivalued properties in Views
                    return true;
                }

                if (VIE.RDFa._readPropertyValue(propertyElement) !== jsonld[propertyName]) {
                    VIE.RDFa._writePropertyValue(propertyElement, jsonld[propertyName]);
                }
            });
            return this;
        },

        _readPropertyValue: function(element) {
            // Property has machine-readable content value
            var content = element.attr('content');
            if (content) {
                return content;
            }
            var resource = element.attr('resource');
            if (resource) {
                return '<' + resource + '>';
            }

            // Property has inline value
            return element.html();
        },

        _writePropertyValue: function(element, value) {
            // Property has machine-readable content value
            var content = element.attr('content');
            if (content) {
                element.attr('content', value);
                return;
            }
            var resource = element.attr('resource');
            if (resource) {
                element.attr('resource', value);
            }

            // Property has inline value
            element.html(value);
        },

        getSubject: function(element) {
            if (typeof document !== 'undefined') {
                if (element === document) {
                    return document.baseURI;
                }
            }
            var subject;
            jQuery(element).closest(VIE.RDFa.subjectSelector).each(function() {
                if (jQuery(this).attr('about')) {
                    subject = jQuery(this).attr('about');
                    return true;
                }
                if (jQuery(this).attr('src')) {
                    subject = jQuery(this).attr('src');
                    return true;
                }
                if (jQuery(this).attr('typeof')) {
                    subject = this;
                }
            });

            return subject;
        },

        _resolveNamespace: function(prefix, element) {
            if (typeof VIE.RDFa.Namespaces[prefix] !== 'undefined') {
                return VIE.RDFa.Namespaces[prefix];
            }

            jQuery('[xmlns\\:' + prefix + ']').each(function() {
                VIE.RDFa.Namespaces[prefix] = jQuery(this).attr('xmlns:' + prefix);
            });

            return VIE.RDFa.Namespaces[prefix];
        },

        // Get the value of attribute from the element or from one of its children
        _getElementValue: function(element, propertyName) {
            element = jQuery(element);
            if (typeof element.attr(propertyName) !== 'undefined')
            {
                // Direct match with container
                return element.attr(propertyName);
            }
            return element.find('[' + propertyName + ']').attr(propertyName);
        },

        findElementProperties: function(subject, element, allowPropertiesInProperties) {
            return jQuery(element).find('[property],[rel]').add(jQuery(element).filter('[property],[rel]')).filter(function() {
                if (VIE.RDFa.getSubject(this) !== subject) {
                    // The property is under another entity, skip
                    return false;
                }

                if (!allowPropertiesInProperties) {
                    if (!jQuery(this).parents('[property]').length) {
                        return true;
                    }
                    // This property is under another property, skip
                    return false;
                }

                return true;
            });
        },

        _getElementProperties: function(subject, element, emptyValues) {
            var containerProperties = {};

            VIE.RDFa.findElementProperties(subject, element, true).each(function() {
                var propertyName;
                var propertyValue;
                var objectProperty = jQuery(this);
                propertyName = objectProperty.attr('property');
                if (!propertyName) {
                    propertyName = objectProperty.attr('rel');
                }

                propertyValue = VIE.RDFa._readPropertyValue(objectProperty);

                if (typeof containerProperties[propertyName] !== 'undefined') {
                    if (containerProperties[propertyName] instanceof Array) {
                        if (emptyValues) {
                            return;
                        }
                        containerProperties[propertyName].push(propertyValue);
                        return;
                    }
                    // Multivalued property, convert to Array
                    var previousValue = containerProperties[propertyName];
                    containerProperties[propertyName] = [];

                    if (emptyValues) {
                        return;
                    }

                    containerProperties[propertyName].push(previousValue);
                    containerProperties[propertyName].push(propertyValue);
                    return;
                }

                if (emptyValues) {
                    containerProperties[propertyName] = '';
                    return;
                }

                containerProperties[propertyName] = propertyValue;
            });

            return containerProperties;
        }
    };

    VIE.cleanup = function() {
        VIE.EntityManager.Entities = {};
        VIE.EntityManager.allEntities = [];
        VIE.EntityManager.Types = {};
        VIE.RDFaEntities.Views = [];
        VIE.RDFa.Namespaces = {};
    };

}).call(this);
<|endoftext|>


<commit_before>import express from 'express';
import r from 'rethinkdb'
import Promise from 'bluebird'
import Room from '../../models/room'
import Item from '../../models/item'
import API from '../../lib/API'
import rediscache from 'express-redis-cache'
import { ENV, PORT, DATADOG_API_KEY, DATADOG_APP_KEY, REDIS_HOST, REDIS_PORT } from '../../environment'

const router = express.Router();

if (REDIS_HOST && REDIS_PORT) {
  const caching = rediscache({
    host: REDIS_HOST,
    port: REDIS_PORT,
    expire: 60
  })

  //Try to cache the results for at least 60 seconds as CPU is also costly
  router.use(caching.route())
}

API.registerCall(
  '/api/fm/world/:worldId/rooms',
  'Gets a list of rooms with the shops and items in a world.',
  API.createParameter(':worldId', 'number', 'The ID of the world. (0 = Scania, 1 = Windia, 2 = Bera, 3 = Khroa, 4 = MYBCKN, 5 = GRAZED)'),
  [
    {
        'shops': {
            'shopId': {
                'characterName': 'CharacterName',
                'id': 'shopId',
                'items': [
                    'itemObject'
                ]
            }
        }
    }
  ]
)
router.get('/world/:worldId/rooms', async (req, res, next) => {
  try{
    var worldId = Number(req.params.worldId)
    const rooms = await Room.findRooms(worldId)
    res.success(rooms)
  }catch(ex){
    res.status(500).send({error: ex.message || ex, trace: ex.trace || null, stack: ex.stack || null})
    console.log(ex, ex.stack)
  }
})

API.registerCall(
  '/api/fm/world/:worldId/rooms/legacy',
  'Gets all of the items in the world.',
  API.createParameter(':worldId', 'number', 'The ID of the world. (0 = Scania, 1 = Windia, 2 = Bera, 3 = Khroa, 4 = MYBCKN, 5 = GRAZED)'),
  [{
    t: 'item.acc',
    u: 'item.avoid',
    B: 'item.battleModeAtt',
    C: 'item.bossDmg',
    b: 'item.bundle',
    Q: 'item.overallCategory',
    R: 'item.category',
    S: 'item.subCategory',
    E: 'item.creator',
    P: 'item.description',
    k: 'item.dex',
    v: 'item.diligence',
    y: 'item.growth',
    A: 'item.hammerApplied',
    T: 'item.itemId',
    D: 'item.ignoreDef',
    l: 'item.intelligence',
    F: 'item.isIdentified',
    x: 'item.jump',
    m: 'item.luk',
    q: 'item.matk',
    s: 'item.mdef',
    n: 'item.mhp',
    o: 'item.mmp',
    O: 'item.name',
    V: 'item.nebulite',
    H: 'item.numberOfEnhancements',
    i: 'item.numberOfPlusses',
    c: 'item.price',
    a: 'item.quantity',
    G: 'item.rarity',
    w: 'item.speed',
    j: 'item.str',
    h: 'item.upgradesAvailable',
    p: 'item.watk',
    r: 'item.wdef',
    U: 'item.itemId',
    d: 'room.channel',
    e: 'room.room',
    f: 'shop.shopName',
    g: 'shop.characterName'
  }]
)
router.get('/world/:worldId/rooms/legacy', async (req, res, next) => {
  try{
    var worldId = Number(req.params.worldId)
    const rooms = await Room.findRooms(worldId)
    var mostRecentTimestamp = 0
    const items = rooms.reduce(function (allItems, room) {
      mostRecentTimestamp = Math.max(mostRecentTimestamp, room.createdAt)
      if (!room.shops.length) return allItems
      return allItems.concat.apply(allItems, room.shops.reduce(function (roomItems, shop) {
        if (!shop.items.length) return roomItems
        return roomItems.concat.apply(roomItems, shop.items.map(function (item) {
          var obscureItem = {
            t: item.acc,
            u: item.avoid,
            B: item.battleModeAtt,
            C: item.bossDmg,
            b: item.bundle,
            Q: item.overallCategory,
            R: item.category,
            S: item.subCategory,
            E: item.creator,
            P: item.description,
            k: item.dex,
            v: item.diligence,
            y: item.growth,
            A: item.hammerApplied,
            T: item.itemId,
            D: item.ignoreDef,
            l: item.intelligence,
            F: item.isIdentified,
            x: item.jump,
            m: item.luk,
            q: item.matk,
            s: item.mdef,
            n: item.mhp,
            o: item.mmp,
            O: item.name,
            V: item.nebulite,
            H: item.numberOfEnhancements,
            i: item.numberOfPlusses,
            c: item.price,
            a: item.quantity,
            G: item.rarity,
            w: item.speed,
            j: item.str,
            h: item.upgradesAvailable,
            p: item.watk,
            r: item.wdef,
            U: item.itemId,
            d: room.channel,
            e: room.room,
            f: shop.shopName,
            g: shop.characterName,
            I: item.potential1 ? item.potential1.line : undefined,
            J: item.potential2 ? item.potential2.line : undefined,
            K: item.potential3 ? item.potential3.line : undefined,
            L: item.bpotential1 ? item.bpotential1.line : undefined,
            M: item.bpotential2 ? item.bpotential2.line : undefined,
            N: item.bpotential3 ? item.bpotential3.line : undefined,
            Y: item.isCash
          }

          Object.keys(obscureItem).forEach((key) => {
            if (obscureItem[key] == -1 || obscureItem[key] === undefined) delete obscureItem[key]
          })

          return obscureItem
        }, []))
      }, []))
    }, [])
    const legacyResponse = [
      { fm_items: items },
      { seconds_ago: (Date.now() - mostRecentTimestamp) / 1000 }
    ]
    res.success(legacyResponse)
  }catch(ex){
    res.status(500).send({error: ex.message || ex, trace: ex.trace || null, stack: ex.stack || null})
    console.log(ex, ex.stack)
  }
})

API.registerCall(
  '/api/fm/world/:worldId/room/:roomId',
  'Gets a list of shops and items in a specific room on a specific world.',
  [
    API.createParameter(':worldId', 'number', 'The ID of the world. (0 = Scania, 1 = Windia, 2 = Bera, 3 = Khroa, 4 = MYBCKN, 5 = GRAZED)'),
    API.createParameter(':roomId', 'number', 'The ID of the room. (1-22)')
  ],
  {
      'shops': {
          'shopId': {
              'characterName': 'CharacterName',
              'id': 'shopId',
              'items': [
                  'itemObject'
              ]
          }
      }
  }
)
router.get('/world/:worldId/room/:roomId', async (req, res, next) => {
  try{
    var worldId = Number(req.params.worldId)
    var roomId = Number(req.params.roomId)
    const rooms = await Room.findRoom(worldId, 1, roomId)
    res.success(rooms)
  }catch(ex){
    res.status(500).send({error: ex.message || ex, trace: ex.trace || null, stack: ex.stack || null})
    console.log(ex, ex.stack)
  }
})

API.registerCall(
  '/api/fm/world/:worldId/room/:roomId/items',
  'Gets a list of items in a specific room on a specific world.',
  [
    API.createParameter(':worldId', 'number', 'The ID of the world. (0 = Scania, 1 = Windia, 2 = Bera, 3 = Khroa, 4 = MYBCKN, 5 = GRAZED)'),
    API.createParameter(':roomId', 'number', 'The ID of the room. (1-22)')
  ],
  [
    {
      cash: {cash:true},
      equip: {'accountSharable':null,'attack':null,'attackSpeed':null,'bdR':null,'bossReward':null,'charismaEXP':null,'charmEXP':null,'craftEXP':null,'durability':null,'equipTradeBlock':null,'exItem':null,'imdR':null,'incACC':null,'incCraft':null,'incDEX':null,'incEVA':null,'incINT':null,'incJump':null,'incLUK':null,'incMAD':null,'incMDD':null,'incMHP':null,'incMMP':null,'incPAD':null,'incPDD':null,'incSTR':null,'incSpeed':null,'islot':'MaPn','noPotential':null,'reqDEX':0,'reqINT':0,'reqJob':0,'reqJob2':null,'reqLUK':0,'reqLevel':0,'reqPOP':null,'reqSTR':0,'reqSpecJob':null,'senseEXP':null,'superiorEqp':null,'tradeAvailable':null,'tradeBlock':null,'tuc':null,'unchangeable':null,'vslot':'MaPn','willEXP':null},
      icon: {
        icon: 'base64/png',
        iconRaw: 'base64/png'
      },
      shop: {'monsterBook':false,'notSale':false,'price':100},
      chair: {'recoveryHP':100,'recoveryMP':50,'reqLevel':0},
      description: 'Some really awesome item',
      ItemId: 0,
      name: 'Pro Item',
      slot: 0,
      acc: 0,
      avoid: 0,
      battleModeAtt: 0,
      bossDmg: 0,
      bundle: 0,
      category: 0,
      channel: 0,
      characterName: 0,
      createdAt: 0,
      creator: 'SomeCoolGuy123456789',
      dex: 0,
      diligence: 0,
      durability: 0,
      expireTime: 0,
      growth: 0,
      hammerApplied: 0,
      ignoreDef: 0,
      intelligence: 0,
      isIdentified: 0,
      jump: 0,
      luk: 0,
      matk: 0,
      mdef: 0,
      mhp: 0,
      mmp: 0,
      nebulite: 0,
      numberOfEnhancements: 0,
      numberOfPlusses: 0,
      only: 0,
      potentials: [
        {
          'Message': '#prop% chance to ignore #ignoreDAM damage when attacked.',
          'Modifiers':[{'Item1':'prop','Item2':'30'},{'Item1':'ignoreDAM','Item2':'51'}],
          'OptionType':20,
          'id':20353,
          'target':'potential1',
          'line':'30% chance to ignore 51 damage when attacked.'
        }
      ],
      price: 0,
      quantity: 0,
      rarity: 0,
      room: 0,
      server: 0,
      shopID: 0,
      shopName: 0,
      speed: 0,
      str: 0,
      untradeable: 0,
      upgradesAvailable: 0,
      watk: 0,
      wdef: 0,
    }
  ]
)
router.get('/world/:worldId/room/:roomId/items', async (req, res, next) => {
  try{
    var worldId = Number(req.params.worldId)
    var roomId = Number(req.params.roomId)
    const items = await Item.findAll({room: roomId, server: worldId})
    res.success(items)
  }catch(ex){
    res.status(500).send({error: ex.message || ex, trace: ex.trace || null, stack: ex.stack || null})
    console.log(ex, ex.stack)
  }
})

API.registerCall(
  '/api/fm/world/:worldId/rooms/items',
  'Gets a list of items in a specific room on a specific world.',
  [
    API.createParameter(':worldId', 'number', 'The ID of the world. (0 = Scania, 1 = Windia, 2 = Bera, 3 = Khroa, 4 = MYBCKN, 5 = GRAZED)'),
    API.createParameter(':roomId', 'number', 'The ID of the room. (1-22)')
  ],
  [
    {
      cash: {cash:true},
      equip: {'accountSharable':null,'attack':null,'attackSpeed':null,'bdR':null,'bossReward':null,'charismaEXP':null,'charmEXP':null,'craftEXP':null,'durability':null,'equipTradeBlock':null,'exItem':null,'imdR':null,'incACC':null,'incCraft':null,'incDEX':null,'incEVA':null,'incINT':null,'incJump':null,'incLUK':null,'incMAD':null,'incMDD':null,'incMHP':null,'incMMP':null,'incPAD':null,'incPDD':null,'incSTR':null,'incSpeed':null,'islot':'MaPn','noPotential':null,'reqDEX':0,'reqINT':0,'reqJob':0,'reqJob2':null,'reqLUK':0,'reqLevel':0,'reqPOP':null,'reqSTR':0,'reqSpecJob':null,'senseEXP':null,'superiorEqp':null,'tradeAvailable':null,'tradeBlock':null,'tuc':null,'unchangeable':null,'vslot':'MaPn','willEXP':null},
      icon: {
        icon: 'base64/png',
        iconRaw: 'base64/png'
      },
      shop: {'monsterBook':false,'notSale':false,'price':100},
      chair: {'recoveryHP':100,'recoveryMP':50,'reqLevel':0},
      description: 'Some really awesome item',
      ItemId: 0,
      name: 'Pro Item',
      slot: 0,
      acc: 0,
      avoid: 0,
      battleModeAtt: 0,
      bossDmg: 0,
      bundle: 0,
      category: 0,
      channel: 0,
      characterName: 0,
      createdAt: 0,
      creator: 'SomeCoolGuy123456789',
      dex: 0,
      diligence: 0,
      durability: 0,
      expireTime: 0,
      growth: 0,
      hammerApplied: 0,
      ignoreDef: 0,
      intelligence: 0,
      isIdentified: 0,
      jump: 0,
      luk: 0,
      matk: 0,
      mdef: 0,
      mhp: 0,
      mmp: 0,
      nebulite: 0,
      numberOfEnhancements: 0,
      numberOfPlusses: 0,
      only: 0,
      potentials: [
        {
          'Message': '#prop% chance to ignore #ignoreDAM damage when attacked.',
          'Modifiers':[{'Item1':'prop','Item2':'30'},{'Item1':'ignoreDAM','Item2':'51'}],
          'OptionType':20,
          'id':20353,
          'target':'potential1',
          'line':'30% chance to ignore 51 damage when attacked.'
        }
      ],
      price: 0,
      quantity: 0,
      rarity: 0,
      room: 0,
      server: 0,
      shopID: 0,
      shopName: 0,
      speed: 0,
      str: 0,
      untradeable: 0,
      upgradesAvailable: 0,
      watk: 0,
      wdef: 0,
    }
  ]
)
router.get('/world/:worldId/rooms/items', async (req, res, next) => {
  try{
    var worldId = Number(req.params.worldId)
    var roomId = Number(req.params.roomId)
    const items = await Item.findAll({server: worldId})
    res.success(items)
  }catch(ex){
    res.status(500).send({error: ex.message || ex, trace: ex.trace || null, stack: ex.stack || null})
    console.log(ex, ex.stack)
  }
})

export default router<commit_msg>Add basic logging for determining if redis is enabled or not<commit_after>import express from 'express';
import r from 'rethinkdb'
import Promise from 'bluebird'
import Room from '../../models/room'
import Item from '../../models/item'
import API from '../../lib/API'
import rediscache from 'express-redis-cache'
import { ENV, PORT, DATADOG_API_KEY, DATADOG_APP_KEY, REDIS_HOST, REDIS_PORT } from '../../environment'

const router = express.Router();

if (REDIS_HOST && REDIS_PORT) {
  console.log('Redis enabled')
  const caching = rediscache({
    host: REDIS_HOST,
    port: REDIS_PORT,
    expire: 60
  })

  //Try to cache the results for at least 60 seconds as CPU is also costly
  router.use(caching.route())
} else {
  console.warn('Redis not enabled')
}

API.registerCall(
  '/api/fm/world/:worldId/rooms',
  'Gets a list of rooms with the shops and items in a world.',
  API.createParameter(':worldId', 'number', 'The ID of the world. (0 = Scania, 1 = Windia, 2 = Bera, 3 = Khroa, 4 = MYBCKN, 5 = GRAZED)'),
  [
    {
        'shops': {
            'shopId': {
                'characterName': 'CharacterName',
                'id': 'shopId',
                'items': [
                    'itemObject'
                ]
            }
        }
    }
  ]
)
router.get('/world/:worldId/rooms', async (req, res, next) => {
  try{
    var worldId = Number(req.params.worldId)
    const rooms = await Room.findRooms(worldId)
    res.success(rooms)
  }catch(ex){
    res.status(500).send({error: ex.message || ex, trace: ex.trace || null, stack: ex.stack || null})
    console.log(ex, ex.stack)
  }
})

API.registerCall(
  '/api/fm/world/:worldId/rooms/legacy',
  'Gets all of the items in the world.',
  API.createParameter(':worldId', 'number', 'The ID of the world. (0 = Scania, 1 = Windia, 2 = Bera, 3 = Khroa, 4 = MYBCKN, 5 = GRAZED)'),
  [{
    t: 'item.acc',
    u: 'item.avoid',
    B: 'item.battleModeAtt',
    C: 'item.bossDmg',
    b: 'item.bundle',
    Q: 'item.overallCategory',
    R: 'item.category',
    S: 'item.subCategory',
    E: 'item.creator',
    P: 'item.description',
    k: 'item.dex',
    v: 'item.diligence',
    y: 'item.growth',
    A: 'item.hammerApplied',
    T: 'item.itemId',
    D: 'item.ignoreDef',
    l: 'item.intelligence',
    F: 'item.isIdentified',
    x: 'item.jump',
    m: 'item.luk',
    q: 'item.matk',
    s: 'item.mdef',
    n: 'item.mhp',
    o: 'item.mmp',
    O: 'item.name',
    V: 'item.nebulite',
    H: 'item.numberOfEnhancements',
    i: 'item.numberOfPlusses',
    c: 'item.price',
    a: 'item.quantity',
    G: 'item.rarity',
    w: 'item.speed',
    j: 'item.str',
    h: 'item.upgradesAvailable',
    p: 'item.watk',
    r: 'item.wdef',
    U: 'item.itemId',
    d: 'room.channel',
    e: 'room.room',
    f: 'shop.shopName',
    g: 'shop.characterName'
  }]
)
router.get('/world/:worldId/rooms/legacy', async (req, res, next) => {
  try{
    var worldId = Number(req.params.worldId)
    const rooms = await Room.findRooms(worldId)
    var mostRecentTimestamp = 0
    const items = rooms.reduce(function (allItems, room) {
      mostRecentTimestamp = Math.max(mostRecentTimestamp, room.createdAt)
      if (!room.shops.length) return allItems
      return allItems.concat.apply(allItems, room.shops.reduce(function (roomItems, shop) {
        if (!shop.items.length) return roomItems
        return roomItems.concat.apply(roomItems, shop.items.map(function (item) {
          var obscureItem = {
            t: item.acc,
            u: item.avoid,
            B: item.battleModeAtt,
            C: item.bossDmg,
            b: item.bundle,
            Q: item.overallCategory,
            R: item.category,
            S: item.subCategory,
            E: item.creator,
            P: item.description,
            k: item.dex,
            v: item.diligence,
            y: item.growth,
            A: item.hammerApplied,
            T: item.itemId,
            D: item.ignoreDef,
            l: item.intelligence,
            F: item.isIdentified,
            x: item.jump,
            m: item.luk,
            q: item.matk,
            s: item.mdef,
            n: item.mhp,
            o: item.mmp,
            O: item.name,
            V: item.nebulite,
            H: item.numberOfEnhancements,
            i: item.numberOfPlusses,
            c: item.price,
            a: item.quantity,
            G: item.rarity,
            w: item.speed,
            j: item.str,
            h: item.upgradesAvailable,
            p: item.watk,
            r: item.wdef,
            U: item.itemId,
            d: room.channel,
            e: room.room,
            f: shop.shopName,
            g: shop.characterName,
            I: item.potential1 ? item.potential1.line : undefined,
            J: item.potential2 ? item.potential2.line : undefined,
            K: item.potential3 ? item.potential3.line : undefined,
            L: item.bpotential1 ? item.bpotential1.line : undefined,
            M: item.bpotential2 ? item.bpotential2.line : undefined,
            N: item.bpotential3 ? item.bpotential3.line : undefined,
            Y: item.isCash
          }

          Object.keys(obscureItem).forEach((key) => {
            if (obscureItem[key] == -1 || obscureItem[key] === undefined) delete obscureItem[key]
          })

          return obscureItem
        }, []))
      }, []))
    }, [])
    const legacyResponse = [
      { fm_items: items },
      { seconds_ago: (Date.now() - mostRecentTimestamp) / 1000 }
    ]
    res.success(legacyResponse)
  }catch(ex){
    res.status(500).send({error: ex.message || ex, trace: ex.trace || null, stack: ex.stack || null})
    console.log(ex, ex.stack)
  }
})

API.registerCall(
  '/api/fm/world/:worldId/room/:roomId',
  'Gets a list of shops and items in a specific room on a specific world.',
  [
    API.createParameter(':worldId', 'number', 'The ID of the world. (0 = Scania, 1 = Windia, 2 = Bera, 3 = Khroa, 4 = MYBCKN, 5 = GRAZED)'),
    API.createParameter(':roomId', 'number', 'The ID of the room. (1-22)')
  ],
  {
      'shops': {
          'shopId': {
              'characterName': 'CharacterName',
              'id': 'shopId',
              'items': [
                  'itemObject'
              ]
          }
      }
  }
)
router.get('/world/:worldId/room/:roomId', async (req, res, next) => {
  try{
    var worldId = Number(req.params.worldId)
    var roomId = Number(req.params.roomId)
    const rooms = await Room.findRoom(worldId, 1, roomId)
    res.success(rooms)
  }catch(ex){
    res.status(500).send({error: ex.message || ex, trace: ex.trace || null, stack: ex.stack || null})
    console.log(ex, ex.stack)
  }
})

API.registerCall(
  '/api/fm/world/:worldId/room/:roomId/items',
  'Gets a list of items in a specific room on a specific world.',
  [
    API.createParameter(':worldId', 'number', 'The ID of the world. (0 = Scania, 1 = Windia, 2 = Bera, 3 = Khroa, 4 = MYBCKN, 5 = GRAZED)'),
    API.createParameter(':roomId', 'number', 'The ID of the room. (1-22)')
  ],
  [
    {
      cash: {cash:true},
      equip: {'accountSharable':null,'attack':null,'attackSpeed':null,'bdR':null,'bossReward':null,'charismaEXP':null,'charmEXP':null,'craftEXP':null,'durability':null,'equipTradeBlock':null,'exItem':null,'imdR':null,'incACC':null,'incCraft':null,'incDEX':null,'incEVA':null,'incINT':null,'incJump':null,'incLUK':null,'incMAD':null,'incMDD':null,'incMHP':null,'incMMP':null,'incPAD':null,'incPDD':null,'incSTR':null,'incSpeed':null,'islot':'MaPn','noPotential':null,'reqDEX':0,'reqINT':0,'reqJob':0,'reqJob2':null,'reqLUK':0,'reqLevel':0,'reqPOP':null,'reqSTR':0,'reqSpecJob':null,'senseEXP':null,'superiorEqp':null,'tradeAvailable':null,'tradeBlock':null,'tuc':null,'unchangeable':null,'vslot':'MaPn','willEXP':null},
      icon: {
        icon: 'base64/png',
        iconRaw: 'base64/png'
      },
      shop: {'monsterBook':false,'notSale':false,'price':100},
      chair: {'recoveryHP':100,'recoveryMP':50,'reqLevel':0},
      description: 'Some really awesome item',
      ItemId: 0,
      name: 'Pro Item',
      slot: 0,
      acc: 0,
      avoid: 0,
      battleModeAtt: 0,
      bossDmg: 0,
      bundle: 0,
      category: 0,
      channel: 0,
      characterName: 0,
      createdAt: 0,
      creator: 'SomeCoolGuy123456789',
      dex: 0,
      diligence: 0,
      durability: 0,
      expireTime: 0,
      growth: 0,
      hammerApplied: 0,
      ignoreDef: 0,
      intelligence: 0,
      isIdentified: 0,
      jump: 0,
      luk: 0,
      matk: 0,
      mdef: 0,
      mhp: 0,
      mmp: 0,
      nebulite: 0,
      numberOfEnhancements: 0,
      numberOfPlusses: 0,
      only: 0,
      potentials: [
        {
          'Message': '#prop% chance to ignore #ignoreDAM damage when attacked.',
          'Modifiers':[{'Item1':'prop','Item2':'30'},{'Item1':'ignoreDAM','Item2':'51'}],
          'OptionType':20,
          'id':20353,
          'target':'potential1',
          'line':'30% chance to ignore 51 damage when attacked.'
        }
      ],
      price: 0,
      quantity: 0,
      rarity: 0,
      room: 0,
      server: 0,
      shopID: 0,
      shopName: 0,
      speed: 0,
      str: 0,
      untradeable: 0,
      upgradesAvailable: 0,
      watk: 0,
      wdef: 0,
    }
  ]
)
router.get('/world/:worldId/room/:roomId/items', async (req, res, next) => {
  try{
    var worldId = Number(req.params.worldId)
    var roomId = Number(req.params.roomId)
    const items = await Item.findAll({room: roomId, server: worldId})
    res.success(items)
  }catch(ex){
    res.status(500).send({error: ex.message || ex, trace: ex.trace || null, stack: ex.stack || null})
    console.log(ex, ex.stack)
  }
})

API.registerCall(
  '/api/fm/world/:worldId/rooms/items',
  'Gets a list of items in a specific room on a specific world.',
  [
    API.createParameter(':worldId', 'number', 'The ID of the world. (0 = Scania, 1 = Windia, 2 = Bera, 3 = Khroa, 4 = MYBCKN, 5 = GRAZED)'),
    API.createParameter(':roomId', 'number', 'The ID of the room. (1-22)')
  ],
  [
    {
      cash: {cash:true},
      equip: {'accountSharable':null,'attack':null,'attackSpeed':null,'bdR':null,'bossReward':null,'charismaEXP':null,'charmEXP':null,'craftEXP':null,'durability':null,'equipTradeBlock':null,'exItem':null,'imdR':null,'incACC':null,'incCraft':null,'incDEX':null,'incEVA':null,'incINT':null,'incJump':null,'incLUK':null,'incMAD':null,'incMDD':null,'incMHP':null,'incMMP':null,'incPAD':null,'incPDD':null,'incSTR':null,'incSpeed':null,'islot':'MaPn','noPotential':null,'reqDEX':0,'reqINT':0,'reqJob':0,'reqJob2':null,'reqLUK':0,'reqLevel':0,'reqPOP':null,'reqSTR':0,'reqSpecJob':null,'senseEXP':null,'superiorEqp':null,'tradeAvailable':null,'tradeBlock':null,'tuc':null,'unchangeable':null,'vslot':'MaPn','willEXP':null},
      icon: {
        icon: 'base64/png',
        iconRaw: 'base64/png'
      },
      shop: {'monsterBook':false,'notSale':false,'price':100},
      chair: {'recoveryHP':100,'recoveryMP':50,'reqLevel':0},
      description: 'Some really awesome item',
      ItemId: 0,
      name: 'Pro Item',
      slot: 0,
      acc: 0,
      avoid: 0,
      battleModeAtt: 0,
      bossDmg: 0,
      bundle: 0,
      category: 0,
      channel: 0,
      characterName: 0,
      createdAt: 0,
      creator: 'SomeCoolGuy123456789',
      dex: 0,
      diligence: 0,
      durability: 0,
      expireTime: 0,
      growth: 0,
      hammerApplied: 0,
      ignoreDef: 0,
      intelligence: 0,
      isIdentified: 0,
      jump: 0,
      luk: 0,
      matk: 0,
      mdef: 0,
      mhp: 0,
      mmp: 0,
      nebulite: 0,
      numberOfEnhancements: 0,
      numberOfPlusses: 0,
      only: 0,
      potentials: [
        {
          'Message': '#prop% chance to ignore #ignoreDAM damage when attacked.',
          'Modifiers':[{'Item1':'prop','Item2':'30'},{'Item1':'ignoreDAM','Item2':'51'}],
          'OptionType':20,
          'id':20353,
          'target':'potential1',
          'line':'30% chance to ignore 51 damage when attacked.'
        }
      ],
      price: 0,
      quantity: 0,
      rarity: 0,
      room: 0,
      server: 0,
      shopID: 0,
      shopName: 0,
      speed: 0,
      str: 0,
      untradeable: 0,
      upgradesAvailable: 0,
      watk: 0,
      wdef: 0,
    }
  ]
)
router.get('/world/:worldId/rooms/items', async (req, res, next) => {
  try{
    var worldId = Number(req.params.worldId)
    var roomId = Number(req.params.roomId)
    const items = await Item.findAll({server: worldId})
    res.success(items)
  }catch(ex){
    res.status(500).send({error: ex.message || ex, trace: ex.trace || null, stack: ex.stack || null})
    console.log(ex, ex.stack)
  }
})

export default router<|endoftext|>


<commit_before>fun.views.profile = Backbone.View.extend({

    /**
    * Bind the event functions to the different HTML elements
    */
    events: {
        'click #profile-new-contact': 'newContact',
        'click #act_1': 'oneDay',
        'click #act_2': 'threeDays',
        'click #act_3': 'oneWeek',
        'click #act_4': 'oneMonth',
        'change #profile-contact-modal-mailing-address-different': 'showMailingAddressDifferent',
        'change #profile-contact-modal-marital-status': 'changeMaritalStatus',
        'change #profile-contact-modal-agent-home-insurance-checkbox': 'homeInsuranceTab',
        'change #profile-contact-modal-agent-health-insurance-checkbox': 'healthInsuranceTab',
        'change #profile-contact-modal-agent-auto-insurance-checkbox': 'autoInsuranceTab',
        'change #profile-contact-modal-agent-life-insurance-checkbox': 'lifeInsuranceTab',
        'change #profile-contact-modal-agent-ancilliary-insurance-checkbox': 'ancilliaryInsuranceTab',
        'change #profile-contact-modal-agent-number-of-children': 'changeNumberChildren',
        'change #profile-contact-modal-health-lead-status': 'showPaymentTab',
        'change #profile-contact-modal-home-lead-status': 'showPaymentTab',
        'change #profile-contact-modal-auto-lead-status': 'showPaymentTab',
        'change #profile-contact-modal-life-lead-status': 'showPaymentTab',
        'change #profile-contact-modal-ancilliary-lead-status': 'showPaymentTab'
    },

    /**
    * Class constructor
    */
    initialize: function(options){
        fun.containers.profile = this.$el;
    },

    /**
    * Render view
    */
    render: function(){
        console.log('render profile view');
        var template,
            startDate,
            startTimestamp,
            i,
            total,
            results;

        template = _.template(fun.utils.getTemplate(fun.conf.templates.profile));

        this.$el.html(template);
        this.$el.removeClass("hide").addClass("show");

        startDate = new Date(2013, 6, 25);
        startTimestamp = new Date(2013, 6, 1).getTime() / 1000;

        options = {
            events_source: '/stochastics/',
            view: 'day',
            tmpl_path: '/static/tmpls/',
            tmpl_cache: false,
            // day: '2013-03-12',
            onAfterEventsLoad: function(events) {
                if(!events) {
                    return;
                }
                list = $('#eventlist');
                list.html('');

                $.each(events, function(key, val) {
                    $(document.createElement('li'))
                        .html('' + val.title + '')
                        .appendTo(list);
                });
            },
            onAfterViewLoad: function(view) {
                $('.page-header h3').text(this.getTitle());
                $('.btn-group button').removeClass('active');
                $('button[data-calendar-view="' + view + '"]').addClass('active');
            },
            classes: {
                months: {
                    general: 'label'
                }
            }
        };

        calendar = $('#test-calendar').calendar(options);

        $('.btn-group button[data-calendar-nav]').each(function() {
            var $this = $(this);
            $this.click(function() {
                calendar.navigate($this.data('calendar-nav'));
            });
        });

        $('.btn-group button[data-calendar-view]').each(function() {
            var $this = $(this);
            $this.click(function() {
                calendar.view($this.data('calendar-view'));
            });
        });

        function GAconverter(data) {
            var i, total, results = {};
            for(i = 0, total = data.length; i < total; i++) {
                results[+data[i].Hour * 3600 + startTimestamp] = +data[i].Visits;
            }
            return results;
        }
    },

    renderCampaignInboundList: function(campaigns){
        console.log('render this campaign inbound list');
    },

    renderCampaignOutboundList: function(campaigns){
        console.log('render this campaigns outbound list');
    },

    renderTaskList: function(tasks){
        console.log('render profile tasks list');
    },

    oneDay: function(event){
        console.log('one day event');
    },

    threeDays: function(event){
        console.log('three days event');
    },

    oneWeek: function(event){
        console.log('one week event');
    },

    oneMonth: function(event){
        console.log('one month event');
    },

    newContact: function(event){
        console.log('new contact and stuff get the popup in here');
        'use strict';
        event.preventDefault();

        //view cache
        var view = this,
            name;

        $('#profileContactModal').modal({
            'show': true
        });

        name = $(event.target).data('name');

        task = new fun.models.Task({'uuid':name});

        task.fetch({
            success: function(response){

                //console.log(response)

                taskUuid.html(response.get('uuid'));
                taskTitle.html(response.get('title') || "Where's the title boy?");
                taskAssigned.html(response.get('assigned'));
                taskLabel.html(response.get('label'));
                taskSource.html(response.get('source'));
                taskStatus.html(response.get('status'));
                taskPriority.html(response.get('priority'));
                taskSeverity.html(response.get('severity'));
                taskDescription.html(response.get('description'));

                $('#taskModal').modal({
                    'show': true
                });
            },
            error: function(error){
                console.log(error);
            }
        });
    },

    showMailingAddressDifferent: function(event){
        var value = $('#mailing-address-different').val();
        if(value===true||value==='true'){
            $('#mailingAddressDifferentDiv').removeClass('hide');
            $('#mailingAddressDifferentDiv').addClass('show');
        } else {
            $('#mailingAddressDifferentDiv').removeClass('show');
            $('#mailingAddressDifferentDiv').addClass('hide');
        }
    },

    changeMaritalStatus: function(event){

        if($('#marital-status').val()==='none'||$('#marital-status').val()==='single'){
            $('#contactSpouseInfoTab').removeClass('show');
            $('#contactSpouseInfoTab').addClass('hide');
        } else {
            $('#contactSpouseInfoTab').removeClass('hide');
            $('#contactSpouseInfoTab').addClass('show');
        }
    },

    healthInsuranceTab: function(event){
        console.log('IN HEALTH INSURANCE!!!',$('#agent-health-insurance-checkbox').val());
        if($('#agent-health-insurance-checkbox').val()==="true"){
            $('#healthInsuranceTab').removeClass('hide');
            $('#healthInsuranceTab').addClass('show');
        } else {
            $('#healthInsuranceTab').removeClass('show');
            $('#healthInsuranceTab').addClass('hide');
        }
    },

    homeInsuranceTab: function(event){
        if($('#agent-home-insurance-checkbox').val()==="true"){
            $('#homeOwnersInsuranceTab').removeClass('hide');
            $('#homeOwnersInsuranceTab').addClass('show');
        } else {
            $('#homeOwnersInsuranceTab').removeClass('show');
            $('#homeOwnersInsuranceTab').addClass('hide');
        }
    },

    autoInsuranceTab: function(event){
        if($('#agent-auto-insurance-checkbox').val()==="true"){
            $('#automobileInsuranceTab').removeClass('hide');
            $('#automobileInsuranceTab').addClass('show');
        } else {
            $('#automobileInsuranceTab').removeClass('show');
            $('#automobileInsuranceTab').addClass('hide');
        }
    },

    lifeInsuranceTab: function(event){
        if($('#agent-life-insurance-checkbox').val()==="true"){
            $('#lifeInsuranceTab').removeClass('hide');
            $('#lifeInsuranceTab').addClass('show');
        } else {
            $('#lifeInsuranceTab').removeClass('show');
            $('#lifeInsuranceTab').addClass('hide');
        }
    },

    ancilliaryInsuranceTab: function(event){
        if($('#agent-ancilliary-insurance-checkbox').val()==="true"){
            $('#ancilliaryInsuranceTab').removeClass('hide');
            $('#ancilliaryInsuranceTab').addClass('show');
        } else {
            $('#ancilliaryInsuranceTab').removeClass('show');
            $('#ancilliaryInsuranceTab').addClass('hide');
        }
    },

    changeNumberChildren: function(event){
        switch($('#agent-number-of-children').val()){

            case '0':
                $('#childrenInfoTab').removeClass('show');
                $('#childrenInfoTab').addClass('hide');
                $('#childrenInfoGroup-1').removeClass('show');
                $('#childrenInfoGroup-1').addClass('hide');
                $('#childrenInfoGroup-2').removeClass('show');
                $('#childrenInfoGroup-2').addClass('hide');
                $('#childrenInfoGroup-3').removeClass('show');
                $('#childrenInfoGroup-3').addClass('hide');
                $('#childrenInfoGroup-4').removeClass('show');
                $('#childrenInfoGroup-4').addClass('hide');
                break;

            case '1':
                $('#childrenInfoTab').removeClass('hide');
                $('#childrenInfoTab').addClass('show');
                $('#childrenInfoGroup-1').removeClass('hide');
                $('#childrenInfoGroup-1').addClass('show');
                break;

            case '2':
                $('#childrenInfoTab').removeClass('hide');
                $('#childrenInfoTab').addClass('show');
                $('#childrenInfoGroup-2').removeClass('hide');
                $('#childrenInfoGroup-2').addClass('show');
                break;

            case '3':
                $('#childrenInfoTab').removeClass('hide');
                $('#childrenInfoTab').addClass('show');
                $('#childrenInfoGroup-3').removeClass('hide');
                $('#childrenInfoGroup-3').addClass('show');
                break;

            case '4':
                $('#childrenInfoTab').removeClass('hide');
                $('#childrenInfoTab').addClass('show');
                $('#childrenInfoGroup-4').removeClass('hide');
                $('#childrenInfoGroup-4').addClass('show');
                break;
        }
    },

    showPaymentTab: function(event){
        if(event.currentTarget.value === 'sold'){
            $('#paymentInfoTab').removeClass('hide');
            $('#paymentInfoTab').addClass('show');
        } else {
            $('#paymentInfoTab').removeClass('show');
            $('#paymentInfoTab').addClass('hide');
        }
    }
});<commit_msg>ok go<commit_after>fun.views.profile = Backbone.View.extend({

    /**
    * Bind the event functions to the different HTML elements
    */
    events: {
        'click #profile-new-contact': 'newContact',
        'click #act_1': 'oneDay',
        'click #act_2': 'threeDays',
        'click #act_3': 'oneWeek',
        'click #act_4': 'oneMonth',
        'change #profile-contact-modal-mailing-address-different': 'showMailingAddressDifferent',
        'change #profile-contact-modal-marital-status': 'changeMaritalStatus',
        'change #profile-contact-modal-agent-home-insurance-checkbox': 'homeInsuranceTab',
        'change #profile-contact-modal-agent-health-insurance-checkbox': 'healthInsuranceTab',
        'change #profile-contact-modal-agent-auto-insurance-checkbox': 'autoInsuranceTab',
        'change #profile-contact-modal-agent-life-insurance-checkbox': 'lifeInsuranceTab',
        'change #profile-contact-modal-agent-ancilliary-insurance-checkbox': 'ancilliaryInsuranceTab',
        'change #profile-contact-modal-agent-number-of-children': 'changeNumberChildren',
        'change #profile-contact-modal-health-lead-status': 'showPaymentTab',
        'change #profile-contact-modal-home-lead-status': 'showPaymentTab',
        'change #profile-contact-modal-auto-lead-status': 'showPaymentTab',
        'change #profile-contact-modal-life-lead-status': 'showPaymentTab',
        'change #profile-contact-modal-ancilliary-lead-status': 'showPaymentTab'
    },

    /**
    * Class constructor
    */
    initialize: function(options){
        fun.containers.profile = this.$el;
    },

    /**
    * Render view
    */
    render: function(){
        console.log('render profile view');
        var template,
            startDate,
            startTimestamp,
            i,
            total,
            results;

        template = _.template(fun.utils.getTemplate(fun.conf.templates.profile));

        this.$el.html(template);
        this.$el.removeClass("hide").addClass("show");

        startDate = new Date(2013, 6, 25);
        startTimestamp = new Date(2013, 6, 1).getTime() / 1000;

        options = {
            events_source: '/stochastics/',
            view: 'day',
            tmpl_path: '/static/tmpls/',
            tmpl_cache: false,
            // day: '2013-03-12',
            onAfterEventsLoad: function(events) {
                if(!events) {
                    return;
                }
                list = $('#eventlist');
                list.html('');

                $.each(events, function(key, val) {
                    $(document.createElement('li'))
                        .html('' + val.title + '')
                        .appendTo(list);
                });
            },
            onAfterViewLoad: function(view) {
                $('.page-header h3').text(this.getTitle());
                $('.btn-group button').removeClass('active');
                $('button[data-calendar-view="' + view + '"]').addClass('active');
            },
            classes: {
                months: {
                    general: 'label'
                }
            }
        };

        calendar = $('#test-calendar').calendar(options);

        $('.btn-group button[data-calendar-nav]').each(function() {
            var $this = $(this);
            $this.click(function() {
                calendar.navigate($this.data('calendar-nav'));
            });
        });

        $('.btn-group button[data-calendar-view]').each(function() {
            var $this = $(this);
            $this.click(function() {
                calendar.view($this.data('calendar-view'));
            });
        });

        function GAconverter(data) {
            var i, total, results = {};
            for(i = 0, total = data.length; i < total; i++) {
                results[+data[i].Hour * 3600 + startTimestamp] = +data[i].Visits;
            }
            return results;
        }
    },

    renderCampaignInboundList: function(campaigns){
        console.log('render this campaign inbound list');
    },

    renderCampaignOutboundList: function(campaigns){
        console.log('render this campaigns outbound list');
    },

    renderTaskList: function(tasks){
        console.log('render profile tasks list');
    },

    renderContactList: function(contacts){
        console.log('render profile contacts list');
    },

    oneDay: function(event){
        console.log('one day event');
    },

    threeDays: function(event){
        console.log('three days event');
    },

    oneWeek: function(event){
        console.log('one week event');
    },

    oneMonth: function(event){
        console.log('one month event');
    },

    newContact: function(event){
        console.log('new contact and stuff get the popup in here');
        'use strict';
        event.preventDefault();

        //view cache
        var view = this,
            name;

        $('#profileContactModal').modal({
            'show': true
        });

        name = $(event.target).data('name');

        task = new fun.models.Task({'uuid':name});

        task.fetch({
            success: function(response){

                //console.log(response)

                taskUuid.html(response.get('uuid'));
                taskTitle.html(response.get('title') || "Where's the title boy?");
                taskAssigned.html(response.get('assigned'));
                taskLabel.html(response.get('label'));
                taskSource.html(response.get('source'));
                taskStatus.html(response.get('status'));
                taskPriority.html(response.get('priority'));
                taskSeverity.html(response.get('severity'));
                taskDescription.html(response.get('description'));

                $('#taskModal').modal({
                    'show': true
                });
            },
            error: function(error){
                console.log(error);
            }
        });
    },

    showMailingAddressDifferent: function(event){
        var value = $('#mailing-address-different').val();
        if(value===true||value==='true'){
            $('#mailingAddressDifferentDiv').removeClass('hide');
            $('#mailingAddressDifferentDiv').addClass('show');
        } else {
            $('#mailingAddressDifferentDiv').removeClass('show');
            $('#mailingAddressDifferentDiv').addClass('hide');
        }
    },

    changeMaritalStatus: function(event){

        if($('#marital-status').val()==='none'||$('#marital-status').val()==='single'){
            $('#contactSpouseInfoTab').removeClass('show');
            $('#contactSpouseInfoTab').addClass('hide');
        } else {
            $('#contactSpouseInfoTab').removeClass('hide');
            $('#contactSpouseInfoTab').addClass('show');
        }
    },

    healthInsuranceTab: function(event){
        console.log('IN HEALTH INSURANCE!!!',$('#agent-health-insurance-checkbox').val());
        if($('#agent-health-insurance-checkbox').val()==="true"){
            $('#healthInsuranceTab').removeClass('hide');
            $('#healthInsuranceTab').addClass('show');
        } else {
            $('#healthInsuranceTab').removeClass('show');
            $('#healthInsuranceTab').addClass('hide');
        }
    },

    homeInsuranceTab: function(event){
        if($('#agent-home-insurance-checkbox').val()==="true"){
            $('#homeOwnersInsuranceTab').removeClass('hide');
            $('#homeOwnersInsuranceTab').addClass('show');
        } else {
            $('#homeOwnersInsuranceTab').removeClass('show');
            $('#homeOwnersInsuranceTab').addClass('hide');
        }
    },

    autoInsuranceTab: function(event){
        if($('#agent-auto-insurance-checkbox').val()==="true"){
            $('#automobileInsuranceTab').removeClass('hide');
            $('#automobileInsuranceTab').addClass('show');
        } else {
            $('#automobileInsuranceTab').removeClass('show');
            $('#automobileInsuranceTab').addClass('hide');
        }
    },

    lifeInsuranceTab: function(event){
        if($('#agent-life-insurance-checkbox').val()==="true"){
            $('#lifeInsuranceTab').removeClass('hide');
            $('#lifeInsuranceTab').addClass('show');
        } else {
            $('#lifeInsuranceTab').removeClass('show');
            $('#lifeInsuranceTab').addClass('hide');
        }
    },

    ancilliaryInsuranceTab: function(event){
        if($('#agent-ancilliary-insurance-checkbox').val()==="true"){
            $('#ancilliaryInsuranceTab').removeClass('hide');
            $('#ancilliaryInsuranceTab').addClass('show');
        } else {
            $('#ancilliaryInsuranceTab').removeClass('show');
            $('#ancilliaryInsuranceTab').addClass('hide');
        }
    },

    changeNumberChildren: function(event){
        switch($('#agent-number-of-children').val()){

            case '0':
                $('#childrenInfoTab').removeClass('show');
                $('#childrenInfoTab').addClass('hide');
                $('#childrenInfoGroup-1').removeClass('show');
                $('#childrenInfoGroup-1').addClass('hide');
                $('#childrenInfoGroup-2').removeClass('show');
                $('#childrenInfoGroup-2').addClass('hide');
                $('#childrenInfoGroup-3').removeClass('show');
                $('#childrenInfoGroup-3').addClass('hide');
                $('#childrenInfoGroup-4').removeClass('show');
                $('#childrenInfoGroup-4').addClass('hide');
                break;

            case '1':
                $('#childrenInfoTab').removeClass('hide');
                $('#childrenInfoTab').addClass('show');
                $('#childrenInfoGroup-1').removeClass('hide');
                $('#childrenInfoGroup-1').addClass('show');
                break;

            case '2':
                $('#childrenInfoTab').removeClass('hide');
                $('#childrenInfoTab').addClass('show');
                $('#childrenInfoGroup-2').removeClass('hide');
                $('#childrenInfoGroup-2').addClass('show');
                break;

            case '3':
                $('#childrenInfoTab').removeClass('hide');
                $('#childrenInfoTab').addClass('show');
                $('#childrenInfoGroup-3').removeClass('hide');
                $('#childrenInfoGroup-3').addClass('show');
                break;

            case '4':
                $('#childrenInfoTab').removeClass('hide');
                $('#childrenInfoTab').addClass('show');
                $('#childrenInfoGroup-4').removeClass('hide');
                $('#childrenInfoGroup-4').addClass('show');
                break;
        }
    },

    showPaymentTab: function(event){
        if(event.currentTarget.value === 'sold'){
            $('#paymentInfoTab').removeClass('hide');
            $('#paymentInfoTab').addClass('show');
        } else {
            $('#paymentInfoTab').removeClass('show');
            $('#paymentInfoTab').addClass('hide');
        }
    }
});<|endoftext|>


<commit_before>dojo.provide("dijit._Widget");

dojo.require("dijit._base");

dojo.declare("dijit._Widget", null, {
	// id: String
	//		a unique, opaque ID string that can be assigned by users or by the
	//		system. If the developer passes an ID which is known not to be
	//		unique, the specified ID is ignored and the system-generated ID is
	//		used instead.
	id: "",

	// lang: String
	//	Language to display this widget in (like en-us).
	//	Defaults to brower's specified preferred language (typically the language of the OS)
	lang: "",

	// dir: String
	//  Bi-directional support, as defined by the HTML DIR attribute. Either left-to-right "ltr" or right-to-left "rtl".
	dir: "",

	// class: String
	// HTML class attribute
	"class": "",

	// style: String
	// HTML style attribute
	style: "",

	// title: String
	// HTML title attribute
	title: "",

	// srcNodeRef: DomNode
	//		pointer to original dom node
	srcNodeRef: null,

	// domNode: DomNode
	//		this is our visible representation of the widget! Other DOM
	//		Nodes may by assigned to other properties, usually through the
	//		template system's dojoAttachPonit syntax, but the domNode
	//		property is the canonical "top level" node in widget UI.
	domNode: null,

	// attributeMap: Object
	//		A map of attributes -- typically standard HTML attributes -- to transfer
	//		from the parsed node into the new dom, at the widget's domNode, by default.
	//		Other node references can be specified as properties of 'this'
	attributeMap: {id:"", dir:"", lang:"", "class":"", style:"", title:""},  // TODO: add on* handlers?

	//////////// INITIALIZATION METHODS ///////////////////////////////////////

	postscript: function(params, srcNodeRef){
		this.create(params, srcNodeRef);
	},

	create: function(params, srcNodeRef){
		// summary:
		//		To understand the process by which widgets are instantiated, it
		//		is critical to understand what other methods create calls and
		//		which of them you'll want to override. Of course, adventurous
		//		developers could override create entirely, but this should
		//		only be done as a last resort.
		//
		//		Below is a list of the methods that are called, in the order
		//		they are fired, along with notes about what they do and if/when
		//		you should over-ride them in your widget:
		//
		//			postMixInProperties:
		//				a stub function that you can over-ride to modify
		//				variables that may have been naively assigned by
		//				mixInProperties
		//			# widget is added to manager object here
		//			buildRendering
		//				Subclasses use this method to handle all UI initialization
		//				Sets this.domNode.  Templated widgets do this automatically
		//				and otherwise it just uses the source dom node.
		//			postCreate
		//				a stub function that you can over-ride to modify take
		//				actions once the widget has been placed in the UI

		// store pointer to original dom tree
		this.srcNodeRef = dojo.byId(srcNodeRef);

		// For garbage collection.  An array of handles returned by Widget.connect()
		// Each handle returned from Widget.connect() is an array of handles from dojo.connect()
		this._connects=[];

		// _attaches: String[]
		// 		names of all our dojoAttachPoint variables
		this._attaches=[];

		//mixin our passed parameters
		if(this.srcNodeRef && (typeof this.srcNodeRef.id == "string")){ this.id = this.srcNodeRef.id; }
		if(params){
			dojo.mixin(this,params);
		}
		this.postMixInProperties();

		// generate an id for the widget if one wasn't specified
		// (be sure to do this before buildRendering() because that function might
		// expect the id to be there.
		if(!this.id){
			this.id=dijit.getUniqueId(this.declaredClass.replace(/\./g,"_"));
		}
		dijit.registry.add(this);

		this.buildRendering();

		// Copy attributes listed in attributeMap into the newly created DOM for the widget
		// The placement of these attributes is according to the property mapping in attributeMap
		// Note special handling for 'style' and 'class' attributes which are lists and can
		// have elements from both old and new structures, and some attributes like "type"
		// cannot be processed this way as they are not mutable.

//KLUDGE: skip the widgets which aren't yet working.  See #3058
if(!/dijit\.form\.(Slider|Textarea|ComboBox|FilteringSelect)/.test(this.declaredClass))

		for(var attr in this.attributeMap){
			if(this.domNode){
				var node = this[this.attributeMap[attr] || "domNode"];
				var value = this[attr];
				if(value !== "" || (params && params[attr])){
					var domValue = node.getAttribute(attr);
					// Deal with IE quirks for 'class' and 'style'
					switch(attr){
					case "class":
						domValue = node.className;
						break;
					case "style":
						domValue = domValue.cssText; // FIXME: Opera
					}
					if(domValue){
						var delim = {style: ";", "class": " "}[attr];
						// style and class attributes are special and contain lists
						// which need to be combined
						if(delim && value){
							value += delim + domValue;
						}else{
							value = domValue;
						}
					}

					// Deal with IE quirks for 'class' and 'style'
					switch(attr){
					case "class":
						node.className = value;
						break;
					case "style":
						if(node.style && dojo.isObject(node.style)){
							node.style.cssText = value; // required for IE, doesn't work in Opera
							break;
						}
						// fallthrough...
					default:
						node.setAttribute(attr, value);
					}
				}
			}
		}

		if(this.domNode){
			this.domNode.setAttribute("widgetId", this.id);
		}
		this.postCreate();

		// If srcNodeRef has been processed and removed from the DOM (e.g. TemplatedWidget) then delete it to allow GC.
		if(this.srcNodeRef && !this.srcNodeRef.parentNode){
			delete this.srcNodeRef;
		}
	},

	postMixInProperties: function(){
		// summary
		//	Called after the parameters to the widget have been read-in,
		//	but before the widget template is instantiated.
		//	Especially useful to set properties that are referenced in the widget template.
	},

	buildRendering: function(){
		// summary:
		//		Construct the UI for this widget, setting this.domNode.
		//		Most widgets will mixin TemplatedWidget, which overrides this method.
		this.domNode = this.srcNodeRef;
	},

	postCreate: function(){
		// summary:
		//		Called after a widget's dom has been setup
	},

	startup: function(){
		// summary:
		//		Called after a widget's children, and other widgets on the page, have been created.
		//		Provides an opportunity to manipulate any children before they are displayed
		//		This is useful for composite widgets that need to control or layout sub-widgets
		//		Many layout widgets can use this as a wiring phase
	},

	//////////// DESTROY FUNCTIONS ////////////////////////////////

	destroyRecursive: function(/*Boolean*/ finalize){
		// summary:
		// 		Destroy this widget and it's descendants. This is the generic
		// 		"destructor" function that all widget users should call to
		// 		cleanly discard with a widget. Once a widget is destroyed, it's
		// 		removed from the manager object.
		// finalize: Boolean
		//		is this function being called part of global environment
		//		tear-down?

		this.destroyDescendants();
		this.destroy();
	},

	destroy: function(/*Boolean*/ finalize){
		// summary:
		// 		Destroy this widget, but not its descendants
		// finalize: Boolean
		//		is this function being called part of global environment
		//		tear-down?
		this.uninitialize();
		dojo.forEach(this._connects, function(array){
			dojo.forEach(array, dojo.disconnect);
		});
		this.destroyRendering(finalize);
		dijit.registry.remove(this.id);
	},

	destroyRendering: function(/*Boolean*/ finalize){
		// summary:
		//		Destroys the DOM nodes associated with this widget
		// finalize: Boolean
		//		is this function being called part of global environment
		//		tear-down?

		if(this.bgIframe){
			this.bgIframe.destroy();
			delete this.bgIframe;
		}

		if(this.domNode){
			dojo._destroyElement(this.domNode);
			delete this.domNode;
		}

		if(this.srcNodeRef){
			dojo._destroyElement(this.srcNodeRef);
			delete this.srcNodeRef;
		}
	},

	destroyDescendants: function(){
		// summary:
		//		Recursively destroy the children of this widget and their
		//		descendants.

		// TODO: should I destroy in the reverse order, to go bottom up?
		dojo.forEach(this.getDescendants(), function(widget){ widget.destroy(); });
	},

	uninitialize: function(){
		// summary:
		//		stub function. Over-ride to implement custom widget tear-down
		//		behavior.
		return false;
	},

	////////////////// MISCELLANEOUS METHODS ///////////////////

	toString: function(){
		// summary:
		//		returns a string that represents the widget. When a widget is
		//		cast to a string, this method will be used to generate the
		//		output. Currently, it does not implement any sort of reversable
		//		serialization.
		return '[Widget ' + this.declaredClass + ', ' + (this.id || 'NO ID') + ']'; // String
	},

	getDescendants: function(){
		// summary:
		//	return all the descendant widgets
		var list = dojo.query('[widgetId]', this.domNode);
		return list.map(dijit.byNode);		// Array
	},

	nodesWithKeyClick : ["input", "button"],

	connect: function(
			/*Object|null*/ obj,
			/*String*/ event,
			/*String|Function*/ method){

		// summary:
		//		Connects specified obj/event to specified method of this object
		//		and registers for disconnect() on widget destroy.
		//		Special event: "ondijitclick" triggers on a click or enter-down or space-up
		//		Similar to dojo.connect() but takes three arguments rather than four.
		var handles =[];
		if(event == "ondijitclick"){
			var w = this;
			// add key based click activation for unsupported nodes.
			if(!this.nodesWithKeyClick[obj.nodeName]){
				handles.push(dojo.connect(obj, "onkeydown", this,
					function(e){
						if(e.keyCode == dojo.keys.ENTER){
							return (dojo.isString(method))?
								w[method](e) : method.call(w, e);
						}else if(e.keyCode == dojo.keys.SPACE){
							// stop space down as it causes IE to scroll
							// the browser window
							dojo.stopEvent(e);
						}
			 		}));
				handles.push(dojo.connect(obj, "onkeyup", this,
					function(e){
						if(e.keyCode == dojo.keys.SPACE){
							return dojo.isString(method) ?
								w[method](e) : method.call(w, e);
						}
			 		}));
			}
			event = "onclick";
		}
		handles.push(dojo.connect(obj, event, this, method));

		// return handles for FormElement and ComboBox
		this._connects.push(handles);
		return handles;
	},

	disconnect: function(/*Object*/ handles){
		// summary:
		//		Disconnects handle created by this.connect.
		//		Also removes handle from this widget's list of connects
		for(var i=0; i<this._connects.length; i++){
			if(this._connects[i]==handles){
				dojo.forEach(handles, dojo.disconnect);
				this._connects.splice(i, 1);
				return;
			}
		}
	},

	isLeftToRight: function(){
		// summary:
		//		Checks the DOM to for the text direction for bi-directional support
		// description:
		//		This method cannot be used during widget construction because the widget
		//		must first be connected to the DOM tree.  Parent nodes are searched for the
		//		'dir' attribute until one is found, otherwise left to right mode is assumed.
		//		See HTML spec, DIR attribute for more information.

		if(typeof this._ltr == "undefined"){
			this._ltr = dojo.getComputedStyle(this.domNode).direction != "rtl";
		}
		return this._ltr; //Boolean
	}
});
<commit_msg>fix typo, update attributeMap comment.  Refs #3058<commit_after>dojo.provide("dijit._Widget");

dojo.require("dijit._base");

dojo.declare("dijit._Widget", null, {
	// id: String
	//		a unique, opaque ID string that can be assigned by users or by the
	//		system. If the developer passes an ID which is known not to be
	//		unique, the specified ID is ignored and the system-generated ID is
	//		used instead.
	id: "",

	// lang: String
	//	Language to display this widget in (like en-us).
	//	Defaults to brower's specified preferred language (typically the language of the OS)
	lang: "",

	// dir: String
	//  Bi-directional support, as defined by the HTML DIR attribute. Either left-to-right "ltr" or right-to-left "rtl".
	dir: "",

	// class: String
	// HTML class attribute
	"class": "",

	// style: String
	// HTML style attribute
	style: "",

	// title: String
	// HTML title attribute
	title: "",

	// srcNodeRef: DomNode
	//		pointer to original dom node
	srcNodeRef: null,

	// domNode: DomNode
	//		this is our visible representation of the widget! Other DOM
	//		Nodes may by assigned to other properties, usually through the
	//		template system's dojoAttachPonit syntax, but the domNode
	//		property is the canonical "top level" node in widget UI.
	domNode: null,

	// attributeMap: Object
	//		A map of attributes and attachpoints -- typically standard HTML attributes -- to set
	//		on the widget's dom, at the "domNode" attach point, by default.
	//		Other node references can be specified as properties of 'this'
	attributeMap: {id:"", dir:"", lang:"", "class":"", style:"", title:""},  // TODO: add on* handlers?

	//////////// INITIALIZATION METHODS ///////////////////////////////////////

	postscript: function(params, srcNodeRef){
		this.create(params, srcNodeRef);
	},

	create: function(params, srcNodeRef){
		// summary:
		//		To understand the process by which widgets are instantiated, it
		//		is critical to understand what other methods create calls and
		//		which of them you'll want to override. Of course, adventurous
		//		developers could override create entirely, but this should
		//		only be done as a last resort.
		//
		//		Below is a list of the methods that are called, in the order
		//		they are fired, along with notes about what they do and if/when
		//		you should over-ride them in your widget:
		//
		//			postMixInProperties:
		//				a stub function that you can over-ride to modify
		//				variables that may have been naively assigned by
		//				mixInProperties
		//			# widget is added to manager object here
		//			buildRendering
		//				Subclasses use this method to handle all UI initialization
		//				Sets this.domNode.  Templated widgets do this automatically
		//				and otherwise it just uses the source dom node.
		//			postCreate
		//				a stub function that you can over-ride to modify take
		//				actions once the widget has been placed in the UI

		// store pointer to original dom tree
		this.srcNodeRef = dojo.byId(srcNodeRef);

		// For garbage collection.  An array of handles returned by Widget.connect()
		// Each handle returned from Widget.connect() is an array of handles from dojo.connect()
		this._connects=[];

		// _attaches: String[]
		// 		names of all our dojoAttachPoint variables
		this._attaches=[];

		//mixin our passed parameters
		if(this.srcNodeRef && (typeof this.srcNodeRef.id == "string")){ this.id = this.srcNodeRef.id; }
		if(params){
			dojo.mixin(this,params);
		}
		this.postMixInProperties();

		// generate an id for the widget if one wasn't specified
		// (be sure to do this before buildRendering() because that function might
		// expect the id to be there.
		if(!this.id){
			this.id=dijit.getUniqueId(this.declaredClass.replace(/\./g,"_"));
		}
		dijit.registry.add(this);

		this.buildRendering();

		// Copy attributes listed in attributeMap into the newly created DOM for the widget
		// The placement of these attributes is according to the property mapping in attributeMap
		// Note special handling for 'style' and 'class' attributes which are lists and can
		// have elements from both old and new structures, and some attributes like "type"
		// cannot be processed this way as they are not mutable.

//KLUDGE: skip the widgets which aren't yet working.  See #3058
if(!/dijit\.form\.(Slider|Textarea|ComboBox|FilteringSelect)/.test(this.declaredClass))

		for(var attr in this.attributeMap){
			if(this.domNode){
				var node = this[this.attributeMap[attr] || "domNode"];
				var value = this[attr];
				if(value !== "" || (params && params[attr])){
					var domValue = node.getAttribute(attr);
					// Deal with IE quirks for 'class' and 'style'
					switch(attr){
					case "class":
						domValue = node.className;
						break;
					case "style":
						domValue = node.cssText; // FIXME: Opera
					}
					if(domValue){
						var delim = {style: ";", "class": " "}[attr];
						// style and class attributes are special and contain lists
						// which need to be combined
						if(delim && value){
							value += delim + domValue;
						}else{
							value = domValue;
						}
					}

					// Deal with IE quirks for 'class' and 'style'
					switch(attr){
					case "class":
						node.className = value;
						break;
					case "style":
						if(node.style && dojo.isObject(node.style)){
							node.style.cssText = value; // required for IE, doesn't work in Opera
							break;
						}
						// fallthrough...
					default:
						node.setAttribute(attr, value);
					}
				}
			}
		}

		if(this.domNode){
			this.domNode.setAttribute("widgetId", this.id);
		}
		this.postCreate();

		// If srcNodeRef has been processed and removed from the DOM (e.g. TemplatedWidget) then delete it to allow GC.
		if(this.srcNodeRef && !this.srcNodeRef.parentNode){
			delete this.srcNodeRef;
		}
	},

	postMixInProperties: function(){
		// summary
		//	Called after the parameters to the widget have been read-in,
		//	but before the widget template is instantiated.
		//	Especially useful to set properties that are referenced in the widget template.
	},

	buildRendering: function(){
		// summary:
		//		Construct the UI for this widget, setting this.domNode.
		//		Most widgets will mixin TemplatedWidget, which overrides this method.
		this.domNode = this.srcNodeRef;
	},

	postCreate: function(){
		// summary:
		//		Called after a widget's dom has been setup
	},

	startup: function(){
		// summary:
		//		Called after a widget's children, and other widgets on the page, have been created.
		//		Provides an opportunity to manipulate any children before they are displayed
		//		This is useful for composite widgets that need to control or layout sub-widgets
		//		Many layout widgets can use this as a wiring phase
	},

	//////////// DESTROY FUNCTIONS ////////////////////////////////

	destroyRecursive: function(/*Boolean*/ finalize){
		// summary:
		// 		Destroy this widget and it's descendants. This is the generic
		// 		"destructor" function that all widget users should call to
		// 		cleanly discard with a widget. Once a widget is destroyed, it's
		// 		removed from the manager object.
		// finalize: Boolean
		//		is this function being called part of global environment
		//		tear-down?

		this.destroyDescendants();
		this.destroy();
	},

	destroy: function(/*Boolean*/ finalize){
		// summary:
		// 		Destroy this widget, but not its descendants
		// finalize: Boolean
		//		is this function being called part of global environment
		//		tear-down?
		this.uninitialize();
		dojo.forEach(this._connects, function(array){
			dojo.forEach(array, dojo.disconnect);
		});
		this.destroyRendering(finalize);
		dijit.registry.remove(this.id);
	},

	destroyRendering: function(/*Boolean*/ finalize){
		// summary:
		//		Destroys the DOM nodes associated with this widget
		// finalize: Boolean
		//		is this function being called part of global environment
		//		tear-down?

		if(this.bgIframe){
			this.bgIframe.destroy();
			delete this.bgIframe;
		}

		if(this.domNode){
			dojo._destroyElement(this.domNode);
			delete this.domNode;
		}

		if(this.srcNodeRef){
			dojo._destroyElement(this.srcNodeRef);
			delete this.srcNodeRef;
		}
	},

	destroyDescendants: function(){
		// summary:
		//		Recursively destroy the children of this widget and their
		//		descendants.

		// TODO: should I destroy in the reverse order, to go bottom up?
		dojo.forEach(this.getDescendants(), function(widget){ widget.destroy(); });
	},

	uninitialize: function(){
		// summary:
		//		stub function. Over-ride to implement custom widget tear-down
		//		behavior.
		return false;
	},

	////////////////// MISCELLANEOUS METHODS ///////////////////

	toString: function(){
		// summary:
		//		returns a string that represents the widget. When a widget is
		//		cast to a string, this method will be used to generate the
		//		output. Currently, it does not implement any sort of reversable
		//		serialization.
		return '[Widget ' + this.declaredClass + ', ' + (this.id || 'NO ID') + ']'; // String
	},

	getDescendants: function(){
		// summary:
		//	return all the descendant widgets
		var list = dojo.query('[widgetId]', this.domNode);
		return list.map(dijit.byNode);		// Array
	},

	nodesWithKeyClick : ["input", "button"],

	connect: function(
			/*Object|null*/ obj,
			/*String*/ event,
			/*String|Function*/ method){

		// summary:
		//		Connects specified obj/event to specified method of this object
		//		and registers for disconnect() on widget destroy.
		//		Special event: "ondijitclick" triggers on a click or enter-down or space-up
		//		Similar to dojo.connect() but takes three arguments rather than four.
		var handles =[];
		if(event == "ondijitclick"){
			var w = this;
			// add key based click activation for unsupported nodes.
			if(!this.nodesWithKeyClick[obj.nodeName]){
				handles.push(dojo.connect(obj, "onkeydown", this,
					function(e){
						if(e.keyCode == dojo.keys.ENTER){
							return (dojo.isString(method))?
								w[method](e) : method.call(w, e);
						}else if(e.keyCode == dojo.keys.SPACE){
							// stop space down as it causes IE to scroll
							// the browser window
							dojo.stopEvent(e);
						}
			 		}));
				handles.push(dojo.connect(obj, "onkeyup", this,
					function(e){
						if(e.keyCode == dojo.keys.SPACE){
							return dojo.isString(method) ?
								w[method](e) : method.call(w, e);
						}
			 		}));
			}
			event = "onclick";
		}
		handles.push(dojo.connect(obj, event, this, method));

		// return handles for FormElement and ComboBox
		this._connects.push(handles);
		return handles;
	},

	disconnect: function(/*Object*/ handles){
		// summary:
		//		Disconnects handle created by this.connect.
		//		Also removes handle from this widget's list of connects
		for(var i=0; i<this._connects.length; i++){
			if(this._connects[i]==handles){
				dojo.forEach(handles, dojo.disconnect);
				this._connects.splice(i, 1);
				return;
			}
		}
	},

	isLeftToRight: function(){
		// summary:
		//		Checks the DOM to for the text direction for bi-directional support
		// description:
		//		This method cannot be used during widget construction because the widget
		//		must first be connected to the DOM tree.  Parent nodes are searched for the
		//		'dir' attribute until one is found, otherwise left to right mode is assumed.
		//		See HTML spec, DIR attribute for more information.

		if(typeof this._ltr == "undefined"){
			this._ltr = dojo.getComputedStyle(this.domNode).direction != "rtl";
		}
		return this._ltr; //Boolean
	}
});
<|endoftext|>