<commit_before>/**
 *  Copyright 2012 Tomaz Muraus
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

var url = require('url');

var async = require('async');
var log = require('logmagic').local('lib.middleware.request.rate_limiting');

var sprintf = require('../../util/sprintf').sprintf;
var client = require('../../db').getClient();
var config = require('../../util/config').config;
var misc = require('../../util/misc');
var errors = require('../../util/errors');
var ProxyError = require('../../util/errors').ProxyError;
var httpUtil = require('../../util/http');

/**
 * Cache of compiled regular expressions.
 * @type {Object}
 */
var REGEX_CACHE = {};

/**
 * Calculate bucket key names for a rate limiting rule.
 *
 * @oaram {String} id User id.
 * @oaram {Object} rule Rate limiting rule.
 * @param {Function} callback Callback called with (err, keys).
 */
function calculateBucketKeyNames(id, rule, callback) {
  var settings = config.middleware.rate_limiting,
      now = misc.getUnixTimestamp(), keys = [], key, bucketCount, tmp, startTs, endTs, ts;

  bucketCount = (now / settings.bucket_size) | 0;
  tmp = (rule.period / settings.bucket_size) | 0;
  startTs = ((bucketCount - tmp) * settings.bucket_size) | 0;
  endTs = (bucketCount * settings.bucket_size);

  for (ts = startTs; ts < endTs; ts = (ts + settings.bucket_size)) {
    key = sprintf('rate_limits:%s:%s_%s:%s', id, rule.path_regex, rule.period, ts);
    keys.push(key);
  }

  callback(null, keys);
}

/**
 * Retrieve current usage.
 *
 * @param {Array} keys Bucket keys.
 * @param {Function} callback Callback called with (err, keys, bucketNameToValueMap).
 */
function getCurrentUsage(keys, callback) {
  client.getCounters(keys, function(err, values) {
    var usage = 0, bucketNameToValueMap = {}, i, key, value;

    if (err) {
      callback(err);
      return;
    }

    for (i = 0; i < keys.length; i++) {
      key = keys[i];
      value = values[i] || 0;
      value = parseFloat(value);
      bucketNameToValueMap[key] = value;

      usage += value;
    }

    callback(null, keys, usage, bucketNameToValueMap);
  });
}

/**
 * Check and update user rate limits.
 *
 * @param {String} id user id.
 * @param {Object} rule Rate limiting rule.
 * @param {Function} callback Callback called with (err).
 */
function checkAndUpdateLimit(id, rule, callback) {
  var settings = config.middleware.rate_limiting, currentUsage = 0;

  async.waterfall([
    calculateBucketKeyNames.bind(null, id, rule),
    getCurrentUsage,

    function checkUsageAgainstLimit(keys, usage, bucketNameToValueMap, callback) {
      currentUsage = usage;

      if (currentUsage >= rule.limit) {
        log.debug('Limit has been reached', {'usage': currentUsage, 'limit': rule.limit});
        callback(new errors.RateLimitReachedError(rule));
        return;
      }

      callback(null, keys, bucketNameToValueMap);
    },

    function updateUsage(keys, bucketNameToValueMap, callback) {
      var currentBucketKey = keys[keys.length - 1], value = bucketNameToValueMap[currentBucketKey];
      // Update counter for the currently active bucket
      client.incr(currentBucketKey, {'step': 1, 'ttl': settings.bucket_size}, function(err) {
        if (err) {
          callback(err);
          return;
        }

        currentUsage++;
        callback();
      });
    }
  ],

  function(err) {
    callback(err, currentUsage);
  });
}

/**
 * Retrieve usage for a user for all the rules.
 *
 * @param {String} id User id.
 * @param {String} callback Callback called with (err, {Array}result).
 */
function getUsage(id, callback) {
  var settings = config.middleware.rate_limiting, result = [];

  async.forEach(settings.limits, function(rule, callback) {
    async.waterfall([
      calculateBucketKeyNames.bind(null, id, rule),
      getCurrentUsage,

      function formatResult(keys, usage, bucketNameToValueMap, callback) {
        result.push({'method': rule.method, 'path_regex': rule.path_regex,
                     'limit': rule.limit, 'used': usage, 'period': rule.period});
        callback();
      }
    ], callback);
  },

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

    callback(null, result);
  });
}

exports.dependencies = ['identity_provider'];

exports.processRequest = function(req, res, callback) {
  var settings = config.middleware.rate_limiting, method = req.method,
      reachedLimitErrors = [], parsed = url.parse(req.url), pathName = parsed.pathname;

  req.activeLimits = [];

  if (!req.userId) {
    log.debug('User id not provided, skipping rate limiting...');
    callback();
    return;
  }

  async.waterfall([
    function getLimits(callback) {
      if (!(pathName === settings.usage_proxy_path && req.method === 'GET')) {
        callback();
        return;
      }

      // User requested a special URL, send raw usage data to the backend
      log.debug('User requested a special usage path, sending usage to the backend...', {'url': req.url});

      getUsage(req.userId, function(err, data) {
        if (err) {
          callback(err);
          return;
        }

        data = JSON.stringify(data);
        parsed.pathname = settings.usage_target_path;
        req.method = 'POST';
        req.url = url.format(parsed);

        req.headers['Conent-Type'] = 'application/json';
        req.headers['Content-Length'] = Buffer.byteLength(data, 'utf8');

        req.emit('data', data);
        callback();
      });
    },

    function checkAndUpdateLimits(callback) {
      async.forEach(settings.limits, function(rule, callback) {
        var regex;

        if (!REGEX_CACHE.hasOwnProperty(rule.path_regex)) {
          REGEX_CACHE[rule.path_regex] = new RegExp(rule.path_regex);
        }

        regex = REGEX_CACHE[rule.path_regex];

        // TODO: More efficient based on the database backend
        if ((rule.method === method || rule.method === 'ALL') && regex.test(req.url)) {
          log.debug('URL matches a rate limited path, updating and checking limits...',
                    {'url': req.url, 'method': method, 'path_regex': rule.path_regex});

          checkAndUpdateLimit(req.userId, rule, function(err, used) {
            if (err && (err instanceof errors.RateLimitReachedError)) {
              reachedLimitErrors.push(err);
              err = null;
            }

            req.activeLimits.push({'regex': rule.path_regex, 'limit': rule.limit,
                                   'period': rule.period, 'available': (rule.limit - used),
                                   'used': used});

            callback(err);
          });
        }
        else {
          callback();
        }
      },

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

        if (reachedLimitErrors.length >= 1) {
          err = reachedLimitErrors[0];
          callback(new ProxyError(2000, err.message));
          return;
        }

        callback();
      });
    }
  ], callback);
};
<commit_msg>Modify rate limiting middleware to use limit overrides from the database.<commit_after>/**
 *  Copyright 2012 Tomaz Muraus
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

var url = require('url');

var async = require('async');
var log = require('logmagic').local('lib.middleware.request.rate_limiting');

var sprintf = require('../../util/sprintf').sprintf;
var client = require('../../db').getClient();
var config = require('../../util/config').config;
var misc = require('../../util/misc');
var errors = require('../../util/errors');
var ProxyError = require('../../util/errors').ProxyError;
var httpUtil = require('../../util/http');

/**
 * Cache of compiled regular expressions.
 * @type {Object}
 */
var REGEX_CACHE = {};

/**
 * Calculate bucket key names for a rate limiting rule.
 *
 * @oaram {String} id User id.
 * @oaram {Object} rule Rate limiting rule.
 * @param {Function} callback Callback called with (err, keys).
 */
function calculateBucketKeyNames(id, rule, callback) {
  var settings = config.middleware.rate_limiting,
      now = misc.getUnixTimestamp(), keys = [], key, bucketCount, tmp, startTs, endTs, ts;

  bucketCount = (now / settings.bucket_size) | 0;
  tmp = (rule.period / settings.bucket_size) | 0;
  startTs = ((bucketCount - tmp) * settings.bucket_size) | 0;
  endTs = (bucketCount * settings.bucket_size);

  for (ts = startTs; ts < endTs; ts = (ts + settings.bucket_size)) {
    key = sprintf('rate_limits:%s:%s_%s:%s', id, rule.path_regex, rule.period, ts);
    keys.push(key);
  }

  callback(null, keys);
}

/**
 * Retrieve current usage.
 *
 * @param {Array} keys Bucket keys.
 * @param {Function} callback Callback called with (err, keys, bucketNameToValueMap).
 */
function getCurrentUsage(keys, callback) {
  client.getCounters(keys, function(err, values) {
    var usage = 0, bucketNameToValueMap = {}, i, key, value;

    if (err) {
      callback(err);
      return;
    }

    for (i = 0; i < keys.length; i++) {
      key = keys[i];
      value = values[i] || 0;
      value = parseFloat(value);
      bucketNameToValueMap[key] = value;

      usage += value;
    }

    callback(null, keys, usage, bucketNameToValueMap);
  });
}

/**
 * Retrieve limit overrides for the provider user id and path regex.
 *
 * @param {String} id user id.
 * @param {String} pathRegex Path regular expression rule.
 * @param {Function} callback Callback called with (err, value).
 */
function getLimitOverrides(id, pathRegex, callback) {
  var key = sprintf('rate_limit_overrides:%s:%s', id, pathRegex);
  client.get(key, function(err, value) {
    if (err) {
      callback(err);
      return;
    }

    if (value) {
      value = parseInt(value, 10);
    }

    callback(null, value);
  });
};

/**
 * Check and update user rate limits.
 *
 * @param {String} id user id.
 * @param {Object} rule Rate limiting rule.
 * @param {Function} callback Callback called with (err).
 */
function checkAndUpdateLimit(id, rule, callback) {
  var settings = config.middleware.rate_limiting, currentUsage = 0;

  async.waterfall([
    calculateBucketKeyNames.bind(null, id, rule),
    getCurrentUsage,

    function getOverrides(keys, usage, bucketNameToValueMap, callback) {
      getLimitOverrides(id, rule.path_regex, function(err, value) {
        var limit = rule.limit;

        if (err) {
          callback(err);
          return;
        }

        if (value) {
          limit = value;
        }

        callback(null, keys, usage, bucketNameToValueMap, limit);
      });
    },

    function checkUsageAgainstLimit(keys, usage, bucketNameToValueMap, limit, callback) {
      currentUsage = usage;

      if (currentUsage >= limit) {
        rule.limit = limit;
        log.debug('Limit has been reached', {'usage': currentUsage, 'limit': limit});
        callback(new errors.RateLimitReachedError(rule));
        return;
      }

      callback(null, keys, bucketNameToValueMap);
    },

    function updateUsage(keys, bucketNameToValueMap, callback) {
      var currentBucketKey = keys[keys.length - 1], value = bucketNameToValueMap[currentBucketKey];
      // Update counter for the currently active bucket
      client.incr(currentBucketKey, {'step': 1, 'ttl': settings.bucket_size}, function(err) {
        if (err) {
          callback(err);
          return;
        }

        currentUsage++;
        callback();
      });
    }
  ],

  function(err) {
    callback(err, currentUsage);
  });
}

/**
 * Retrieve usage for a user for all the rules.
 *
 * @param {String} id User id.
 * @param {String} callback Callback called with (err, {Array}result).
 */
function getUsage(id, callback) {
  var settings = config.middleware.rate_limiting, result = [];

  async.forEach(settings.limits, function(rule, callback) {
    async.waterfall([
      calculateBucketKeyNames.bind(null, id, rule),
      getCurrentUsage,

      function formatResult(keys, usage, bucketNameToValueMap, callback) {
        result.push({'method': rule.method, 'path_regex': rule.path_regex,
                     'limit': rule.limit, 'used': usage, 'period': rule.period});
        callback();
      }
    ], callback);
  },

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

    callback(null, result);
  });
}

exports.dependencies = ['identity_provider'];

exports.processRequest = function(req, res, callback) {
  var settings = config.middleware.rate_limiting, method = req.method,
      reachedLimitErrors = [], parsed = url.parse(req.url), pathName = parsed.pathname;

  req.activeLimits = [];

  if (!req.userId) {
    log.debug('User id not provided, skipping rate limiting...');
    callback();
    return;
  }

  async.waterfall([
    function getLimits(callback) {
      if (!(pathName === settings.usage_proxy_path && req.method === 'GET')) {
        callback();
        return;
      }

      // User requested a special URL, send raw usage data to the backend
      log.debug('User requested a special usage path, sending usage to the backend...', {'url': req.url});

      getUsage(req.userId, function(err, data) {
        if (err) {
          callback(err);
          return;
        }

        data = JSON.stringify(data);
        parsed.pathname = settings.usage_target_path;
        req.method = 'POST';
        req.url = url.format(parsed);

        req.headers['Conent-Type'] = 'application/json';
        req.headers['Content-Length'] = Buffer.byteLength(data, 'utf8');

        req.emit('data', data);
        callback();
      });
    },

    function checkAndUpdateLimits(callback) {
      async.forEach(settings.limits, function(rule, callback) {
        var regex;

        if (!REGEX_CACHE.hasOwnProperty(rule.path_regex)) {
          REGEX_CACHE[rule.path_regex] = new RegExp(rule.path_regex);
        }

        regex = REGEX_CACHE[rule.path_regex];

        // TODO: More efficient based on the database backend and less
        // round-trips
        if ((rule.method === method || rule.method === 'ALL') && regex.test(req.url)) {
          log.debug('URL matches a rate limited path, updating and checking limits...',
                    {'url': req.url, 'method': method, 'path_regex': rule.path_regex});

          checkAndUpdateLimit(req.userId, rule, function(err, used) {
            if (err && (err instanceof errors.RateLimitReachedError)) {
              reachedLimitErrors.push(err);
              err = null;
            }

            req.activeLimits.push({'regex': rule.path_regex, 'limit': rule.limit,
                                   'period': rule.period, 'available': (rule.limit - used),
                                   'used': used});

            callback(err);
          });
        }
        else {
          callback();
        }
      },

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

        if (reachedLimitErrors.length >= 1) {
          err = reachedLimitErrors[0];
          callback(new ProxyError(2000, err.message));
          return;
        }

        callback();
      });
    }
  ], callback);
};
<|endoftext|>


<commit_before>'use strict';

angular.module('acceptanceTests').factory('AcceptanceTestsService', function($q, $http) {
    return {
        categorizeTests: function(tests) {
            var categories = [];
            var categoriesMap = {};
            var unknownCategory = { name: 'Non catégorisés', tests: [] };
            tests.forEach(function(test) {
                if (test._updated) {
                    var updatedAt = moment(test._updated);
                    test.updatedAt = updatedAt.format('DD/MM/YYYY') + ' à ' + updatedAt.format('HH:mm');
                }
                if (test._created) {
                    var createdAt = moment(test._created);
                    test.createdAt = createdAt.format('DD/MM/YYYY') + ' à ' + createdAt.format('HH:mm');
                }
                var index = test.name.indexOf(']');
                if (-1 !== index) {
                    var categoryName = test.name.substring(1, index);
                    test.name = test.name.substring(index + 2, test.name.length);
                    var category = categoriesMap[categoryName];
                    if (!category) {
                        category = categoriesMap[categoryName] = {name: categoryName, tests: []};
                        categories.push(category);
                    }
                    category.tests.push(test);
                } else {
                    unknownCategory.tests.push(test);
                }
            });

            if (unknownCategory.tests.length) {
                categories.push(unknownCategory);
            }

            categories.forEach(function(category) {
                category.tests = _.sortBy(category.tests, 'name');
            });

            return categories;
        },

        launchTest: function(test) {
            var deferred = $q.defer();

            var promise = $http.get('/api/situations/' + test.situation + '/simulation');
            promise.then(function(result) {
                var droits = result.data;
                test.status = 'ok';
                test.droitsAttendus.forEach(function(droit) {
                    var actualValue = droits[droit.id];
                    if (angular.isDefined(actualValue)) {
                        delete droits[droit.id];
                        droit.actualValue = actualValue;
                        if (_.isUndefined(droit.expectedValue)) {
                            droit.status = 'unknown';
                        } else if (droit.actualValue === droit.expectedValue) {
                            droit.status = 'ok';
                        } else if ((Math.abs(droit.actualValue - droit.expectedValue) / droit.expectedValue) < 0.02) {
                            droit.status = 'ok';
                        } else if ((Math.abs(droit.actualValue - droit.expectedValue) / droit.expectedValue) < 0.1) {
                            droit.status = 'near';
                            if (test.status !== 'ko') test.status = 'near';
                        } else {
                            droit.status = 'ko';
                            test.status = 'ko';
                        }
                    }
                });
                _.forEach(droits, function(value, id) {
                    if (value) {
                        test.droitsAttendus.push({ id: id, expectedValue: undefined, actualValue: value, status: 'unknown' });
                    }
                });
                _.where(test.droitsAttendus, { status: undefined }).forEach(function(droit) {
                    droit.status = 'ko';
                    test.status = 'ko';
                    droit.actualValue = false;
                });
                if (test.status === 'ko') {
                    deferred.reject();
                } else {
                    deferred.resolve();
                }
            }, function() {
                test.status = 'ko';
                test.droitsAttendus.forEach(function(droit) {
                    droit.status = 'ko';
                });
                deferred.reject();
            });

            deferred.promise.finally(function() {
                test.running = false;
            });

            return deferred.promise;
        }
    };
});
<commit_msg>Mise à jour de l'API d'exécution des tests d'acceptance<commit_after>'use strict';

angular.module('acceptanceTests').factory('AcceptanceTestsService', function($q, $http) {
    return {
        categorizeTests: function(tests) {
            var categories = [];
            var categoriesMap = {};
            var unknownCategory = { name: 'Non catégorisés', tests: [] };
            tests.forEach(function(test) {
                if (test._updated) {
                    var updatedAt = moment(test._updated);
                    test.updatedAt = updatedAt.format('DD/MM/YYYY') + ' à ' + updatedAt.format('HH:mm');
                }
                if (test._created) {
                    var createdAt = moment(test._created);
                    test.createdAt = createdAt.format('DD/MM/YYYY') + ' à ' + createdAt.format('HH:mm');
                }
                var index = test.name.indexOf(']');
                if (-1 !== index) {
                    var categoryName = test.name.substring(1, index);
                    test.name = test.name.substring(index + 2, test.name.length);
                    var category = categoriesMap[categoryName];
                    if (!category) {
                        category = categoriesMap[categoryName] = {name: categoryName, tests: []};
                        categories.push(category);
                    }
                    category.tests.push(test);
                } else {
                    unknownCategory.tests.push(test);
                }
            });

            if (unknownCategory.tests.length) {
                categories.push(unknownCategory);
            }

            categories.forEach(function(category) {
                category.tests = _.sortBy(category.tests, 'name');
            });

            return categories;
        },

        launchTest: function(test) {
            var deferred = $q.defer();

            var promise = $http.post('/api/acceptance-tests/' + test._id + '/executions', {});
            promise.then(function(result) {
                var droits = result.data.droitsCalcules;
                test.status = 'ok';
                test.droitsAttendus.forEach(function(droit) {
                    var actualValue = droits[droit.id];
                    if (angular.isDefined(actualValue)) {
                        delete droits[droit.id];
                        droit.actualValue = actualValue;
                        if (_.isUndefined(droit.expectedValue)) {
                            droit.status = 'unknown';
                        } else if (droit.actualValue === droit.expectedValue) {
                            droit.status = 'ok';
                        } else if ((Math.abs(droit.actualValue - droit.expectedValue) / droit.expectedValue) < 0.02) {
                            droit.status = 'ok';
                        } else if ((Math.abs(droit.actualValue - droit.expectedValue) / droit.expectedValue) < 0.1) {
                            droit.status = 'near';
                            if (test.status !== 'ko') test.status = 'near';
                        } else {
                            droit.status = 'ko';
                            test.status = 'ko';
                        }
                    }
                });
                _.forEach(droits, function(value, id) {
                    if (value) {
                        test.droitsAttendus.push({ id: id, expectedValue: undefined, actualValue: value, status: 'unknown' });
                    }
                });
                _.where(test.droitsAttendus, { status: undefined }).forEach(function(droit) {
                    droit.status = 'ko';
                    test.status = 'ko';
                    droit.actualValue = false;
                });
                if (test.status === 'ko') {
                    deferred.reject();
                } else {
                    deferred.resolve();
                }
            }, function() {
                test.status = 'ko';
                test.droitsAttendus.forEach(function(droit) {
                    droit.status = 'ko';
                });
                deferred.reject();
            });

            deferred.promise.finally(function() {
                test.running = false;
            });

            return deferred.promise;
        }
    };
});
<|endoftext|>


<commit_before>// Do multiple calls to MarkLogic using the same transaction.
// Task: move a binary image to a different URI. As we do that, we also need to
// update the "binary" property in the corresponding JSON document so that we
// still have a path from one to the other. Since two documents are changing,
// we want to change both in the same transaction to ensure they never get
// disconnected.

var ml = require('marklogic');
var conn = require('../config.js').admin;
var db = ml.createDatabaseClient(conn);
var pb = ml.patchBuilder;

var jsonURI = '/image/01.JPG.json';
var binaryURI = null;
var updatedBinaryURI = null;

// Create a transaction
db.transactions.open(true).result()
  .then(function(txResponse) {
    // Search for all the JSON image descriptions
    db.documents.read(jsonURI).result()
      .then(function(doc) {
        // patch the JSON document
        binaryURI = doc[0].content.binary;
        updatedBinaryURI = binaryURI.toLowerCase();

        console.log('patching ' + jsonURI);
        return db.documents.patch({
          uri: jsonURI,
          operations: pb.replace('/binary', updatedBinaryURI),
          txid: txResponse
        }).result();
      })
      .then(function() {
        // get the binary image
        console.log('retrieving ' + binaryURI);
        return db.documents.read({uris: binaryURI, categories: ['content', 'collections']}).result();
      })
      .then(function(doc) {
        // Insert the binary image at its new URI
        console.log('inserting image at ' + updatedBinaryURI);

        return db.documents.write({
          documents: {
            uri: updatedBinaryURI,
            contentType: doc[0].contentType,
            collections: doc[0].collections,
            content: doc[0].content
          },
          txid: txResponse
        }).result();

      })
      .then(function() {
        // Remove the original binary image
        console.log('removing image from ' + binaryURI);
        db.documents.remove({ uris: binaryURI, txid: txResponse}).result();
      })
      .then(function() {
        // We've gotten this far, so everything worked. Commit.
        console.log('done... commit!');
        db.transactions.commit(txResponse);
      })
      .catch(function(error) {
        // Something went wrong. Roll back the transaction.
        console.log('Problem: ' + error);
        db.transactions.rollback(txResponse);
      });
  });
<commit_msg>refactor code<commit_after>// Do multiple calls to MarkLogic using the same transaction.
// Task: move a binary image to a different URI. As we do that, we also need to
// update the "binary" property in the corresponding JSON document so that we
// still have a path from one to the other. Since two documents are changing,
// we want to change both in the same transaction to ensure they never get
// disconnected.

var ml = require('marklogic');
var conn = require('../config.js').admin;
var db = ml.createDatabaseClient(conn);
var pb = ml.patchBuilder;

var jsonURI = '/image/01.JPG.json';

// Change the 'binary' JSON property
function updateJSONdoc(binaryURI, updatedBinaryURI, transactionID) {
  // patch the JSON document

  console.log('changing ' + binaryURI + ' to ' + updatedBinaryURI);
  return db.documents.patch({
    uri: jsonURI,
    operations: pb.replace('/binary', updatedBinaryURI),
    txid: transactionID
  }).result();

}

function getBinaryImage(binaryURI, transactionID) {
  // get the binary image
  console.log('retrieving ' + binaryURI);
  return db.documents.read({uris: binaryURI, categories: ['content', 'collections']}).result();
}

function insertBinary(binaryDocDesc, updatedBinaryURI, transactionID) {
  // Insert the binary image at its new URI
  console.log('inserting image at ' + updatedBinaryURI);

  return db.documents.write({
    documents: {
      uri: updatedBinaryURI,
      contentType: binaryDocDesc.contentType,
      collections: binaryDocDesc.collections,
      content: binaryDocDesc.content
    },
    txid: transactionID
  }).result();
}

function deleteBinary(binaryURI, transactionID) {
  // Remove the original binary image
  console.log('removing image from ' + binaryURI);
  db.documents.remove({ uris: binaryURI, txid: transactionID}).result();
}

// Create a transaction
db.transactions.open(true).result()
  .then(function(txResponse) {
    var transactionID = txResponse;
    // Search for all the JSON image descriptions
    db.documents.read(jsonURI).result()
      .then(function(jsonDoc){
        var binaryURI = jsonDoc[0].content.binary;
        var updatedBinaryURI = binaryURI.toLowerCase();
        getBinaryImage(binaryURI, transactionID)
          .then(function(binaryDocDesc){
            Promise.all(
              [
                updateJSONdoc(binaryURI, updatedBinaryURI, transactionID),
                insertBinary(binaryDocDesc[0], updatedBinaryURI, transactionID),
                deleteBinary(binaryURI, transactionID)
              ])
              .then(function(){
                // We've gotten this far, so everything worked. Commit.
                console.log('done... commit!');
                db.transactions.commit(txResponse);
              });
          });
      })
      .catch(function(error) {
        // Something went wrong. Roll back the transaction.
        console.log('Problem: ' + error);
        db.transactions.rollback(txResponse);
      });
  });
<|endoftext|>


<commit_before>// ==========================================================================
// Project:   SproutCore - JavaScript Application Framework
// Copyright: ©2006-2009 Sprout Systems, Inc. and contributors.
//            Portions ©2008-2009 Apple, Inc. All rights reserved.
// License:   Licened under MIT license (see license.js)
// ==========================================================================

/*globals throws */

var content, controller, extra;

var TestObject = SC.Object.extend({
  title: "test",
  toString: function() { return "TestObject(%@)".fmt(this.get("title")); }
});

// ..........................................................
// allowsSingleContent
//

module("SC.ArrayController - single_case - SINGLE", {
  setup: function() {
    content = TestObject.create({ title: "FOO" });
    controller = SC.ArrayController.create({
      content: content, allowsSingleContent: YES
    });
  },

  teardown: function() {
    controller.destroy();
  }
});

test("state properties", function() {
  equals(controller.get("hasContent"), YES, 'c.hasContent');
  equals(controller.get("canRemoveContent"), YES, "c.canRemoveContent");
  equals(controller.get("canReorderContent"), NO, "c.canReorderContent");
  equals(controller.get("canAddContent"), NO, "c.canAddContent");
});

// addObject should append to end of array + notify observers on Array itself
test("addObject", function() {
  should_throw(function() {
    controller.addObject(extra);
  }, Error, "controller.addObject should throw exception");
});

test("removeObject - no destroyOnRemoval", function() {
  var callCount = 0;
  controller.set('destroyOnRemoval', NO);
  controller.addObserver('[]', function() { callCount++; });

  controller.removeObject(content);

  equals(controller.get('content'), null, 'removeObject(content) should set content to null');
  ok((callCount>=1), 'should notify observer since content did not change');
  equals(controller.get('length'), 0, 'should now have length of 0');
  equals(content.isDestroyed, NO, 'content.isDestroyed should be destroyed');
});

test("removeObject - destroyOnRemoval", function() {
  controller.set('destroyOnRemoval', YES);
  SC.run(function() { controller.removeObject(content); });
  equals(content.isDestroyed, YES, 'content.isDestroyed should be destroyed');
});

test("basic array READ operations", function() {
  equals(controller.get("length"), 1, 'length should be 1');
  equals(controller.objectAt(0), content, "objectAt(0) should return content");
});

test("basic array WRITE operations", function() {
  should_throw(function() {
    controller.replace(0,1,[extra]);
  }, Error, "calling replace on an enumerable should throw");
});

test("arrangedObjects", function() {
  equals(controller.get("arrangedObjects"), controller, 'c.arrangedObjects should return receiver');
});

// ..........................................................
// allowsSingleContent=NO
//

module("SC.ArrayController - single_case - allowsSingleContent=NO", {
  setup: function() {
    content = TestObject.create({ title: "FOO" });
    controller = SC.ArrayController.create({
      content: content, allowsSingleContent: NO
    });
  },

  teardown: function() {
    controller.destroy();
  }
});

test("state properties", function() {
  equals(controller.get("hasContent"), NO, 'c.hasContent');
  equals(controller.get("canRemoveContent"), NO, "c.canRemoveContent");
  equals(controller.get("canReorderContent"), NO, "c.canReorderContent");
  equals(controller.get("canAddContent"), NO, "c.canAddContent");
});

// addObject should append to end of array + notify observers on Array itself
test("addObject", function() {
  should_throw(function() {
    controller.addObject(extra);
  }, Error, "controller.addObject should throw exception");
});

test("removeObject", function() {
  should_throw(function() {
    controller.removeObject(extra);
  }, Error, "controller.removeObject should throw exception");
});

test("basic array READ operations", function() {
  equals(controller.get("length"), 0, 'length should be empty');
  equals(controller.objectAt(0), undefined, "objectAt() should return undefined");
});

test("basic array WRITE operations", function() {
  should_throw(function() {
    controller.replace(0,1,[extra]);
  }, Error, "calling replace on an enumerable should throw");
});

test("arrangedObjects", function() {
  equals(controller.get("arrangedObjects"), controller, 'c.arrangedObjects should return receiver');
});

<commit_msg>Reverting array controller unit test. Observer should only be called once<commit_after>// ==========================================================================
// Project:   SproutCore - JavaScript Application Framework
// Copyright: ©2006-2009 Sprout Systems, Inc. and contributors.
//            Portions ©2008-2009 Apple, Inc. All rights reserved.
// License:   Licened under MIT license (see license.js)
// ==========================================================================

/*globals throws */

var content, controller, extra;

var TestObject = SC.Object.extend({
  title: "test",
  toString: function() { return "TestObject(%@)".fmt(this.get("title")); }
});

// ..........................................................
// allowsSingleContent
//

module("SC.ArrayController - single_case - SINGLE", {
  setup: function() {
    content = TestObject.create({ title: "FOO" });
    controller = SC.ArrayController.create({
      content: content, allowsSingleContent: YES
    });
  },

  teardown: function() {
    controller.destroy();
  }
});

test("state properties", function() {
  equals(controller.get("hasContent"), YES, 'c.hasContent');
  equals(controller.get("canRemoveContent"), YES, "c.canRemoveContent");
  equals(controller.get("canReorderContent"), NO, "c.canReorderContent");
  equals(controller.get("canAddContent"), NO, "c.canAddContent");
});

// addObject should append to end of array + notify observers on Array itself
test("addObject", function() {
  should_throw(function() {
    controller.addObject(extra);
  }, Error, "controller.addObject should throw exception");
});

test("removeObject - no destroyOnRemoval", function() {
  var callCount = 0;
  controller.set('destroyOnRemoval', NO);
  controller.addObserver('[]', function() { callCount++; });

  controller.removeObject(content);

  equals(controller.get('content'), null, 'removeObject(content) should set content to null');
  ok((callCount=1), 'should notify observer since content did not change');
  equals(controller.get('length'), 0, 'should now have length of 0');
  equals(content.isDestroyed, NO, 'content.isDestroyed should be destroyed');
});

test("removeObject - destroyOnRemoval", function() {
  controller.set('destroyOnRemoval', YES);
  SC.run(function() { controller.removeObject(content); });
  equals(content.isDestroyed, YES, 'content.isDestroyed should be destroyed');
});

test("basic array READ operations", function() {
  equals(controller.get("length"), 1, 'length should be 1');
  equals(controller.objectAt(0), content, "objectAt(0) should return content");
});

test("basic array WRITE operations", function() {
  should_throw(function() {
    controller.replace(0,1,[extra]);
  }, Error, "calling replace on an enumerable should throw");
});

test("arrangedObjects", function() {
  equals(controller.get("arrangedObjects"), controller, 'c.arrangedObjects should return receiver');
});

// ..........................................................
// allowsSingleContent=NO
//

module("SC.ArrayController - single_case - allowsSingleContent=NO", {
  setup: function() {
    content = TestObject.create({ title: "FOO" });
    controller = SC.ArrayController.create({
      content: content, allowsSingleContent: NO
    });
  },

  teardown: function() {
    controller.destroy();
  }
});

test("state properties", function() {
  equals(controller.get("hasContent"), NO, 'c.hasContent');
  equals(controller.get("canRemoveContent"), NO, "c.canRemoveContent");
  equals(controller.get("canReorderContent"), NO, "c.canReorderContent");
  equals(controller.get("canAddContent"), NO, "c.canAddContent");
});

// addObject should append to end of array + notify observers on Array itself
test("addObject", function() {
  should_throw(function() {
    controller.addObject(extra);
  }, Error, "controller.addObject should throw exception");
});

test("removeObject", function() {
  should_throw(function() {
    controller.removeObject(extra);
  }, Error, "controller.removeObject should throw exception");
});

test("basic array READ operations", function() {
  equals(controller.get("length"), 0, 'length should be empty');
  equals(controller.objectAt(0), undefined, "objectAt() should return undefined");
});

test("basic array WRITE operations", function() {
  should_throw(function() {
    controller.replace(0,1,[extra]);
  }, Error, "calling replace on an enumerable should throw");
});

test("arrangedObjects", function() {
  equals(controller.get("arrangedObjects"), controller, 'c.arrangedObjects should return receiver');
});

<|endoftext|>


<commit_before>var Generator = require('../generator');
var debug = require('debug')('fire:bridge');
var inflection = require('inflection');
var utils = require('../../../helpers/utils');
var path = require('path');

/**
 * Generates a client-side library to manage your models with a 1-to-1 API as the models api on the server-context.
 *
 * For more information on the API itself, see the Models module.
 *
 * @name Bridge~generateModels
 * @return {Generator} A generator instance which holds the information to render this module.
 */
exports = module.exports = function() {
	var models = [];

	this.app.models.forEach(function(model) {
		var modelName = model.getName();

		var properties = [];

		var allProperties = model.getAllProperties();
		var propertyNames = Object.keys(allProperties);

		var methodMaps = [];

		debug('Generate model `' + modelName + '`.');
		debug(propertyNames);

		debug('Authenticator: ' + model.isAuthenticator());

		propertyNames.forEach(function(propertyName) {
			var property = allProperties[propertyName];

			if(!property.options.isPrivate) {
				if(property.isAssociation()) {
					if(property.isManyToMany()) {
						methodMaps.push({
							manyToMany: true,
							createMethodName: 'create' + utils.ucfirst(inflection.singularize(propertyName)),
							resource: propertyName
						});
					}
					else if(property.options.hasMany) {
						if(!property.options.relationshipVia) {
							throw new Error('Found has many relationship but could not find link. Did you call HasMany-BelongsTo on the correct models?');
						}

						methodMaps.push({
							getMethodName: 'get' + inflection.capitalize(propertyName),
							resource: propertyName,
							modelName: property.options.relationshipVia.model.getName()
						});
					}
				}
				else if(property.options.hasMethod) {
					methodMaps.push({
						getMethodName: 'get' + inflection.capitalize(propertyName),
						resource: propertyName,
						modelName: property.options.hasModel.getName()
					});
				}

				properties.push(property);
			}
		});

		models.push({
			name: modelName,
			properties: properties,
			resource: inflection.pluralize(inflection.dasherize(modelName)).toLowerCase(),
			isAuthenticator: model.isAuthenticator(),
			authenticatingPropertyName: model.options.authenticatingProperty ? model.options.authenticatingProperty.name : null,
			methods: methodMaps
		});
	});

	return new Generator(path.join(__dirname, '../templates/models-js.mu'), {models: models});
};
<commit_msg>fixes dashes in model names with multiple words<commit_after>var Generator = require('../generator');
var debug = require('debug')('fire:bridge');
var inflection = require('inflection');
var utils = require('../../../helpers/utils');
var path = require('path');

/**
 * Generates a client-side library to manage your models with a 1-to-1 API as the models api on the server-context.
 *
 * For more information on the API itself, see the Models module.
 *
 * @name Bridge~generateModels
 * @return {Generator} A generator instance which holds the information to render this module.
 */
exports = module.exports = function() {
	var models = [];

	this.app.models.forEach(function(model) {
		var modelName = model.getName();

		var properties = [];

		var allProperties = model.getAllProperties();
		var propertyNames = Object.keys(allProperties);

		var methodMaps = [];

		debug('Generate model `' + modelName + '`.');
		debug(propertyNames);

		debug('Authenticator: ' + model.isAuthenticator());

		propertyNames.forEach(function(propertyName) {
			var property = allProperties[propertyName];

			if(!property.options.isPrivate) {
				if(property.isAssociation()) {
					if(property.isManyToMany()) {
						methodMaps.push({
							manyToMany: true,
							createMethodName: 'create' + utils.ucfirst(inflection.singularize(propertyName)),
							resource: propertyName
						});
					}
					else if(property.options.hasMany) {
						if(!property.options.relationshipVia) {
							throw new Error('Found has many relationship but could not find link. Did you call HasMany-BelongsTo on the correct models?');
						}

						methodMaps.push({
							getMethodName: 'get' + inflection.capitalize(propertyName),
							resource: propertyName,
							modelName: property.options.relationshipVia.model.getName()
						});
					}
				}
				else if(property.options.hasMethod) {
					methodMaps.push({
						getMethodName: 'get' + inflection.capitalize(propertyName),
						resource: propertyName,
						modelName: property.options.hasModel.getName()
					});
				}

				properties.push(property);
			}
		});

		models.push({
			name: modelName,
			properties: properties,
			resource: inflection.transform(modelName, ['tableize', 'dasherize']).toLowerCase(),
			isAuthenticator: model.isAuthenticator(),
			authenticatingPropertyName: model.options.authenticatingProperty ? model.options.authenticatingProperty.name : null,
			methods: methodMaps
		});
	});

	return new Generator(path.join(__dirname, '../templates/models-js.mu'), {models: models});
};
<|endoftext|>


<commit_before>// Copyright (c) 2016 Kari Karvonen, OH1KK

var ant_switch_ext_name = 'ant_switch';		// NB: must match ant_switch.c:ant_switch_ext.name

var ant_switch_first_time = true;

function ant_switch_main()
{
	ext_switch_to_client(ant_switch_ext_name, ant_switch_first_time, ant_switch_recv);		// tell server to use us (again)
	if (!ant_switch_first_time)
		ant_switch_controls_setup();
	ant_switch_first_time = false;
}

var ant_switch_cmd_e = { CMD1:0 };

function ant_switch_recv(data)
{
	var firstChars = getFirstChars(data, 3);

	// process data sent from server/C by ext_send_data_msg()
	if (firstChars == "DAT") {
		var ba = new Uint8Array(data, 4);
		var cmd = ba[0];

		if (cmd == ant_switch_cmd_e.CMD1) {
			// do something ...
		} else {
			console.log('ant_switch_recv: DATA UNKNOWN cmd='+ cmd +' len='+ (ba.length-1));
		}
		return;
	}

	// process command sent from server/C by ext_send_msg() or ext_send_encoded_msg()
	var stringData = arrayBufferToString(data);
	var params = stringData.substring(4).split(" ");

	for (var i=0; i < params.length; i++) {
		var param = params[i].split("=");

		switch (param[0]) {

			case "ready":
				ant_switch_controls_setup();
				break;

			case "Antenna":
				var arg = String.fromCharCode(param[1]).charAt(0);
                                ant_switch_process_reply(arg);
				break;

			default:
				console.log('ant_switch_recv: UNKNOWN CMD '+ param[0]);
				break;
		}
	}
}

function ant_switch_controls_setup()
{

   var antdesc = [ ];
   antdesc[1] = ext_get_cfg_param_string('ant_switch.ant1desc', '');
   antdesc[2] = ext_get_cfg_param_string('ant_switch.ant2desc', '');
   antdesc[3] = ext_get_cfg_param_string('ant_switch.ant3desc', '');
   antdesc[4] = ext_get_cfg_param_string('ant_switch.ant4desc', '');
   antdesc[5] = ext_get_cfg_param_string('ant_switch.ant5desc', '');
   antdesc[6] = ext_get_cfg_param_string('ant_switch.ant6desc', '');
   antdesc[7] = ext_get_cfg_param_string('ant_switch.ant7desc', '');

   var buttons_html = ''

   var tmp;
   console.log('ant_switch: Antenna configuration');
   for (tmp = 1; tmp<8; tmp++) {
           if (antdesc[tmp] == undefined || antdesc[tmp] == null || antdesc[tmp] == '') {
                antdesc[tmp] = '';
           }  else {
                buttons_html+=w3_divs('','', w3_inline('', '', w3_btn('Antenna '+tmp, 'ant_switch_select_'+tmp),antdesc[tmp]));
           }
           console.log('ant_switch: Antenna '+ tmp +': '+ antdesc[tmp]);
   }

   buttons_html+=w3_inline('', '', w3_btn('Ground all', 'ant_switch_select_groundall'), 'Ground all antennas');
   console.log('ant_switch: Antenna g: Ground all antennas');

   var data_html =
      '

';
	var controls_html =
		w3_divs('id-ant_display-controls w3-text-white', '',
			data_html,
        		w3_divs('w3-container', 'w3-tspace-8',
			        w3_divs('', 'w3-medium w3-text-aqua', 'MS-S7-WEB Antenna switch'),
                                w3_divs('id-ant-display-selected', '','Selected antenna: unknown'),
                                w3_divs('', '',buttons_html),
                                w3_divs('', '','')
                        )
		);

	ext_panel_show(controls_html, null, null);
	ant_switch_visible(1);
	ext_send('GET Antenna');
	ant_display_update();
}

function ant_switch_blur()
{
	console.log('### ant_switch_blur');
	ant_switch_visible(0);		// hook to be called when controls panel is closed
}

// called to display HTML for configuration parameters in admin interface
function ant_switch_config_html()
{
	ext_admin_config(ant_switch_ext_name, 'Antenna switch',
		w3_divs('id-ant_switch w3-text-teal w3-hide', '',
			'MS-S7-WEB Antenna switch configuration' +
			'
Leave Antenna description field empty if you want to disable antenna' +
			'
' +
			w3_third('', 'w3-container',
				w3_divs('', 'w3-margin-bottom',
					w3_input_get_param('Antenna 1 description', 'ant_switch.ant1desc', 'admin_string_cb'),
					w3_input_get_param('Antenna 2 description', 'ant_switch.ant2desc', 'admin_string_cb'),
					w3_input_get_param('Antenna 3 description', 'ant_switch.ant3desc', 'admin_string_cb'),
					w3_input_get_param('Antenna 4 description', 'ant_switch.ant4desc', 'admin_string_cb'),
					w3_input_get_param('Antenna 5 description', 'ant_switch.ant5desc', 'admin_string_cb'),
					w3_input_get_param('Antenna 6 description', 'ant_switch.ant6desc', 'admin_string_cb'),
					w3_input_get_param('Antenna 7 description', 'ant_switch.ant7desc', 'admin_string_cb'),
					w3_input_get_param('Antenna switch failure or unknown status decription', 'ant_switch.ant0desc', 'admin_string_cb')
				), '', ''
			)
		)
	);
}

function ant_switch_visible(v)
{
	visible_block('id-ant_switch-data', v);
}

function ant_switch_select_1(path,val) {
        ant_switch_select_antenna('1');
}

function ant_switch_select_2(path,val) {
        ant_switch_select_antenna('2');
}

function ant_switch_select_3(path,val) {
        ant_switch_select_antenna('3');
}

function ant_switch_select_4(path,val) {
        ant_switch_select_antenna('4');
}

function ant_switch_select_5(path,val) {
        ant_switch_select_antenna('5');
}

function ant_switch_select_6(path,val) {
        ant_switch_select_antenna('6');
}

function ant_switch_select_7(path,val) {
        ant_switch_select_antenna('7');
}

function ant_switch_select_groundall(path,val) {
        setTimeout('w3_radio_unhighlight('+ q(path) +')', w3_highlight_time);
        ant_switch_select_antenna('g');
}

function ant_switch_select_antenna(ant) {
        console.log('ant_switch: switching to antenna '+ant);
	ext_send('SET Antenna='+ant);
        ext_send('GET Antenna');
}

function ant_switch_process_reply(ant) {
        ant_selected_antenna = ant;

        for (var tmp=0; tmp<7; tmp++) {
                var elid='cl-id-btn-grp-'+tmp;
                if (w3_el_id(elid) !=  null) w3_unhighlight(elid);
        }

        if (ant == 'g') {
                console.log('ant_switch: all antennas grouded');
                ant_display_update('Thunderstorm mode. All antennas are grounded.');
        } else {
                console.log('ant_switch: antenna '+ ant_selected_antenna +' in use');
                ant_display_update('Antenna '+ant_selected_antenna + ": "+ext_get_cfg_param_string('ant_switch.ant'+ant_selected_antenna+'desc', ''));
                for (tmp=0; tmp<6; tmp++) {
                        if (ant == (tmp+1)) {
                                 var elid='cl-id-btn-grp-'+tmp;
                                 if (w3_el_id(elid) != null) w3_highlight(elid);
                        }
                }
        }
}

function ant_display_update(ant) {
        html('id-ant-display-selected').innerHTML = ant;
        var el = html('rx-antenna');
        if (el != undefined && ant) {
                el.innerHTML = ant;
        }
}
<commit_msg>Added 5 sec timer to poll antenna status<commit_after>// Copyright (c) 2016 Kari Karvonen, OH1KK

var ant_switch_ext_name = 'ant_switch';		// NB: must match ant_switch.c:ant_switch_ext.name
var ant_switch_first_time = true;
var ant_switch_poll_interval;

function ant_switch_main()
{
	ext_switch_to_client(ant_switch_ext_name, ant_switch_first_time, ant_switch_recv);		// tell server to use us (again)
	if (!ant_switch_first_time)
		ant_switch_controls_setup();
	ant_switch_first_time = false;
}

var ant_switch_cmd_e = { CMD1:0 };

function ant_switch_recv(data)
{
	var firstChars = getFirstChars(data, 3);

	// process data sent from server/C by ext_send_data_msg()
	if (firstChars == "DAT") {
		var ba = new Uint8Array(data, 4);
		var cmd = ba[0];

		if (cmd == ant_switch_cmd_e.CMD1) {
			// do something ...
		} else {
			console.log('ant_switch_recv: DATA UNKNOWN cmd='+ cmd +' len='+ (ba.length-1));
		}
		return;
	}

	// process command sent from server/C by ext_send_msg() or ext_send_encoded_msg()
	var stringData = arrayBufferToString(data);
	var params = stringData.substring(4).split(" ");

	for (var i=0; i < params.length; i++) {
		var param = params[i].split("=");

		switch (param[0]) {

			case "ready":
				ant_switch_controls_setup();
				break;

			case "Antenna":
				var arg = String.fromCharCode(param[1]).charAt(0);
                                ant_switch_process_reply(arg);
				break;

			default:
				console.log('ant_switch_recv: UNKNOWN CMD '+ param[0]);
				break;
		}
	}
}

function ant_switch_controls_setup()
{

   var antdesc = [ ];
   antdesc[1] = ext_get_cfg_param_string('ant_switch.ant1desc', '');
   antdesc[2] = ext_get_cfg_param_string('ant_switch.ant2desc', '');
   antdesc[3] = ext_get_cfg_param_string('ant_switch.ant3desc', '');
   antdesc[4] = ext_get_cfg_param_string('ant_switch.ant4desc', '');
   antdesc[5] = ext_get_cfg_param_string('ant_switch.ant5desc', '');
   antdesc[6] = ext_get_cfg_param_string('ant_switch.ant6desc', '');
   antdesc[7] = ext_get_cfg_param_string('ant_switch.ant7desc', '');

   var buttons_html = ''

   var tmp;
   console.log('ant_switch: Antenna configuration');
   for (tmp = 1; tmp<8; tmp++) {
           if (antdesc[tmp] == undefined || antdesc[tmp] == null || antdesc[tmp] == '') {
                antdesc[tmp] = '';
           }  else {
                buttons_html+=w3_divs('','', w3_inline('', '', w3_btn('Antenna '+tmp, 'ant_switch_select_'+tmp),antdesc[tmp]));
           }
           console.log('ant_switch: Antenna '+ tmp +': '+ antdesc[tmp]);
   }

   buttons_html+=w3_inline('', '', w3_btn('Ground all', 'ant_switch_select_groundall'), 'Ground all antennas');
   console.log('ant_switch: Antenna g: Ground all antennas');

   var data_html =
      '

';
	var controls_html =
		w3_divs('id-ant_display-controls w3-text-white', '',
			data_html,
        		w3_divs('w3-container', 'w3-tspace-8',
			        w3_divs('', 'w3-medium w3-text-aqua', 'MS-S7-WEB Antenna switch'),
                                w3_divs('id-ant-display-selected', '','Selected antenna: unknown'),
                                w3_divs('', '',buttons_html),
                                w3_divs('', '','')
                        )
		);

	ext_panel_show(controls_html, null, null);
	ant_switch_visible(1);
        ant_switch_poll();
 	//ant_display_update();
}

function ant_switch_blur()
{
        kiwi_clearInterval(ant_switch_poll_interval);
	console.log('### ant_switch_blur');
	ant_switch_visible(0);		// hook to be called when controls panel is closed
}

// called to display HTML for configuration parameters in admin interface
function ant_switch_config_html()
{
	ext_admin_config(ant_switch_ext_name, 'Antenna switch',
		w3_divs('id-ant_switch w3-text-teal w3-hide', '',
			'MS-S7-WEB Antenna switch configuration' +
			'
Leave Antenna description field empty if you want to disable antenna' +
			'
' +
			w3_third('', 'w3-container',
				w3_divs('', 'w3-margin-bottom',
					w3_input_get_param('Antenna 1 description', 'ant_switch.ant1desc', 'admin_string_cb'),
					w3_input_get_param('Antenna 2 description', 'ant_switch.ant2desc', 'admin_string_cb'),
					w3_input_get_param('Antenna 3 description', 'ant_switch.ant3desc', 'admin_string_cb'),
					w3_input_get_param('Antenna 4 description', 'ant_switch.ant4desc', 'admin_string_cb'),
					w3_input_get_param('Antenna 5 description', 'ant_switch.ant5desc', 'admin_string_cb'),
					w3_input_get_param('Antenna 6 description', 'ant_switch.ant6desc', 'admin_string_cb'),
					w3_input_get_param('Antenna 7 description', 'ant_switch.ant7desc', 'admin_string_cb'),
					w3_input_get_param('Antenna switch failure or unknown status decription', 'ant_switch.ant0desc', 'admin_string_cb')
				), '', ''
			)
		)
	);
}

function ant_switch_visible(v)
{
	visible_block('id-ant_switch-data', v);
}

function ant_switch_select_1(path,val) {
        ant_switch_select_antenna('1');
}

function ant_switch_select_2(path,val) {
        ant_switch_select_antenna('2');
}

function ant_switch_select_3(path,val) {
        ant_switch_select_antenna('3');
}

function ant_switch_select_4(path,val) {
        ant_switch_select_antenna('4');
}

function ant_switch_select_5(path,val) {
        ant_switch_select_antenna('5');
}

function ant_switch_select_6(path,val) {
        ant_switch_select_antenna('6');
}

function ant_switch_select_7(path,val) {
        ant_switch_select_antenna('7');
}

function ant_switch_select_groundall(path,val) {
        setTimeout('w3_radio_unhighlight('+ q(path) +')', w3_highlight_time);
        ant_switch_select_antenna('g');
}

function ant_switch_select_antenna(ant) {
        console.log('ant_switch: switching to antenna '+ant);
	ext_send('SET Antenna='+ant);
        ext_send('GET Antenna');
}

function ant_switch_poll() {
        kiwi_clearInterval(ant_switch_poll_interval);
        ant_switch_poll_interval = setInterval(ant_switch_poll, 5000);
        ext_send('GET Antenna');
}

function ant_switch_process_reply(ant) {
        ant_selected_antenna = ant;

        for (var tmp=0; tmp<7; tmp++) {
                var elid='cl-id-btn-grp-'+tmp;
                if (w3_el_id(elid) !=  null) w3_unhighlight(elid);
        }

        if (ant == 'g') {
                console.log('ant_switch: all antennas grouded');
                ant_display_update('Thunderstorm mode. All antennas are grounded.');
        } else {
                console.log('ant_switch: antenna '+ ant_selected_antenna +' in use');
                ant_display_update('Antenna '+ant_selected_antenna + ": "+ext_get_cfg_param_string('ant_switch.ant'+ant_selected_antenna+'desc', ''));
                for (tmp=0; tmp<6; tmp++) {
                        if (ant == (tmp+1)) {
                                 var elid='cl-id-btn-grp-'+tmp;
                                 if (w3_el_id(elid) != null) w3_highlight(elid);
                        }
                }
        }
}

function ant_display_update(ant) {
        html('id-ant-display-selected').innerHTML = ant;
        var el = html('rx-antenna');
        if (el != undefined && ant) {
                el.innerHTML = ant;
        }
}
<|endoftext|>


<commit_before>import React from 'react';
import TextField from '../../node_modules/material-ui/lib/text-field';
import RaisedButton from '../../node_modules/material-ui/lib/raised-button';
import DropDownMenu from '../../node_modules/material-ui/lib/DropDownMenu';
import MenuItem from '../../node_modules/material-ui/lib/menus/menu-item';
import { History } from 'react-router';
import reactMixin from 'react-mixin';
import {BinaryClient} from 'binaryjs-client';
import $ from '../../public/js/jquery-1.11.1.min';

class BroadcastSetup extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      audioSelect : document.querySelector('select#audioSource'),
      name: (new Date).getTime().toString() + Math.random().toFixed(2),
      broadcaster: 'anonymous',
      desc: 'Hi, I\'m anonymous and you\'re listening to QuantumRadio',
      isInitializing: false,
      isLoggedIn: false,
      isLive: true,
      favorites: []
    };
    var that = this;
    this.gotSources = function(sourceInfos) {
      for (var i = 0; i !== sourceInfos.length; i++) {
        var sourceInfo = sourceInfos[i];
        if (sourceInfo.kind === 'audio') {
          var option = document.createElement('option');
          option.value = sourceInfo.id;
          option.text = sourceInfo.label || 'microphone' + (that.state.audioSelect.length + 1);
          that.state.audioSelect.appendChild(option);
        }
      }
    }
  }

  componentDidMount() {
    if (!this.state.isLoggedIn) {
      SC.initialize({
        client_id: '67e4bbe5a2b1b64416b0ed84366b34ca',
        redirect_uri: 'http://localhost:3000/auth/soundcloud'
      });

      var component = this;

      // initiate auth popup
      SC.connect()
      .then(function(err, result) {
        return SC.get('/me');
      })
      .then(function(me) {
        localStorage.setItem("me", JSON.stringify(me));
        component.setState({
          isLoggedIn: true
        });
        return SC.get('/me/favorites');
      })
      .then(function(favorites) {
        localStorage.setItem("favorites", JSON.stringify(favorites));
        this.setState({
          favorites: favorites
        });
      });
    this.state.audioSelect = document.querySelector('select#audioSource');

    MediaStreamTrack.getSources(this.gotSources);
    }

    this.state.renderAudio = function(data) {
      var canvas = document.getElementById("canvas"),
      width = canvas.width,
      height = canvas.height,
      context = canvas.getContext('2d');
      context.clearRect(0, 0, width, height);
      var step = Math.ceil(data.length / width);
      var amp = height / 2;
      for (var i = 0; i < width; i++) {
        var min = 1.0;
        var max = -1.0;
        for (var j = 0; j < step; j++) {
          var datum = data[(i * step) + j];
          if (datum < min)
            min = datum;
          if (datum > max)
            max = datum;
        }
        context.fillRect(i, (1 + min) * amp, 1, Math.max(1, (max - min) * amp));
      }
    };
  }

    this.state.renderAudio = function(data) {
      var canvas = document.getElementById("canvas"),
      width = canvas.width,
      height = canvas.height,
      context = canvas.getContext('2d');
      context.clearRect(0, 0, width, height);
      var step = Math.ceil(data.length / width);
      var amp = height / 2;
      for (var i = 0; i < width; i++) {
        var min = 1.0;
        var max = -1.0;
        for (var j = 0; j < step; j++) {
          var datum = data[(i * step) + j];
          if (datum < min)
            min = datum;
          if (datum > max)
            max = datum;
        }
        context.fillRect(i, (1 + min) * amp, 1, Math.max(1, (max - min) * amp));
      }
    };
    // if (!this.state.isLoggedIn) {
    //   SC.initialize({
    //     client_id: '67e4bbe5a2b1b64416b0ed84366b34ca',
    //     redirect_uri: 'http://localhost:3000/auth/soundcloud'
    //   });

    //   // initiate auth popup
    //   SC.connect()
      // .then(function(err, result) {
      //   return SC.get('/me');
      // })
      // .then(function(me) {
      //   this.setState({
      //     isLoggedIn: true
      //   })
      //   .catch(function(error) {

      //   alert(error);
      //   })
      // });
}

stationNameInput(event) {
  this.setState({
    name: event.target.value
  });
}

stationBroadcasterInput(event) {
  this.setState({
    broadcaster: event.target.value
  });
}

stationDescriptionInput(event) {
  this.setState({
    desc: event.target.value
  });
}

stationNameInput(event) {
  this.setState({
    name: event.target.value
  });
}

stationBroadcasterInput(event) {
  this.setState({
    broadcaster: event.target.value
  });
}

stationDescriptionInput(event) {
  this.setState({
    desc: event.target.value
  });
}

startBroadcast() {

          //I need the id to be generated before this point
          var serverURL = "http://localhost:3000/api/stream";

          //I need to pass state into the success callback
          var that = this;

    //fetch can't run JSON
    $.ajax({
      url: serverURL,
      method: 'POST',
      contentType:"application/x-www-form-urlencoded",
      data: {
        name: this.state.name,
        creator: "John Doe",
        desc: this.state.desc,
        lng: 40,
        lat: 30
      },
      success: function(data) {
        console.log('id from database ', data._id);
        var streamId = data._id;
        console.log('when I first assign a streamId client-side, it is: ', streamId);
        var Broadcaster = function(streamId, inputSourcesCB, renderAudioCallback) {
          console.log('when I pass streamId into Broadcaster, it is: ', streamId);
            navigator.getUserMedia = navigator.getUserMedia ||
            navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
         var protocol = (window.location.protocol === "https:") ? 'wss://' : 'ws://';

         //binaryJS client - server/socket connection
         this.client = new BinaryClient(protocol + document.location.host + '/binary-endpoint');
         this.stream;
         this.context = new AudioContext();

         // this.contextSampleRate = this.context.sampleRate;
         this.client.on('open', function() {
          console.log('client opened');
          this.stream = this.client.createStream({
            sampleRate: this.context.SampleRate,
            //we need some way to access the actual streamId from here
            streamId: streamId
          });
          console.log('when I create the PCM stream, streamId is: ', this.stream.meta.streamId);
          console.log('stream sRate is: ', this.stream.meta.sampleRate);
        }.bind(this));

         this.audioSource;
         this.recorder;
         this.context;
         //todo - handle api not supported
         if (typeof MediaStreamTrack === 'undefined' ||
          typeof MediaStreamTrack.getSources === 'undefined') {
          alert('This browser does not support MediaStreamTrack.\n\nTry Chrome.');
      }
      else {
        console.log('inputSourcesCB is: ', inputSourcesCB);
        MediaStreamTrack.getSources(inputSourcesCB);
      }

      this.renderAudioCallback = renderAudioCallback;
    }

      Broadcaster.prototype.start = function() {
        if (!this.audioSource) {
          return 'Broadcast source not set!';
        }

        var constraints = {
          audio: {
            optional: [{
              sourceId: this.audioSource
            }]
          },
          video: false
        };

        navigator.getUserMedia(constraints, function(stream) {
          var audioInput = this.context.createMediaStreamSource(stream);

          var bufferSize = 0; // let implementation decide
          this.recorder = this.context.createScriptProcessor(bufferSize, 2, 2);

          this.recorder.onaudioprocess = function(e) {
            this.onAudio(e);
          }.bind(this);

    Broadcaster.prototype.stop = function() {
      this.recorder.disconnect();
      this.client.close();
    }

    Broadcaster.prototype.setAudioSource = function(value) {
      this.audioSource = value;
    }

    Broadcaster.prototype.onAudio = function(e) {
      var left = e.inputBuffer.getChannelData(0);
      var right = e.inputBuffer.getChannelData(1);

      var stereoBuff = this._interleave(left, right);

      this.stream.write(this._convertFloat32ToInt16(stereoBuff));
      if (this.renderAudioCallback) {
          this.renderAudioCallback(left); //callback to render audio value
        }
      }

      Broadcaster.prototype._convertFloat32ToInt16 = function(buffer) {
        var l = buffer.length;
        var buf = new Int16Array(l);
        while (l--) {
          buf[l] = Math.min(1, buffer[l]) * 0x7FFF;
        }
        return buf.buffer;
      }

      Broadcaster.prototype._interleave = function(leftChannel, rightChannel) {
        var length = leftChannel.length + rightChannel.length;
        var result = new Float32Array(length);

        var inputIndex = 0;

        for (var index = 0; index < length;) {
          result[index++] = leftChannel[inputIndex];
          result[index++] = rightChannel[inputIndex];
          inputIndex++;
        }
        return result;
      }

    // this.props.history.push({
    //   pathname: '/broadcast/live'
    // })

// var streamId = 1;
        console.log('this.state.renderAudio is: ', that.state.renderAudio);
        var bc = new Broadcaster(streamId, that.gotSources, that.state.renderAudio);
        var audioSource = that.state.audioSelect.value;
        //need to set audio source before calling start
        bc.audioSource = audioSource;
        console.log('my audio Source is: ', audioSource);
        bc.start();
      },
      error: function(xhr, status, err) {

      }});
}

render() {
  var partial;
  if (this.state.isLoggedIn) {
    partial = (

      Audio source: <select id="audioSource"></select>

Tell us about your station...

      <TextField onChange={this.stationNameInput.bind(this)}
      hintText="Station Name"
      floatingLabelText="Station Name"
      />

      <TextField onChange={this.stationBroadcasterInput.bind(this)}
      hintText="Broadcast Name"
      floatingLabelText="Broadcast Name"
      />

      <TextField onChange={this.stationDescriptionInput.bind(this)}
      hintText="Description"
      floatingLabelText="Description"
      />

      <RaisedButton primary={true} onClick={this.startBroadcast.bind(this)} label="Start Broadcasting"/>

      <canvas width="600" height="100" id="canvas"></canvas>

      )
  } else {
    partial = (

Broadcasting requires a SoundCloud account. Please sign in!

      )
  }

  return partial;
}
}

var styles = {
  container: {
    'flexDirection': 'column',
    'justifyContent': 'center',
    'alignContent': 'center'
  },
  title:{
    'fontFamily':'Roboto, sans-serif'
  }
}

reactMixin.onClass(BroadcastSetup, History);

export default BroadcastSetup;
<commit_msg>minor formatting and edits<commit_after>import React from 'react';
import TextField from '../../node_modules/material-ui/lib/text-field';
import RaisedButton from '../../node_modules/material-ui/lib/raised-button';
import DropDownMenu from '../../node_modules/material-ui/lib/DropDownMenu';
import MenuItem from '../../node_modules/material-ui/lib/menus/menu-item';
import {
  History
}
from 'react-router';
import reactMixin from 'react-mixin';
import {
  BinaryClient
}
from 'binaryjs-client';
import $ from '../../public/js/jquery-1.11.1.min';

class BroadcastSetup extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      audioSelect: document.querySelector('select#audioSource'),
      name: (new Date).getTime().toString() + Math.random().toFixed(2),
      broadcaster: 'anonymous',
      desc: 'Hi, I\'m anonymous and you\'re listening to QuantumRadio',
      isInitializing: false,
      isLoggedIn: false,
      isLive: true,
      favorites: []
    };
    var that = this;
    this.gotSources = function(sourceInfos) {
      for (var i = 0; i !== sourceInfos.length; i++) {
        var sourceInfo = sourceInfos[i];
        if (sourceInfo.kind === 'audio') {
          var option = document.createElement('option');
          option.value = sourceInfo.id;
          option.text = sourceInfo.label || 'microphone' + (that.state.audioSelect.length + 1);
          that.state.audioSelect.appendChild(option);
        }
      }
    }
  }

  componentDidMount() {
      if (!this.state.isLoggedIn) {
        SC.initialize({
          client_id: '67e4bbe5a2b1b64416b0ed84366b34ca',
          redirect_uri: 'http://localhost:3000/auth/soundcloud'
        });

        var component = this;

        // initiate auth popup
        SC.connect()
          .then(function(err, result) {
            return SC.get('/me');
          })
          .then(function(me) {
            localStorage.setItem("me", JSON.stringify(me));
            component.setState({
              isLoggedIn: true
            });
            return SC.get('/me/favorites');
          })
          .then(function(favorites) {
            localStorage.setItem("favorites", JSON.stringify(favorites));
            this.setState({
              favorites: favorites
            });
          });
        this.state.audioSelect = document.querySelector('select#audioSource');

        MediaStreamTrack.getSources(this.gotSources);
      }

      this.state.renderAudio = function(data) {
        var canvas = document.getElementById("canvas"),
          width = canvas.width,
          height = canvas.height,
          context = canvas.getContext('2d');
        context.clearRect(0, 0, width, height);
        var step = Math.ceil(data.length / width);
        var amp = height / 2;
        for (var i = 0; i < width; i++) {
          var min = 1.0;
          var max = -1.0;
          for (var j = 0; j < step; j++) {
            var datum = data[(i * step) + j];
            if (datum < min)
              min = datum;
            if (datum > max)
              max = datum;
          }
          context.fillRect(i, (1 + min) * amp, 1, Math.max(1, (max - min) * amp));
        }
      };
    }
    // if (!this.state.isLoggedIn) {
    //   SC.initialize({
    //     client_id: '67e4bbe5a2b1b64416b0ed84366b34ca',
    //     redirect_uri: 'http://localhost:3000/auth/soundcloud'
    //   });

  //   // initiate auth popup
  //   SC.connect()
  // .then(function(err, result) {
  //   return SC.get('/me');
  // })
  // .then(function(me) {
  //   this.setState({
  //     isLoggedIn: true
  //   })
  //   .catch(function(error) {

  //   alert(error);
  //   })
  // });
  // }

  stationNameInput(event) {
    this.setState({
      name: event.target.value
    });
  }

  stationBroadcasterInput(event) {
    this.setState({
      broadcaster: event.target.value
    });
  }

  stationDescriptionInput(event) {
    this.setState({
      desc: event.target.value
    });
  }

  stationNameInput(event) {
    this.setState({
      name: event.target.value
    });
  }

  stationBroadcasterInput(event) {
    this.setState({
      broadcaster: event.target.value
    });
  }

  stationDescriptionInput(event) {
    this.setState({
      desc: event.target.value
    });
  }

  startBroadcast() {

    //I need the id to be generated before this point
    var serverURL = "http://localhost:3000/api/stream";

    //I need to pass state into the success callback
    var that = this;

    //fetch can't run JSON
    $.ajax({
      url: serverURL,
      method: 'POST',
      contentType: "application/x-www-form-urlencoded",
      data: {
        name: this.state.name,
        creator: "John Doe",
        desc: this.state.desc,
        lng: 40,
        lat: 30
      },
      success: function(data) {
        console.log('id from database ', data._id);
        var streamId = data._id;
        console.log('when I first assign a streamId client-side, it is: ', streamId);
        var Broadcaster = function(streamId, inputSourcesCB, renderAudioCallback) {
          console.log('when I pass streamId into Broadcaster, it is: ', streamId);
          navigator.getUserMedia = navigator.getUserMedia ||
            navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
          var protocol = (window.location.protocol === "https:") ? 'wss://' : 'ws://';

          //binaryJS client - server/socket connection
          this.client = new BinaryClient(protocol + document.location.host + '/binary-endpoint');
          this.stream;
          this.context = new AudioContext();

          // this.contextSampleRate = this.context.sampleRate;
          this.client.on('open', function() {
            console.log('client opened');
            this.stream = this.client.createStream({
              sampleRate: this.context.SampleRate,
              //we need some way to access the actual streamId from here
              streamId: streamId
            });
            console.log('when I create the PCM stream, streamId is: ', this.stream.meta.streamId);
            console.log('stream sRate is: ', this.stream.meta.sampleRate);
          }.bind(this));

          this.audioSource;
          this.recorder;
          this.context;
          //todo - handle api not supported
          if (typeof MediaStreamTrack === 'undefined' ||
            typeof MediaStreamTrack.getSources === 'undefined') {
            alert('This browser does not support MediaStreamTrack.\n\nTry Chrome.');
          } else {
            console.log('inputSourcesCB is: ', inputSourcesCB);
            MediaStreamTrack.getSources(inputSourcesCB);
          }

          this.renderAudioCallback = renderAudioCallback;
        }

        Broadcaster.prototype.start = function() {
          if (!this.audioSource) {
            return 'Broadcast source not set!';
          }

          var constraints = {
            audio: {
              optional: [{
                sourceId: this.audioSource
              }]
            },
            video: false
          };

          navigator.getUserMedia(constraints, function(stream) {
            var audioInput = this.context.createMediaStreamSource(stream);

            var bufferSize = 0; // let implementation decide
            this.recorder = this.context.createScriptProcessor(bufferSize, 2, 2);

            this.recorder.onaudioprocess = function(e) {
              this.onAudio(e);
            }.bind(this);

            audioInput.connect(this.recorder);
            this.recorder.connect(this.context.destination);

          }.bind(this), function(e) {
            console.log('error connectiing to audio source');
            throw e;
          });
        }

        Broadcaster.prototype.stop = function() {
          this.recorder.disconnect();
          this.client.close();
        }

        Broadcaster.prototype.setAudioSource = function(value) {
          this.audioSource = value;
        }

        Broadcaster.prototype.onAudio = function(e) {
          var left = e.inputBuffer.getChannelData(0);
          var right = e.inputBuffer.getChannelData(1);

          var stereoBuff = this._interleave(left, right);

          this.stream.write(this._convertFloat32ToInt16(stereoBuff));
          if (this.renderAudioCallback) {
            this.renderAudioCallback(left); //callback to render audio value
          }
        }

        Broadcaster.prototype._convertFloat32ToInt16 = function(buffer) {
          var l = buffer.length;
          var buf = new Int16Array(l);
          while (l--) {
            buf[l] = Math.min(1, buffer[l]) * 0x7FFF;
          }
          return buf.buffer;
        }

        Broadcaster.prototype._interleave = function(leftChannel, rightChannel) {
          var length = leftChannel.length + rightChannel.length;
          var result = new Float32Array(length);

          var inputIndex = 0;

          for (var index = 0; index < length;) {
            result[index++] = leftChannel[inputIndex];
            result[index++] = rightChannel[inputIndex];
            inputIndex++;
          }
          return result;
        }

        // this.props.history.push({
        //   pathname: '/broadcast/live'
        // })
        // var streamId = 1;
        console.log('this.state.renderAudio is: ', that.state.renderAudio);
        var bc = new Broadcaster(streamId, that.gotSources, that.state.renderAudio);
        var audioSource = that.state.audioSelect.value;
        //need to set audio source before calling start
        bc.audioSource = audioSource;
        console.log('my audio Source is: ', audioSource);
        bc.start();

      },
      error: function(xhr, status, err) {

      }
    });
  }

  render() {
    var partial;
    if (this.state.isLoggedIn) {
      partial = (

         Audio source: <select id="audioSource"></select >

 Tell us about your station...
 < TextField onChange = {
      this.stationNameInput.bind(this)
    }
    hintText = "Station Name"
    floatingLabelText = "Station Name" / >

      < TextField onChange = {
        this.stationBroadcasterInput.bind(this)
      }
    hintText = "Broadcast Name"
    floatingLabelText = "Broadcast Name" / >

      < TextField onChange = {
        this.stationDescriptionInput.bind(this)
      }
    hintText = "Description"
    floatingLabelText = "Description" / >

      < RaisedButton primary = {
        true
      }
    onClick = {
      this.startBroadcast.bind(this)
    }
    label = "Start Broadcasting" / >

      < canvas width = "600"
    height = "100"
    id = "canvas" > < /canvas>

  )
} else {
  partial = (

 Broadcasting requires a SoundCloud account.Please sign in !

  )
}

return partial;
}
}

var styles = {
  container: {
    'flexDirection': 'column',
    'justifyContent': 'center',
    'alignContent': 'center'
  },
  title: {
    'fontFamily': 'Roboto, sans-serif'
  }
}

reactMixin.onClass(BroadcastSetup, History);

export default BroadcastSetup;<|endoftext|>


<commit_before>angular.module('paperworkNotes')
    .directive('pwNoteContent', function ($compile) {
        return {
            restrict: 'AE',
            scope: {
                content: '='
            },
            link: function (scope, element) {
                scope.$watch('content', function () {
                    if (scope.content) {
                        var content = angular.element(scope.content), code = content.find('code');

                        code.each(function () {
                            var $this = angular.element(this, content);

                            $this.replaceWith(
                                angular.element('<div>', {
                                    'class': $this.attr('class'),
                                    'language': $this.attr('class').replace(/language-/, ''),
                                    'hljs': true,
                                    'html': $this.html()
                                })
                            );
                        });

                        element.html(content.html());

                        $compile(element.contents())(scope);
                    }
                });

            }
        }
    });<commit_msg>Fix #109 - Fixed escaping of < and ><commit_after>angular.module('paperworkNotes')
    .directive('pwNoteContent', function ($compile) {
        return {
            restrict: 'AE',
            scope: {
                content: '='
            },
            link: function (scope, element) {
                scope.$watch('content', function () {
                    if (scope.content) {
                        var content = angular.element(scope.content), code = content.find('code');

                        code.each(function () {
                            var $this = angular.element(this, content);

                            $this.replaceWith(
                                angular.element('<div>', {
                                    'class': $this.attr('class'),
                                    'language': $this.attr('class').replace(/language-/, ''),
                                    'hljs': true,
                                    'no-escape': true,
                                    'html': $this.html()
                                })
                            );
                        });

                        element.html(content.html());

                        $compile(element.contents())(scope);
                    }
                });

            }
        }
    });<|endoftext|>


<commit_before>/*global $, ace, Materialize, newCSS, CSS_ID, ADMIN*/
(function () {
    $(window).bind('beforeunload', function(){
        if ($("#live_preview_warning").is(":visible")) {
            return true;
        }
    });

    if($("#css_editor").length != 0) {
        var editor = ace.edit("css_editor");
    }

    function postForm() {
        var name = $('#css_name').val();
        var var_enabled = $("#toggleCSSVar").is(':checked');
        var css = null;
        if($("#css_editor").length != 0) {
            css = editor.getValue();
            if (css.length == 0) {
                css = null;
            }
        }

        var payload = {"name": name, "variables_enabled": var_enabled, "css": css};
        if (var_enabled) {
            var variables = JSON.stringify(formatCSSVars());
            payload.variables = variables;
        }
        if (ADMIN) {
            var user_id = $('#css_user_id').val();
            payload.user_id = user_id;
        }

        var funct = $.ajax({
            dataType: "json",
            method: "POST",
            data: payload
        });
        return funct.promise();
    }

    $(function(){
        if($("#css_editor").length != 0) {
            editor.getSession().setMode("ace/mode/css");
            editor.setTheme("ace/theme/chrome");

            editor.commands.addCommand({
                name: 'save',
                bindKey: {win: "Ctrl-S", "mac": "Cmd-S"},
                exec: function(editor) {
                    $('#submit-btn').trigger('click');
                }
            });
            editor.on("blur", function () {
                update_live_preview();
            });
        }
        $("#submit-btn").click(submitForm);

        if (!newCSS) {
            $("#delete-btn").click(delete_css);
        }

        $(".updateLivePreview").on("blur", function () {
            update_live_preview();
        });

        $("#toggleCSSVar").on("change", function () {
            update_live_preview();
        });

        $("#preview_guildid").keyup(function (event) {
            if (event.keyCode == 13) {
                change_live_preview();
            }
        });
    });

    function formatCSSVars() {
        return {
            "modal": $("#css_var_modal").val(),
            "noroleusers": $("#css_var_noroleusers").val(),
            "main": $("#css_var_main").val(),
            "placeholder": $("#css_var_placeholder").val(),
            "sidebardivider": $("#css_var_sidebardivider").val(),
            "leftsidebar": $("#css_var_leftsidebar").val(),
            "rightsidebar": $("#css_var_rightsidebar").val(),
            "header": $("#css_var_header").val(),
            "chatmessage": $("#css_var_chatmessage").val(),
            "discrim": $("#css_var_discrim").val(),
            "chatbox": $("#css_var_chatbox").val(),
        };
    }

    function submitForm() {
        var name = $('#css_name').val();
        if (name == "") {
            Materialize.toast("Don't forget to name your CSS!", 10000);
            return;
        }

        var formPost = postForm();
        formPost.done(function (data) {
            if (newCSS) {
                window.location.href = "edit/" + data.id;
            } else {
                Materialize.toast('CSS Updated!', 10000);
                update_live_preview(true);
                $("#live_preview_warning").hide();
            }
        });
        formPost.fail(function () {
            Materialize.toast('Oh no! Something has failed posting your CSS!', 10000);
        });
    }

    function delete_css() {
        var candelete = confirm("Do you really want to delete this css???");
        if (!candelete) {
            return;
        }

        $.ajax({
          type: 'DELETE',
          success: function() {
              alert("You have successfully deleted the CSS!");
                    if (ADMIN) {
                        window.location.href = "/admin/custom_css";
                    } else {
                        window.location.href = "/user/dashboard";
                    }
              },
          error: function() {
              Materialize.toast('Oh no! Something has failed deleting your CSS!', 10000);
          }
        });
    }

    function update_live_preview(refresh=false) {
        if (refresh) {
            $('#iframepreview').attr('src', $('#iframepreview').attr('src'));
            return;
        }
        $("#live_preview_warning").show();
        var output = "";

        if ($("#toggleCSSVar").is(':checked')) {
            var cssVars = formatCSSVars();
            output += ":root {";
            for (var key in cssVars) {
              if (cssVars.hasOwnProperty(key)) {
                output += "--" + key + ":" + cssVars[key] + ";";
              }
            }
            output += "}";
        }

        if($("#css_editor").length != 0) {
            output += editor.getValue();
        }

        $("#iframepreview").contents().find('#user-defined-css').html(output);
    }

    function change_live_preview() {
        var src = "/embed/" + $("#preview_guildid").val();
        if (!newCSS) {
            src += "?css=" + CSS_ID;
        }
        $("#iframepreview").attr("src", src);
        update_live_preview();
    }
})();<commit_msg>Fix bug where it would alert warning when saving custom css<commit_after>/*global $, ace, Materialize, newCSS, CSS_ID, ADMIN*/
(function () {
    $(window).bind('beforeunload', function(){
        if ($("#live_preview_warning").is(":visible")) {
            return true;
        }
    });

    if($("#css_editor").length != 0) {
        var editor = ace.edit("css_editor");
    }

    function postForm() {
        var name = $('#css_name').val();
        var var_enabled = $("#toggleCSSVar").is(':checked');
        var css = null;
        if($("#css_editor").length != 0) {
            css = editor.getValue();
            if (css.length == 0) {
                css = null;
            }
        }

        var payload = {"name": name, "variables_enabled": var_enabled, "css": css};
        if (var_enabled) {
            var variables = JSON.stringify(formatCSSVars());
            payload.variables = variables;
        }
        if (ADMIN) {
            var user_id = $('#css_user_id').val();
            payload.user_id = user_id;
        }

        var funct = $.ajax({
            dataType: "json",
            method: "POST",
            data: payload
        });
        return funct.promise();
    }

    $(function(){
        if($("#css_editor").length != 0) {
            editor.getSession().setMode("ace/mode/css");
            editor.setTheme("ace/theme/chrome");

            editor.commands.addCommand({
                name: 'save',
                bindKey: {win: "Ctrl-S", "mac": "Cmd-S"},
                exec: function(editor) {
                    $('#submit-btn').trigger('click');
                }
            });
            editor.on("blur", function () {
                update_live_preview();
            });
        }
        $("#submit-btn").click(submitForm);

        if (!newCSS) {
            $("#delete-btn").click(delete_css);
        }

        $(".updateLivePreview").on("blur", function () {
            update_live_preview();
        });

        $("#toggleCSSVar").on("change", function () {
            update_live_preview();
        });

        $("#preview_guildid").keyup(function (event) {
            if (event.keyCode == 13) {
                change_live_preview();
            }
        });
    });

    function formatCSSVars() {
        return {
            "modal": $("#css_var_modal").val(),
            "noroleusers": $("#css_var_noroleusers").val(),
            "main": $("#css_var_main").val(),
            "placeholder": $("#css_var_placeholder").val(),
            "sidebardivider": $("#css_var_sidebardivider").val(),
            "leftsidebar": $("#css_var_leftsidebar").val(),
            "rightsidebar": $("#css_var_rightsidebar").val(),
            "header": $("#css_var_header").val(),
            "chatmessage": $("#css_var_chatmessage").val(),
            "discrim": $("#css_var_discrim").val(),
            "chatbox": $("#css_var_chatbox").val(),
        };
    }

    function submitForm() {
        var name = $('#css_name').val();
        if (name == "") {
            Materialize.toast("Don't forget to name your CSS!", 10000);
            return;
        }

        $("#live_preview_warning").hide();
        var formPost = postForm();
        formPost.done(function (data) {
            if (newCSS) {
                window.location.href = "edit/" + data.id;
            } else {
                Materialize.toast('CSS Updated!', 10000);
                update_live_preview(true);
                $("#live_preview_warning").hide();
            }
        });
        formPost.fail(function () {
            Materialize.toast('Oh no! Something has failed posting your CSS!', 10000);
            $("#live_preview_warning").show();
        });
    }

    function delete_css() {
        var candelete = confirm("Do you really want to delete this css???");
        if (!candelete) {
            return;
        }

        $.ajax({
          type: 'DELETE',
          success: function() {
              alert("You have successfully deleted the CSS!");
                    if (ADMIN) {
                        window.location.href = "/admin/custom_css";
                    } else {
                        window.location.href = "/user/dashboard";
                    }
              },
          error: function() {
              Materialize.toast('Oh no! Something has failed deleting your CSS!', 10000);
          }
        });
    }

    function update_live_preview(refresh=false) {
        if (refresh) {
            $('#iframepreview').attr('src', $('#iframepreview').attr('src'));
            return;
        }
        $("#live_preview_warning").show();
        var output = "";

        if ($("#toggleCSSVar").is(':checked')) {
            var cssVars = formatCSSVars();
            output += ":root {";
            for (var key in cssVars) {
              if (cssVars.hasOwnProperty(key)) {
                output += "--" + key + ":" + cssVars[key] + ";";
              }
            }
            output += "}";
        }

        if($("#css_editor").length != 0) {
            output += editor.getValue();
        }

        $("#iframepreview").contents().find('#user-defined-css').html(output);
    }

    function change_live_preview() {
        var src = "/embed/" + $("#preview_guildid").val();
        if (!newCSS) {
            src += "?css=" + CSS_ID;
        }
        $("#iframepreview").attr("src", src);
        update_live_preview();
    }
})();<|endoftext|>


<commit_before>var assert = require('assert')

module.exports = function () {}

module.exports.prototype =
  { name: 'commaSeparatedAttributes'

  , configure: function (isRequired) {

      assert(typeof isRequired === 'boolean'
        , this.name + ' option requires boolean value')

      this._isRequired = isRequired

    }

  , lint: function (file) {

      var attributeNodes = file.ast.nodes.filter(function (node) {
        return node.type === 'Tag' && node.attrs
      })

      attributeNodes.forEach(function (node) {
        var attributeCount = node.attrs.length
          , separator = this._isRequired ? ', ' : ' '
          , message = this._isRequired ? 'Missing comma after attribute' : 'Comma after attribute'
          , line = file.lines[node.line - 1]
          , regex = new RegExp(/\((.+)\)/g)
          , match

        while ((match = regex.exec(line)) !== null) {
          var attributes = match[1].split(separator).filter(filterAttributes)

          if (attributes.length !== attributeCount) {
            file.errors.push({ message: message, filename: file.filename, line: node.line })
          }
        }
      }, this)

      function filterAttributes(attribute) {
        return attribute.lastIndexOf(',') !== attribute.length - 1
      }

    }
  }
<commit_msg>> Refactor test to use `tokens` rather than `ast`<commit_after>var assert = require('assert')

module.exports = function () {}

module.exports.prototype =
  { name: 'commaSeparatedAttributes'

  , configure: function (isRequired) {

      assert(typeof isRequired === 'boolean'
        , this.name + ' option requires boolean value')

      this._isRequired = isRequired

    }

  , lint: function (file) {

      var attributeTokens = file.tokens.filter(function (token) {
        return token.type === 'attrs' && token.attrs
      })

      attributeTokens.forEach(function (token) {
        var attributeCount = token.attrs.length
          , separator = this._isRequired ? ', ' : ' '
          , message = this._isRequired ? 'Missing comma after attribute' : 'Comma after attribute'
          , lineNumber = token.line
          , line = file.lines[lineNumber - 1]
          , regex = new RegExp(/\((.+)\)/g)
          , match

        while ((match = regex.exec(line)) !== null) {
          var attributes = match[1].split(separator).filter(filterAttributes)

          if (attributes.length !== attributeCount) {
            file.errors.push({ message: message, filename: file.filename, line: lineNumber })
          }
        }
      }, this)

      function filterAttributes(attribute) {
        return attribute.lastIndexOf(',') !== attribute.length - 1
      }

    }
  }
<|endoftext|>