<commit_before>'use strict';

// external deps
var ObjectId = require('mongodb').ObjectId;

// internal deps
require('mongodb-toolkit');
var BateeqModels = require('bateeq-models');
var map = BateeqModels.map;

var TransferOutDoc = BateeqModels.inventory.TransferOutDoc;
var TransferOutItem = BateeqModels.inventory.TransferOutItem;

const moduleId = "EFR-KB/RTT";

module.exports = class TokoTransferStokManager {
    constructor(db, user) {
        this.db = db;
        this.user = user;
        this.transferOutDocCollection = this.db.use(map.inventory.TransferOutDoc);
        var StorageManager = require('./storage-manager');
        this.storageManager = new StorageManager(db, user);

        var ArticleVariantManager = require('../core/article/article-variant-manager');
        this.articleVariantManager = new ArticleVariantManager(db, user);

        var InventoryManager = require('./inventory-manager');
        this.inventoryManager = new InventoryManager(db, user);

        var TrasferOutManager = require('./transfer-out-doc-manager');
        this.transferOutDocManager = new TrasferOutManager(db, user);

        var ModuleManager = require('../core/module-manager');
        this.moduleManager = new ModuleManager(db, user);

        var ModuleSeedManager = require('../core/module-seed-manager');
        this.moduleSeedManager = new ModuleSeedManager(db, user);
    }

    read(paging) {
        var _paging = Object.assign({
            page: 1,
            size: 20,
            order: '_id',
            asc: true
        }, paging);

        return new Promise((resolve, reject) => {
            var deleted = {
                _deleted: false
            };
            var query = _paging.keyword ? {
                '$and': [deleted]
            } : deleted;

            if (_paging.keyword) {
                var regex = new RegExp(_paging.keyword, "i");
                var filterCode = {
                    'code': {
                        '$regex': regex
                    }
                };
                var $or = {
                    '$or': [filterCode]
                };

                query['$and'].push($or);
            }

            this.transferOutDocCollection
                .where(query)
                .page(_paging.page, _paging.size)
                .orderBy(_paging.order, _paging.asc)
                .execute()
                .then(transferOutDocs => {
                    resolve(transferOutDocs);
                })
                .catch(e => {
                    reject(e);
                });
        });
    }

    getById(id) {
        return new Promise((resolve, reject) => {
            var query = {
                _id: new ObjectId(id),
                _deleted: false
            };
            this.getSingleByQuery(query)
                .then(transferOutDoc => {
                    resolve(transferOutDoc);
                })
                .catch(e => {
                    reject(e);
                });
        });
    }

    getByIdOrDefault(id) {
        return new Promise((resolve, reject) => {
            var query = {
                _id: new ObjectId(id),
                _deleted: false
            };
            this.getSingleOrDefaultByQuery(query)
                .then(transferOutDoc => {
                    resolve(transferOutDoc);
                })
                .catch(e => {
                    reject(e);
                });
        });
    }

    getSingleByQuery(query) {
        return new Promise((resolve, reject) => {
            this.transferOutDocCollection
                .single(query)
                .then(transferOutDoc => {
                    resolve(transferOutDoc);
                })
                .catch(e => {
                    reject(e);
                });
        })
    }

    getSingleOrDefaultByQuery(query) {
        return new Promise((resolve, reject) => {
            this.transferOutDocCollection
                .singleOrDefault(query)
                .then(transferOutDoc => {
                    resolve(transferOutDoc);
                })
                .catch(e => {
                    reject(e);
                });
        })
    }

    create(transferOutDoc) {
        return new Promise((resolve, reject) => {
            this._validate(transferOutDoc)
                .then(validTransferOutDoc => {
                    var now = new Date();
                    var year = now.getFullYear();
                    var month = now.getMonth() + 1;
                    this.moduleSeedManager
                        .getModuleSeed(moduleId, year, month)
                        .then(moduleSeed => {
                            var number = ++moduleSeed.seed;
                            var zero = 4 - number.toString().length + 1;
                            var runningNumber = Array(+(zero > 0 && zero)).join("0") + number;
                            zero = 2 - month.toString().length + 1;
                            var formattedMonth = Array(+(zero > 0 && zero)).join("0") + month;
                            validTransferOutDoc.code = `${runningNumber}/${moduleId}/${formattedMonth}/${year}`;
                            this.transferOutDocManager.create(validTransferOutDoc)
                                .then(id => {
                                    this.moduleSeedManager
                                        .update(moduleSeed)
                                        .then(seedId => {
                                            resolve(id);
                                        })
                                        .catch(e => {
                                            reject(e);
                                        })
                                })
                                .catch(e => {
                                    reject(e);
                                })
                        })
                        .catch(e => {
                            reject(e);
                        });
                })
                .catch(e => {
                    reject(e);
                })
        });
    }

    update(transferOutDoc) {
        return new Promise((resolve, reject) => {
            this._validate(transferOutDoc)
                .then(validTransferOutDoc => {
                    this.transferOutDocManager.update(validTransferOutDoc)
                        .then(id => {
                            resolve(id);
                        })
                        .catch(ex => {
                            reject(ex);
                        })
                })
                .catch(e => {
                    reject(e);
                })
        });
    }

    delete(transferOutDoc) {
        return new Promise((resolve, reject) => {
            this._validate(transferOutDoc)
                .then(validTransferOutDoc => {
                    validTransferOutDoc._deleted = true;
                    this.transferOutDocManager.update(validTransferOutDoc)
                        .then(id => {
                            resolve(id);
                        })
                        .catch(e => {
                            reject(e);
                        })
                })
                .catch(e => {
                    reject(e);
                })
        });
    }

    _validate(transferOutDoc) {
        return new Promise((resolve, reject) => {
            var valid = transferOutDoc;
            this.moduleManager.getByCode(moduleId)
                .then(module => {
                    var config = module.config;
                    valid.sourceId = config.sourceId;
                    valid.destinationId = config.destinationId;
                    resolve(valid);
                })
                .catch(e => {
                    reject(new Error(`Unable to load module:${moduleId}`));
                });
        });
    }
}; <commit_msg>remove module-seed and add generateCode in function create<commit_after>'use strict';

// external deps
var ObjectId = require('mongodb').ObjectId;

// internal deps
require('mongodb-toolkit');
var BateeqModels = require('bateeq-models');
var generateCode = require('../../utils/code-generator');
var map = BateeqModels.map;

var TransferOutDoc = BateeqModels.inventory.TransferOutDoc;
var TransferOutItem = BateeqModels.inventory.TransferOutItem;

const moduleId = "EFR-KB/RTT";

module.exports = class TokoTransferStokManager {
    constructor(db, user) {
        this.db = db;
        this.user = user;
        this.transferOutDocCollection = this.db.use(map.inventory.TransferOutDoc);
        var StorageManager = require('./storage-manager');
        this.storageManager = new StorageManager(db, user);

        var ArticleVariantManager = require('../core/article/article-variant-manager');
        this.articleVariantManager = new ArticleVariantManager(db, user);

        var InventoryManager = require('./inventory-manager');
        this.inventoryManager = new InventoryManager(db, user);

        var TrasferOutManager = require('./transfer-out-doc-manager');
        this.transferOutDocManager = new TrasferOutManager(db, user);

        var ModuleManager = require('../core/module-manager');
        this.moduleManager = new ModuleManager(db, user);
    }

    read(paging) {
        var _paging = Object.assign({
            page: 1,
            size: 20,
            order: '_id',
            asc: true
        }, paging);

        return new Promise((resolve, reject) => {
            var deleted = {
                _deleted: false
            };
            var query = _paging.keyword ? {
                '$and': [deleted]
            } : deleted;

            if (_paging.keyword) {
                var regex = new RegExp(_paging.keyword, "i");
                var filterCode = {
                    'code': {
                        '$regex': regex
                    }
                };
                var $or = {
                    '$or': [filterCode]
                };

                query['$and'].push($or);
            }

            this.transferOutDocCollection
                .where(query)
                .page(_paging.page, _paging.size)
                .orderBy(_paging.order, _paging.asc)
                .execute()
                .then(transferOutDocs => {
                    resolve(transferOutDocs);
                })
                .catch(e => {
                    reject(e);
                });
        });
    }

    getById(id) {
        return new Promise((resolve, reject) => {
            var query = {
                _id: new ObjectId(id),
                _deleted: false
            };
            this.getSingleByQuery(query)
                .then(transferOutDoc => {
                    resolve(transferOutDoc);
                })
                .catch(e => {
                    reject(e);
                });
        });
    }

    getByIdOrDefault(id) {
        return new Promise((resolve, reject) => {
            var query = {
                _id: new ObjectId(id),
                _deleted: false
            };
            this.getSingleOrDefaultByQuery(query)
                .then(transferOutDoc => {
                    resolve(transferOutDoc);
                })
                .catch(e => {
                    reject(e);
                });
        });
    }

    getSingleByQuery(query) {
        return new Promise((resolve, reject) => {
            this.transferOutDocCollection
                .single(query)
                .then(transferOutDoc => {
                    resolve(transferOutDoc);
                })
                .catch(e => {
                    reject(e);
                });
        })
    }

    getSingleOrDefaultByQuery(query) {
        return new Promise((resolve, reject) => {
            this.transferOutDocCollection
                .singleOrDefault(query)
                .then(transferOutDoc => {
                    resolve(transferOutDoc);
                })
                .catch(e => {
                    reject(e);
                });
        })
    }

    create(transferOutDoc) {
        return new Promise((resolve, reject) => {
            this._validate(transferOutDoc)
                .then(validTransferOutDoc => {
                    validTransferOutDoc.code = generateCode(moduleId);
                    this.transferOutDocManager.create(validTransferOutDoc)
                        .then(id => {
                            resolve(id);
                        })
                        .catch(e => {
                            reject(e);
                        })
                })

        });
    }

    update(transferOutDoc) {
        return new Promise((resolve, reject) => {
            this._validate(transferOutDoc)
                .then(validTransferOutDoc => {
                    this.transferOutDocManager.update(validTransferOutDoc)
                        .then(id => {
                            resolve(id);
                        })
                        .catch(ex => {
                            reject(ex);
                        })
                })
                .catch(e => {
                    reject(e);
                })
        });
    }

    delete(transferOutDoc) {
        return new Promise((resolve, reject) => {
            this._validate(transferOutDoc)
                .then(validTransferOutDoc => {
                    validTransferOutDoc._deleted = true;
                    this.transferOutDocManager.update(validTransferOutDoc)
                        .then(id => {
                            resolve(id);
                        })
                        .catch(e => {
                            reject(e);
                        })
                })
                .catch(e => {
                    reject(e);
                })
        });
    }

    _validate(transferOutDoc) {
        return new Promise((resolve, reject) => {
            var valid = transferOutDoc;
            this.moduleManager.getByCode(moduleId)
                .then(module => {
                    var config = module.config;
                    valid.sourceId = config.sourceId;
                    valid.destinationId = config.destinationId;
                    resolve(valid);
                })
                .catch(e => {
                    reject(new Error(`Unable to load module:${moduleId}`));
                });
        });
    }
}; <|endoftext|>


<commit_before>'use strict';

const config   = require('./config');
const mongoose = require('mongoose');
const Schema   = mongoose.Schema;
const request  = require('request');
const cheerio  = require('cheerio');
const async    = require('async');
const moment   = require('moment');

const CATALOG_URL = 'http://catalog.oregonstate.edu/';
const COURSE_SEARCH_URL = CATALOG_URL + 'CourseSearcher.aspx?chr=abg';

/////////////////////////////////////////////////
// DATABASE
/////////////////////////////////////////////////
console.log(process.env.MONGO_URL);
mongoose.connect(process.env.MONGO_URL);
const db = mongoose.connection;

const sectionSchema = new Schema({
  term: String,
  startDate: Date,
  endDate: Date,
  session: String,
  crn: {
    type: Number,
    unique: true,
  },
  sec: Number,
  credits: String,
  instructor: String,
  days: String,
  startTime: Date,
  endTime: Date,
  location: String,
  campus: String,
  type: String,
  status: String,
  enrollCap: Number,
  enrolled: Number,
  waitlistCap: Number,
  waitlisted: Number,
  fees: String,
  restrictions: String,
  comments: String,
});

const courseSchema = new Schema({
  title: {
    type: String,
    unique: true,
  },
  abbr: String,
  credits: String,
  desc: String,
  sections: [sectionSchema],
  updated: {
    type: Date,
    default: Date.now,
  },
  meta: {
    likes: Number,
    dislikes: Number,
  },
});

const Course = mongoose.model('Course', courseSchema);

/////////////////////////////////////////////////
// MAIN
/////////////////////////////////////////////////

db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function(callback) {
  console.log('Connected to MongoDB @ ' + db.host + ':' + db.port);
  getCourseLinks(function scrapeComplete(courses, err) {
    console.log('Scrape complete.');
    db.close();
  });
});

function getCourseLinks(callback) {
  request(COURSE_SEARCH_URL, function parseSearchPage(error, res, body) {
    if (!error && res.statusCode === 200) {
      var classURLs = [];
      var $ = cheerio.load(body);

      $('a[id^=\'ctl00_ContentPlaceHolder\']').each(function(i, element) {
        var link = $(this).attr('href');
        classURLs.push(link);
      });

      // First two elements are currently trash, don't attempt to scrape
      classURLs.splice(0, 2);
      return getCourseInfo(CATALOG_URL, classURLs, callback);
    }
  });
}

function getCourseInfo(baseURL, classURLs, callback) {
  var index = 1;

  async.eachSeries(classURLs, function(url, asyncCallback) {
    const classURL = baseURL + url;
    console.log('Scraping ' + index++ + ' of ' + classURLs.length);
    console.log('URL: ' + classURL);

    request(classURL, function scrapeClassPage(error, response, body) {
      if (error) {
        console.error('Error scraping ' + classURL + '\n' + error);
        asyncCallback();
      } else if (response.statusCode !== 200) {
        console.error('Reponse status code == ' + response.statusCode);
        asyncCallback();
      } else {
        parseCourseFromHTML(body);
        asyncCallback();
      }
    });
  },

  function doneScraping(err) {
    if (err) {
      console.error('An error occured scraping: ' + err);
    } else {
      callback();
    }
  });
}

/////////////////////////////////////////////////
// PARSERS
/////////////////////////////////////////////////

function parseCourseFromHTML(htmlBody) {
  var $ = cheerio.load(htmlBody);

  var course = new Course({
    title: parseTitle($),
    abbr: parseAbbr($),
    credits: parseCredits($),
    desc: parseDesc($),
    sections: parseTable($),
  });

  course.save(function(err) {
    if (err) console.error(err);
  });
}

// Gets course title from the class site. Regex's follow these steps:
// Select h3, remove abbreviation, remove credits, replace
// non-words/whitespace with whitespace, remove spaces, tabs &
// newlines, turn multiple spaces into one, remove spaces on ends
function parseTitle($) {
  var title = $('h3').text();
  title = title.replace(/(^[A-Z]{1,4}\s[0-9]{2,3})/, '');
  title = title.replace(/\(([^\)]+)\)/i, '');
  title = title.replace(/[^\w\s]/gi, ' ');
  title = title.replace(/\r?\n|\r|\t/g, '');
  title = title.replace(/\ {2,}/g, ' ');
  return title.trim();
}

// Gets course abbreviation from the class site
function parseAbbr($) {
  return $('h3').text().trim().match(/^[A-Z]{1,4}\s[0-9]{2,3}/)[0];
}

// Gets credits from the class site
function parseCredits($) {
  return $('h3').text().match(/\(([^\)]+)\)/i)[1];
}

// Gets course description from the class site
function parseDesc($) {
  var desc = $('#aspnetForm').first()
                             .clone()
                             .children()
                             .remove()
                             .end()
                             .text()
                             .trim();
  desc = trimNewlines(desc);
  return desc;
}

// Parses table of class sections
function parseTable($) {
  var columnNames = [];
  $('th').each(function(i, element) {
    var columnName = $(this).text().replace(/\r?\n|\r|\t/g, '');
    columnName = columnName.replace(/\ {2,}/g, '');
    columnNames.push(columnName);
  });

  var tableRows = $('#ctl00_ContentPlaceHolder1_SOCListUC1_gvOfferings > tr ');
  var sections = [];

  tableRows.each(function(i, element) {
    var sectionDict = {};
    const td = $(this).children('td');

    td.each(function(i) {
      const data = $(this).text().replace(/\s{2,}/g, ' ').trim();
      const key = formatKey(columnNames[i % columnNames.length]);

      // Parse date
      if (key === 'daytimedate') {
        const text = $(this).text();
        parseTableDate(text, sectionDict);
      } else {
        sectionDict[key] = data;
      }
    });

    const section = createSection(sectionDict);
    sections.push(section);
  });

  // First element will be categories, so remove it
  sections.splice(0, 1);
  return sections;
}

// Returns object w/ keys for days, startTime, endTime, startDate & endDate
function parseTableDate(text, sectionDict) {
  if (text.match(/\d+/g) && text.indexOf('TBA') === -1) {
    sectionDict.days = text
    .match(/([A-Z])+/g)[0];
    sectionDict.startTime = moment(text
    .match(/[0-9]{4}/g)[0], 'HHmm');
    sectionDict.endTime = moment(text
    .match(/[0-9]{4}/g)[1], 'HHmm');
    sectionDict.startDate = text
    .match(/[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{1,2}/g)[0];
    sectionDict.endDate = text
    .match(/[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{1,2}/g)[1];
  }
}

/////////////////////////////////////////////////
// HELPER FUNCTIONS
/////////////////////////////////////////////////

function trimNewlines(desc) {
  var n = desc.indexOf('\n');
  n = n === -1 ? desc.length : n;
  return desc.substring(0, n);
}

function formatKey(key) {
  return key.toLowerCase().replace(/[^A-z]/g, '');
}

function createSection(sectionDict) {
  return {
    term: sectionDict.term,
    startDate: sectionDict.startDate,
    endDate: sectionDict.endDate,
    session: sectionDict.session,
    crn: sectionDict.crn,
    sectionNumber: sectionDict.sec,
    credits: sectionDict.cr,
    instructor: sectionDict.instructor,
    days: sectionDict.days,
    startTime: sectionDict.startTime,
    endTime: sectionDict.endTime,
    location: sectionDict.location,
    campus: sectionDict.campus,
    type: sectionDict.type,
    status: sectionDict.status,
    enrollCap: sectionDict.cap,
    enrolled: sectionDict.curr,
    waitlistCap: sectionDict.wlcap,
    waitlisted: sectionDict.wlavail,
    fees: sectionDict.fees,
    restrictions: sectionDict.restrictions,
    comments: sectionDict.comments,
  };
}
<commit_msg>Fixed fatal error requiring config.js<commit_after>'use strict';

const mongoose = require('mongoose');
const Schema   = mongoose.Schema;
const request  = require('request');
const cheerio  = require('cheerio');
const async    = require('async');
const moment   = require('moment');

const CATALOG_URL = 'http://catalog.oregonstate.edu/';
const COURSE_SEARCH_URL = CATALOG_URL + 'CourseSearcher.aspx?chr=abg';

/////////////////////////////////////////////////
// DATABASE
/////////////////////////////////////////////////
console.log(process.env.MONGO_URL);
mongoose.connect(process.env.MONGO_URL);
const db = mongoose.connection;

const sectionSchema = new Schema({
  term: String,
  startDate: Date,
  endDate: Date,
  session: String,
  crn: {
    type: Number,
    unique: true,
  },
  sec: Number,
  credits: String,
  instructor: String,
  days: String,
  startTime: Date,
  endTime: Date,
  location: String,
  campus: String,
  type: String,
  status: String,
  enrollCap: Number,
  enrolled: Number,
  waitlistCap: Number,
  waitlisted: Number,
  fees: String,
  restrictions: String,
  comments: String,
});

const courseSchema = new Schema({
  title: {
    type: String,
    unique: true,
  },
  abbr: String,
  credits: String,
  desc: String,
  sections: [sectionSchema],
  updated: {
    type: Date,
    default: Date.now,
  },
  meta: {
    likes: Number,
    dislikes: Number,
  },
});

const Course = mongoose.model('Course', courseSchema);

/////////////////////////////////////////////////
// MAIN
/////////////////////////////////////////////////

db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function(callback) {
  console.log('Connected to MongoDB @ ' + db.host + ':' + db.port);
  getCourseLinks(function scrapeComplete(courses, err) {
    console.log('Scrape complete.');
    db.close();
  });
});

function getCourseLinks(callback) {
  request(COURSE_SEARCH_URL, function parseSearchPage(error, res, body) {
    if (!error && res.statusCode === 200) {
      var classURLs = [];
      var $ = cheerio.load(body);

      $('a[id^=\'ctl00_ContentPlaceHolder\']').each(function(i, element) {
        var link = $(this).attr('href');
        classURLs.push(link);
      });

      // First two elements are currently trash, don't attempt to scrape
      classURLs.splice(0, 2);
      return getCourseInfo(CATALOG_URL, classURLs, callback);
    }
  });
}

function getCourseInfo(baseURL, classURLs, callback) {
  var index = 1;

  async.eachSeries(classURLs, function(url, asyncCallback) {
    const classURL = baseURL + url;
    console.log('Scraping ' + index++ + ' of ' + classURLs.length);
    console.log('URL: ' + classURL);

    request(classURL, function scrapeClassPage(error, response, body) {
      if (error) {
        console.error('Error scraping ' + classURL + '\n' + error);
        asyncCallback();
      } else if (response.statusCode !== 200) {
        console.error('Reponse status code == ' + response.statusCode);
        asyncCallback();
      } else {
        parseCourseFromHTML(body);
        asyncCallback();
      }
    });
  },

  function doneScraping(err) {
    if (err) {
      console.error('An error occured scraping: ' + err);
    } else {
      callback();
    }
  });
}

/////////////////////////////////////////////////
// PARSERS
/////////////////////////////////////////////////

function parseCourseFromHTML(htmlBody) {
  var $ = cheerio.load(htmlBody);

  var course = new Course({
    title: parseTitle($),
    abbr: parseAbbr($),
    credits: parseCredits($),
    desc: parseDesc($),
    sections: parseTable($),
  });

  course.save(function(err) {
    if (err) console.error(err);
  });
}

// Gets course title from the class site. Regex's follow these steps:
// Select h3, remove abbreviation, remove credits, replace
// non-words/whitespace with whitespace, remove spaces, tabs &
// newlines, turn multiple spaces into one, remove spaces on ends
function parseTitle($) {
  var title = $('h3').text();
  title = title.replace(/(^[A-Z]{1,4}\s[0-9]{2,3})/, '');
  title = title.replace(/\(([^\)]+)\)/i, '');
  title = title.replace(/[^\w\s]/gi, ' ');
  title = title.replace(/\r?\n|\r|\t/g, '');
  title = title.replace(/\ {2,}/g, ' ');
  return title.trim();
}

// Gets course abbreviation from the class site
function parseAbbr($) {
  return $('h3').text().trim().match(/^[A-Z]{1,4}\s[0-9]{2,3}/)[0];
}

// Gets credits from the class site
function parseCredits($) {
  return $('h3').text().match(/\(([^\)]+)\)/i)[1];
}

// Gets course description from the class site
function parseDesc($) {
  var desc = $('#aspnetForm').first()
                             .clone()
                             .children()
                             .remove()
                             .end()
                             .text()
                             .trim();
  desc = trimNewlines(desc);
  return desc;
}

// Parses table of class sections
function parseTable($) {
  var columnNames = [];
  $('th').each(function(i, element) {
    var columnName = $(this).text().replace(/\r?\n|\r|\t/g, '');
    columnName = columnName.replace(/\ {2,}/g, '');
    columnNames.push(columnName);
  });

  var tableRows = $('#ctl00_ContentPlaceHolder1_SOCListUC1_gvOfferings > tr ');
  var sections = [];

  tableRows.each(function(i, element) {
    var sectionDict = {};
    const td = $(this).children('td');

    td.each(function(i) {
      const data = $(this).text().replace(/\s{2,}/g, ' ').trim();
      const key = formatKey(columnNames[i % columnNames.length]);

      // Parse date
      if (key === 'daytimedate') {
        const text = $(this).text();
        parseTableDate(text, sectionDict);
      } else {
        sectionDict[key] = data;
      }
    });

    const section = createSection(sectionDict);
    sections.push(section);
  });

  // First element will be categories, so remove it
  sections.splice(0, 1);
  return sections;
}

// Returns object w/ keys for days, startTime, endTime, startDate & endDate
function parseTableDate(text, sectionDict) {
  if (text.match(/\d+/g) && text.indexOf('TBA') === -1) {
    sectionDict.days = text
    .match(/([A-Z])+/g)[0];
    sectionDict.startTime = moment(text
    .match(/[0-9]{4}/g)[0], 'HHmm');
    sectionDict.endTime = moment(text
    .match(/[0-9]{4}/g)[1], 'HHmm');
    sectionDict.startDate = text
    .match(/[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{1,2}/g)[0];
    sectionDict.endDate = text
    .match(/[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{1,2}/g)[1];
  }
}

/////////////////////////////////////////////////
// HELPER FUNCTIONS
/////////////////////////////////////////////////

function trimNewlines(desc) {
  var n = desc.indexOf('\n');
  n = n === -1 ? desc.length : n;
  return desc.substring(0, n);
}

function formatKey(key) {
  return key.toLowerCase().replace(/[^A-z]/g, '');
}

function createSection(sectionDict) {
  return {
    term: sectionDict.term,
    startDate: sectionDict.startDate,
    endDate: sectionDict.endDate,
    session: sectionDict.session,
    crn: sectionDict.crn,
    sectionNumber: sectionDict.sec,
    credits: sectionDict.cr,
    instructor: sectionDict.instructor,
    days: sectionDict.days,
    startTime: sectionDict.startTime,
    endTime: sectionDict.endTime,
    location: sectionDict.location,
    campus: sectionDict.campus,
    type: sectionDict.type,
    status: sectionDict.status,
    enrollCap: sectionDict.cap,
    enrolled: sectionDict.curr,
    waitlistCap: sectionDict.wlcap,
    waitlisted: sectionDict.wlavail,
    fees: sectionDict.fees,
    restrictions: sectionDict.restrictions,
    comments: sectionDict.comments,
  };
}
<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
    env = process.env.NODE_ENV || 'development',
    config = require('../../config/config')[env],
    Schema = mongoose.Schema;

/**
 * Quote Schema
 */
var QuoteSchema = new Schema({
    created: {
        type: Date,
        "default": Date.now
    },
    totalCost: {
        type: Number,
        required: true
    },
    status: {
        type: String,
        "default": 'Open',
        trim: true
    },
    vendorId: {
        type: Schema.ObjectId,
        ref: 'Vendor',
        required: true
    },
    "salesRep": {
        type: Schema.ObjectId,
        ref: 'User'
    },
    "vendorRep": {
        type: Schema.ObjectId,
        ref: 'User'
    },
    description: {
        type: String,
        "default": '',
        trim: true
    },
    company: {
        fullLegalBusinessName: {type: String, "default": '', trim: true},
        contactPerson: {
            name: {type: String, "default": '', trim: true},
            email: {type: String, "default": '', trim: true},
            phone: {type: String, "default": '', trim: true},
            contactMethod:{type: String, "default": '', trim: true}
        },
        businessAddress: {
          "address1": {type: String, "default": '', trim: true},
          "address2": {type: String, "default": '', trim: true},
          "city": {type: String, "default": '', trim: true},
          "state": {type: String, "default": '', trim: true},
          "zip": {type: String, "default": '', trim: true}
        }
    },
    customField: {
        displayName: String,
        value: String
    }
});

// virtual fields are available anywhere on the server.
// They are not passes over the API unless explicitly set.
QuoteSchema.virtual('quoterToolLink').get(function() {
    return config.siteUrl + '/tools/quoter/' + this._id;
});

QuoteSchema.virtual('dashboardLink').get(function() {
    return config.siteUrl + '/dashboard/quotes/' + this._id;
});

/*
// the below 4 validations only apply if you are signing up traditionally
QuoteSchema.path('vendorId').validate(function(vendorId) {
    console.log('Validate vendorId');
    return vendorId ? true : false;
}, 'Vendor id must be provided with a new quote.');
*/

QuoteSchema.pre('save', function(next, something) {

    var self = this;

    // attempt to get the sales rep, which we save with the quote
    // for easy geting from the database
    mongoose.models.Vendor.getCurrentReps(self.vendorId, function(err, result) {
        if (err) {
            next();

            //next(new Error(self.vendorId + ' Not a valid vendor'));
        } else {
            self.salesRep = result.salesRep;
            self.vendorRep = result.vendorRep;
            self.vendorId = self.vendorId;
            next();
        }
    });
});
/**
 * Statics
 */
QuoteSchema.statics = {
    load: function(id, cb) {
        this.findOne({
            _id: id
        }).exec(cb);
    }
};

mongoose.model('Quote', QuoteSchema);
<commit_msg>Fix issue where vendor id was not properly saving with quote.<commit_after>/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
    env = process.env.NODE_ENV || 'development',
    config = require('../../config/config')[env],
    Schema = mongoose.Schema;

/**
 * Quote Schema
 */
var QuoteSchema = new Schema({
    created: {
        type: Date,
        "default": Date.now
    },
    totalCost: {
        type: Number,
        required: true
    },
    status: {
        type: String,
        "default": 'Open',
        trim: true
    },
    vendorId: {
        type: Schema.ObjectId,
        ref: 'Vendor',
        required: true
    },
    "salesRep": {
        type: Schema.ObjectId,
        ref: 'User'
    },
    "vendorRep": {
        type: Schema.ObjectId,
        ref: 'User'
    },
    description: {
        type: String,
        "default": '',
        trim: true
    },
    company: {
        fullLegalBusinessName: {type: String, "default": '', trim: true},
        contactPerson: {
            name: {type: String, "default": '', trim: true},
            email: {type: String, "default": '', trim: true},
            phone: {type: String, "default": '', trim: true},
            contactMethod:{type: String, "default": '', trim: true}
        },
        businessAddress: {
          "address1": {type: String, "default": '', trim: true},
          "address2": {type: String, "default": '', trim: true},
          "city": {type: String, "default": '', trim: true},
          "state": {type: String, "default": '', trim: true},
          "zip": {type: String, "default": '', trim: true}
        }
    },
    customField: {
        displayName: String,
        value: String
    }
});

// virtual fields are available anywhere on the server.
// They are not passes over the API unless explicitly set.
QuoteSchema.virtual('quoterToolLink').get(function() {
    return config.siteUrl + '/tools/quoter/' + this._id;
});

QuoteSchema.virtual('dashboardLink').get(function() {
    return config.siteUrl + '/dashboard/quotes/' + this._id;
});

/*
// the below 4 validations only apply if you are signing up traditionally
QuoteSchema.path('vendorId').validate(function(vendorId) {
    console.log('Validate vendorId');
    return vendorId ? true : false;
}, 'Vendor id must be provided with a new quote.');
*/

QuoteSchema.pre('save', function(next, something) {

    var self = this;

    // attempt to get the sales rep, which we save with the quote
    // for easy geting from the database
    mongoose.models.Vendor.getCurrentReps(self.vendorId, function(err, result) {
        if (err) {
            next();

            //next(new Error(self.vendorId + ' Not a valid vendor'));
        } else {

            self.salesRep = result.salesRep;
            self.vendorRep = result.vendorRep;
            self.vendorId = result._id;
            next();
        }
    });
});
/**
 * Statics
 */
QuoteSchema.statics = {
    load: function(id, cb) {
        this.findOne({
            _id: id
        }).exec(cb);
    }
};

mongoose.model('Quote', QuoteSchema);
<|endoftext|>


<commit_before>app.controller('LoginController', ['$scope', '$mdDialog', '$firebaseAuth', 'AuthFactory', function($scope, $mdDialog, $firebaseAuth, AuthFactory){
  console.log('login controller is running');
  var auth = $firebaseAuth();
  var self = this;

  self.isLoggedIn = AuthFactory.userStatus.isLoggedIn;
  console.log(self.isLoggedIn);

  self.logIn = function() {
    console.log('logging user in');
    AuthFactory.logIn().then(function(response){
      console.log('Logged In: ', AuthFactory.userStatus.isLoggedIn);
    });

    // TODO: Can this be removed?
    // auth.$signInWithPopup("google").then(function(firebaseUser) {
    //   console.log("Firebase Authenticated as: ", firebaseUser.user);
    // }).catch(function(error) {
    //   console.log("Authentication failed: ", error);
    // });
  };

  self.logOut = function() {
    console.log('logging user out');
    AuthFactory.logOut();
  };

  // TODO: I don't think this function is used. Can we delete?
  // self.hide = function() {
  //   $mdDialog.hide();
  // };

  self.cancel = function() {
    $mdDialog.cancel();
  };

  // TODO: I don't think we need this code, but I could be wrong.
  self.answer = function(answer) {
    logIn();
    // $mdDialog.hide(answer);
    // console.log('from dialog controller', answer);
  };

}]);
<commit_msg>Remove redundant code<commit_after>app.controller('LoginController', ['$scope', '$mdDialog', '$firebaseAuth', 'AuthFactory', function($scope, $mdDialog, $firebaseAuth, AuthFactory){
  console.log('login controller is running');
  var auth = $firebaseAuth();
  var self = this;

  self.isLoggedIn = AuthFactory.userStatus.isLoggedIn;
  console.log(self.isLoggedIn);

  self.logIn = function() {
    console.log('logging user in');
    AuthFactory.logIn().then(function(response){
      console.log('Logged In: ', AuthFactory.userStatus.isLoggedIn);
    });
  };

  self.logOut = function() {
    console.log('logging user out');
    AuthFactory.logOut();
  };

  self.cancel = function() {
    $mdDialog.cancel();
  };

}]);
<|endoftext|>


<commit_before>/*
This module contains functions for generating lists of items that,
when clicked, show corresponding content or trigger corresponding
logic. Here are three examples.

### CT.panel.simple(pnames, keystring, itemnode, panelnode, cbs)
This function wraps CT.panel.load(), supporting a subset of load()'s
options (the simple ones). It supports the following args, of which
only the first is required:

    - pnames (string array): short for 'panel names'
    - keystring (string, default: 'sb'): identifier for collections of content/lister nodes
    - itemnode (node, default: CT.dom.id(keystring + "items")): parent node for lister items
    - panelnode (node, default: CT.dom.id(keystring + "panels")): parent node for content panels
    - cbs (function array, optional): callbacks to invoke on lister item click post- panel swap

### CT.panel.pager(getContent, request, limit, colClass, dataClass, ks)
This function generates and returns a node containing a
(CT.Pager-backed) paging lister node and a corresponding content panel.

    - getContent (function): combined with CT.panel.simple() in pager's renderCb
    - request (function): pager's requestCb
    - limit (number, default: 20): pager's limit (chunk size)
    - colClass (string, optional): class of generated pager (list selector) node
    - dataClass (string, optional): class of generated data (content panel) node
    - ks (string, default: "p" + CT.Pager._id): keystring of data and list nodes

### CT.panel.triggerList(data, cb, node)
This function fills the 'node' node with a list of clickable items, each of
which triggers cb(d), where d is the corresponding object in the 'data' array.

    - data (object array): data set used to generate list
      - for d in data: link content equals d.label || d.title
    - cb (function): the callback to invoke when an item is clicked
    - node (node): the list parent node
*/

CT.panel = {
	"lastClicked": {},
	"accordion": function(data, triggerCb, activeClass, content, condition, notick, rmbutton, ricon) {
		var n = CT.dom.node(null, null, "tabbed pointer", "ac" + data.name);
		n.add = function(tdata, makeFirst) {
			var t = CT.panel.trigger(tdata, triggerCb, activeClass, content, condition, rmbutton, ricon);
			if (makeFirst)
				n.insertBefore(t, n.firstChild);
			else
				n.appendChild(t);
			return t;
		};
		data.forEach(function(d) {
			if (d.children) {
				var tick = CT.dom.node(">", "div", "inline-block" + (notick && " hidden" || "")),
					t = CT.dom.node(),
					tline = [tick, CT.dom.node(content ? content(d) : d.name, "div", "inline-block")];
				if (ricon)
					tline.unshift(CT.dom.node(ricon(d), "div", "right rpaddedsmall"));
				CT.dom.setContent(t, tline);
				t.content = CT.panel.accordion(d.children, triggerCb,
					activeClass, content, condition, notick, rmbutton, ricon);
				n.appendChild(t);
				new CT.Drop({
					anchor: t,
					parent: n,
					relative: true,
					constrained: false,
					setNodeClick: false,
					content: t.content,
					onShow: function() {
						CT.trans.rotate(tick, { duration: 100, degrees: 90 });
					},
					onHide: function() {
						CT.trans.rotate(tick, { duration: 100, degrees: 0 });
					}
				});
			} else
				n.add(d);
		});
		return n;
	},
	"trigger": function(data, cb, activeClass, content, condition, rmbutton, ricon) {
		var label = data.label || data.title || data.name || data,
			n = CT.dom.node(null, "div", null, label && ("tl" + label));
		activeClass = activeClass || "activetab";
		n.data = data;
		n.select = function() {
			CT.dom.each(n.parentNode, function(c) {
				if (c == n)
					c.classList.add(activeClass);
				else
					c.classList.remove(activeClass);
				c.className = (c == n) ? activeClass : "";
			});
		};
		n.trigger = function() {
			if (condition && !condition()) return;
			n.select();
			cb(data);
		};
		n.remove = function() {
			CT.dom.remove(n);
			n.onRemove && n.onRemove();
		};
		if (ricon)
			n.appendChild(CT.dom.node(ricon(data), "div", "right rpaddedsmall"));
		if (rmbutton)
			n.appendChild(CT.dom.link("X", n.remove, null, "right"));
		n.appendChild(CT.dom.link(content ? content(data) : label, n.trigger));
		return n;
	},
	"triggerList": function(data, cb, node, activeClass, content, condition, rmbutton) {
		node = node || CT.dom.node();
		data.forEach(function(d) {
			node.appendChild(CT.panel.trigger(d, cb, activeClass, content, condition, rmbutton));
		});
		return node;
	},
	"swap": function(key, trysidepanel, keystring, noitem) {
	    keystring = keystring || "sb";
	    key = key.replace(/ /g, "");
	    var items = CT.dom.className(keystring + "panel");
	    for (var i = 0; i < items.length; i++)
	        items[i].className = keystring + "panel hidden";
	    CT.dom.id(keystring +  "panel" + key, true).className = keystring + "panel";
	    if (trysidepanel) {
	        var n = CT.dom.id(keystring + "panel" + key + "Side");
	        if (n) n.className = keystring+"panel";
	        var n2 = CT.dom.id(keystring + "panel" + key + "Side2");
	        if (n2) n2.className = keystring + "panel";
	    }
	    if (!noitem)
	        CT.panel.select(key, keystring);
	},
	"drill": function(keymap) { // keystring: key
		for (var k in keymap)
			CT.panel.swap(keymap[k], false, k);
	},
	"select": function(key, keystring) {
	    keystring = keystring || "sb";
	    var items = CT.dom.className(keystring + "item");
	    for (var i = 0; i < items.length; i++)
	        items[i].className = items[i].className.replace(" activetab", "");
	    if (key) {
	        CT.dom.id(keystring + "item" + key, true).className += " activetab";
	        CT.panel.lastClicked[keystring] = key;
	    }
	    CT.mobile && CT.mobile.mobileSnap();
	},
	"selectLister": function(newkey, oldkey, newhtml) {
	    if (oldkey)
	        CT.dom.id("ll" + oldkey).className = "pointer";
	    var newnode = CT.dom.id("ll" + newkey);
	    newnode.className = "pointer activetab";
	    if (newhtml)
	        newnode.firstChild.innerHTML = newhtml;
	    CT.mobile && CT.mobile.mobileSnap();
	},
	"add": function(key, trysidepanel, keystring, itemnode, panelnode, nospace, icon, cb, condition) {
	    nospace = nospace || key.replace(/ /g, "");
	    keystring = keystring || "sb";
	    if (!CT.dom.id(keystring + "panel" + nospace)) {
	        var n = CT.dom.node("", "div", keystring + "panel hidden", keystring + "panel" + nospace);
	        n.appendChild(CT.dom.node(key, "div", "bigger blue bold bottompadded"));
	        n.appendChild(CT.dom.node("", "div", "", keystring+"content"+nospace));
	        (panelnode || CT.dom.id(keystring+"panels")).appendChild(n);
	    }
	    var i = CT.dom.id(keystring+"item"+nospace);
	    if (i)
	        i.style.display = "block";
	    else {
	        itemnode = itemnode || CT.dom.id(keystring+"items");
	        var clickfunc = function() {
	        	if (condition && !condition()) return;
	            CT.panel.swap(nospace, trysidepanel, keystring);
	            cb && cb();
	        };
	        if (icon)
	            itemnode.appendChild(CT.dom.node(CT.dom.img(icon,
	                null, clickfunc), "div", "lfloat shiftleft"));
	        itemnode.appendChild(CT.dom.node(CT.dom.link(key, clickfunc),
	            "div", keystring + "item pointer", keystring + "item" + nospace));
	        if (icon)
	            itemnode.appendChild(CT.dom.node("", "div", "clearnode"));
	    }
	},
	"rename": function(oldkey, newkey, keystring, item2keystring) {
		keystring = keystring || "sb";
		var nsold = oldkey.replace(/ /g, ""),
			nsnew = newkey.replace(/ /g, ""),
			item = CT.dom.id(keystring + "item" + nsold),
			panel = CT.dom.id(keystring + "panel" + nsold),
			content = CT.dom.id(keystring + "content" + nsold);
		item.id = keystring + "item" + nsnew;
		panel.id = keystring + "panel" + nsnew;
		if (content) content.id = keystring + "content" + nsnew;
		item.firstChild.innerHTML = panel.firstChild.innerHTML = newkey;
		if (item2keystring) {
			var item2 = CT.dom.id(item2keystring + "item" + nsold);
			item2.id = nsnew;
			item2.firstChild.innerHTML = newkey;
		}
	},
	"remove": function(key, keystring) {
	    var nospace = key.replace(/ /g, "");
	    var p = CT.dom.id(keystring+"panel"+nospace);
	    if (p) p.style.display = "none";
	    var l = CT.dom.id(keystring+"item"+nospace);
	    if (l) l.style.display = "none";
	},
	"load": function(pnames, trysidepanel, keystring, itemnode, panelnode, nospaces, icons, noclear, stillswap, cbs, condition) {
	    keystring = keystring || "sb";
	    if (!noclear)
	        (itemnode || CT.dom.id(keystring+"items")).innerHTML = "";
	    for (var i = 0; i < pnames.length; i++)
	        CT.panel.add(pnames[i], trysidepanel, keystring, itemnode,
	            panelnode, nospaces && nospaces[i] || null,
	            icons && icons[i] || null, cbs && cbs[i] || null, condition);
	    if (pnames.length && (stillswap || (!itemnode && !noclear)))
	        CT.panel.swap(pnames[0], trysidepanel, keystring);
	},
	"simple": function(pnames, keystring, itemnode, panelnode, cbs, condition) {
		CT.panel.load(pnames, null, keystring, itemnode, panelnode, null, null, null, true, cbs, condition);
	},
	"pager": function(getContent, request, limit, colClass, dataClass, ks) {
		var keystring = ks || ("p" + CT.Pager._id),
			content = CT.dom.node(null, null, dataClass, keystring + "panels"),
			sideBar = CT.dom.node(),
			pager = new CT.Pager(function(data) {
				var dnames = data.map(function(d) { return d.label; });
				CT.panel.simple(dnames, keystring, sideBar, content);
				data.forEach(function(d) {
					CT.dom.setContent(CT.dom.id(keystring + "content"
						+ d.label.replace(/ /g, ""),
						true), getContent(d));
				});
				return sideBar;
			}, request, limit, colClass, keystring + "items"),
			n = CT.dom.node([pager.node, content]);
		n.pager = pager;
		return n;
	},
	"alternatebg": function(n, watchforicons, resetonbreak) {
	    n = n || document.getElementById("sbitems");
	    if (watchforicons) {
	        var switcher = 1;
	        for (var i = 0; i < n.childNodes.length; i++) {
	            var c = n.childNodes[i];
	            if (c.className.indexOf("lfloat") == -1 && c.className.indexOf("clearnode") == -1) {
	                if (resetonbreak && c.nodeName == "BR") {
	                    switcher = 1;
	                    continue;
	                }
	                if (switcher == 1)
	                    c.style.background = "#899fb0";
	                switcher *= -1;
	            }
	        }
	    }
	    else {
	        for (var i = 0; i < n.childNodes.length; i+=2)
	            n.childNodes[i].style.background = "#899fb0";
	    }
	}
};<commit_msg>CT.panel.accordion(): changed main node from div to a (link); moved pointer class from line to link<commit_after>/*
This module contains functions for generating lists of items that,
when clicked, show corresponding content or trigger corresponding
logic. Here are three examples.

### CT.panel.simple(pnames, keystring, itemnode, panelnode, cbs)
This function wraps CT.panel.load(), supporting a subset of load()'s
options (the simple ones). It supports the following args, of which
only the first is required:

    - pnames (string array): short for 'panel names'
    - keystring (string, default: 'sb'): identifier for collections of content/lister nodes
    - itemnode (node, default: CT.dom.id(keystring + "items")): parent node for lister items
    - panelnode (node, default: CT.dom.id(keystring + "panels")): parent node for content panels
    - cbs (function array, optional): callbacks to invoke on lister item click post- panel swap

### CT.panel.pager(getContent, request, limit, colClass, dataClass, ks)
This function generates and returns a node containing a
(CT.Pager-backed) paging lister node and a corresponding content panel.

    - getContent (function): combined with CT.panel.simple() in pager's renderCb
    - request (function): pager's requestCb
    - limit (number, default: 20): pager's limit (chunk size)
    - colClass (string, optional): class of generated pager (list selector) node
    - dataClass (string, optional): class of generated data (content panel) node
    - ks (string, default: "p" + CT.Pager._id): keystring of data and list nodes

### CT.panel.triggerList(data, cb, node)
This function fills the 'node' node with a list of clickable items, each of
which triggers cb(d), where d is the corresponding object in the 'data' array.

    - data (object array): data set used to generate list
      - for d in data: link content equals d.label || d.title
    - cb (function): the callback to invoke when an item is clicked
    - node (node): the list parent node
*/

CT.panel = {
	"lastClicked": {},
	"accordion": function(data, triggerCb, activeClass, content, condition, notick, rmbutton, ricon) {
		var n = CT.dom.node(null, null, "tabbed", "ac" + data.name);
		n.add = function(tdata, makeFirst) {
			var t = CT.panel.trigger(tdata, triggerCb, activeClass, content, condition, rmbutton, ricon);
			if (makeFirst)
				n.insertBefore(t, n.firstChild);
			else
				n.appendChild(t);
			return t;
		};
		data.forEach(function(d) {
			if (d.children) {
				var tick = CT.dom.node(">", "div", "inline-block" + (notick && " hidden" || "")),
					t = CT.dom.node(),
					tline = [tick, CT.dom.link(content ? content(d) : d.name, null, null, "inline-block pointer")];
				if (ricon)
					tline.unshift(CT.dom.node(ricon(d), "div", "right rpaddedsmall"));
				CT.dom.setContent(t, tline);
				t.content = CT.panel.accordion(d.children, triggerCb,
					activeClass, content, condition, notick, rmbutton, ricon);
				n.appendChild(t);
				new CT.Drop({
					anchor: t,
					parent: n,
					relative: true,
					constrained: false,
					setNodeClick: false,
					content: t.content,
					onShow: function() {
						CT.trans.rotate(tick, { duration: 100, degrees: 90 });
					},
					onHide: function() {
						CT.trans.rotate(tick, { duration: 100, degrees: 0 });
					}
				});
			} else
				n.add(d);
		});
		return n;
	},
	"trigger": function(data, cb, activeClass, content, condition, rmbutton, ricon) {
		var label = data.label || data.title || data.name || data,
			n = CT.dom.node(null, "div", null, label && ("tl" + label));
		activeClass = activeClass || "activetab";
		n.data = data;
		n.select = function() {
			CT.dom.each(n.parentNode, function(c) {
				if (c == n)
					c.classList.add(activeClass);
				else
					c.classList.remove(activeClass);
				c.className = (c == n) ? activeClass : "";
			});
		};
		n.trigger = function() {
			if (condition && !condition()) return;
			n.select();
			cb(data);
		};
		n.remove = function() {
			CT.dom.remove(n);
			n.onRemove && n.onRemove();
		};
		if (ricon)
			n.appendChild(CT.dom.node(ricon(data), "div", "right rpaddedsmall"));
		if (rmbutton)
			n.appendChild(CT.dom.link("X", n.remove, null, "right"));
		n.appendChild(CT.dom.link(content ? content(data) : label, n.trigger));
		return n;
	},
	"triggerList": function(data, cb, node, activeClass, content, condition, rmbutton) {
		node = node || CT.dom.node();
		data.forEach(function(d) {
			node.appendChild(CT.panel.trigger(d, cb, activeClass, content, condition, rmbutton));
		});
		return node;
	},
	"swap": function(key, trysidepanel, keystring, noitem) {
	    keystring = keystring || "sb";
	    key = key.replace(/ /g, "");
	    var items = CT.dom.className(keystring + "panel");
	    for (var i = 0; i < items.length; i++)
	        items[i].className = keystring + "panel hidden";
	    CT.dom.id(keystring +  "panel" + key, true).className = keystring + "panel";
	    if (trysidepanel) {
	        var n = CT.dom.id(keystring + "panel" + key + "Side");
	        if (n) n.className = keystring+"panel";
	        var n2 = CT.dom.id(keystring + "panel" + key + "Side2");
	        if (n2) n2.className = keystring + "panel";
	    }
	    if (!noitem)
	        CT.panel.select(key, keystring);
	},
	"drill": function(keymap) { // keystring: key
		for (var k in keymap)
			CT.panel.swap(keymap[k], false, k);
	},
	"select": function(key, keystring) {
	    keystring = keystring || "sb";
	    var items = CT.dom.className(keystring + "item");
	    for (var i = 0; i < items.length; i++)
	        items[i].className = items[i].className.replace(" activetab", "");
	    if (key) {
	        CT.dom.id(keystring + "item" + key, true).className += " activetab";
	        CT.panel.lastClicked[keystring] = key;
	    }
	    CT.mobile && CT.mobile.mobileSnap();
	},
	"selectLister": function(newkey, oldkey, newhtml) {
	    if (oldkey)
	        CT.dom.id("ll" + oldkey).className = "pointer";
	    var newnode = CT.dom.id("ll" + newkey);
	    newnode.className = "pointer activetab";
	    if (newhtml)
	        newnode.firstChild.innerHTML = newhtml;
	    CT.mobile && CT.mobile.mobileSnap();
	},
	"add": function(key, trysidepanel, keystring, itemnode, panelnode, nospace, icon, cb, condition) {
	    nospace = nospace || key.replace(/ /g, "");
	    keystring = keystring || "sb";
	    if (!CT.dom.id(keystring + "panel" + nospace)) {
	        var n = CT.dom.node("", "div", keystring + "panel hidden", keystring + "panel" + nospace);
	        n.appendChild(CT.dom.node(key, "div", "bigger blue bold bottompadded"));
	        n.appendChild(CT.dom.node("", "div", "", keystring+"content"+nospace));
	        (panelnode || CT.dom.id(keystring+"panels")).appendChild(n);
	    }
	    var i = CT.dom.id(keystring+"item"+nospace);
	    if (i)
	        i.style.display = "block";
	    else {
	        itemnode = itemnode || CT.dom.id(keystring+"items");
	        var clickfunc = function() {
	        	if (condition && !condition()) return;
	            CT.panel.swap(nospace, trysidepanel, keystring);
	            cb && cb();
	        };
	        if (icon)
	            itemnode.appendChild(CT.dom.node(CT.dom.img(icon,
	                null, clickfunc), "div", "lfloat shiftleft"));
	        itemnode.appendChild(CT.dom.node(CT.dom.link(key, clickfunc),
	            "div", keystring + "item pointer", keystring + "item" + nospace));
	        if (icon)
	            itemnode.appendChild(CT.dom.node("", "div", "clearnode"));
	    }
	},
	"rename": function(oldkey, newkey, keystring, item2keystring) {
		keystring = keystring || "sb";
		var nsold = oldkey.replace(/ /g, ""),
			nsnew = newkey.replace(/ /g, ""),
			item = CT.dom.id(keystring + "item" + nsold),
			panel = CT.dom.id(keystring + "panel" + nsold),
			content = CT.dom.id(keystring + "content" + nsold);
		item.id = keystring + "item" + nsnew;
		panel.id = keystring + "panel" + nsnew;
		if (content) content.id = keystring + "content" + nsnew;
		item.firstChild.innerHTML = panel.firstChild.innerHTML = newkey;
		if (item2keystring) {
			var item2 = CT.dom.id(item2keystring + "item" + nsold);
			item2.id = nsnew;
			item2.firstChild.innerHTML = newkey;
		}
	},
	"remove": function(key, keystring) {
	    var nospace = key.replace(/ /g, "");
	    var p = CT.dom.id(keystring+"panel"+nospace);
	    if (p) p.style.display = "none";
	    var l = CT.dom.id(keystring+"item"+nospace);
	    if (l) l.style.display = "none";
	},
	"load": function(pnames, trysidepanel, keystring, itemnode, panelnode, nospaces, icons, noclear, stillswap, cbs, condition) {
	    keystring = keystring || "sb";
	    if (!noclear)
	        (itemnode || CT.dom.id(keystring+"items")).innerHTML = "";
	    for (var i = 0; i < pnames.length; i++)
	        CT.panel.add(pnames[i], trysidepanel, keystring, itemnode,
	            panelnode, nospaces && nospaces[i] || null,
	            icons && icons[i] || null, cbs && cbs[i] || null, condition);
	    if (pnames.length && (stillswap || (!itemnode && !noclear)))
	        CT.panel.swap(pnames[0], trysidepanel, keystring);
	},
	"simple": function(pnames, keystring, itemnode, panelnode, cbs, condition) {
		CT.panel.load(pnames, null, keystring, itemnode, panelnode, null, null, null, true, cbs, condition);
	},
	"pager": function(getContent, request, limit, colClass, dataClass, ks) {
		var keystring = ks || ("p" + CT.Pager._id),
			content = CT.dom.node(null, null, dataClass, keystring + "panels"),
			sideBar = CT.dom.node(),
			pager = new CT.Pager(function(data) {
				var dnames = data.map(function(d) { return d.label; });
				CT.panel.simple(dnames, keystring, sideBar, content);
				data.forEach(function(d) {
					CT.dom.setContent(CT.dom.id(keystring + "content"
						+ d.label.replace(/ /g, ""),
						true), getContent(d));
				});
				return sideBar;
			}, request, limit, colClass, keystring + "items"),
			n = CT.dom.node([pager.node, content]);
		n.pager = pager;
		return n;
	},
	"alternatebg": function(n, watchforicons, resetonbreak) {
	    n = n || document.getElementById("sbitems");
	    if (watchforicons) {
	        var switcher = 1;
	        for (var i = 0; i < n.childNodes.length; i++) {
	            var c = n.childNodes[i];
	            if (c.className.indexOf("lfloat") == -1 && c.className.indexOf("clearnode") == -1) {
	                if (resetonbreak && c.nodeName == "BR") {
	                    switcher = 1;
	                    continue;
	                }
	                if (switcher == 1)
	                    c.style.background = "#899fb0";
	                switcher *= -1;
	            }
	        }
	    }
	    else {
	        for (var i = 0; i < n.childNodes.length; i+=2)
	            n.childNodes[i].style.background = "#899fb0";
	    }
	}
};<|endoftext|>


<commit_before>function setupFragmentEditor() {
	var $window = $(window);
	var $editorFrame = $("#editor-frame");

	$("#new-fragment-editor-trigger, .new-fragment-editor-trigger-on-toolbar").click(function (e) {
		onClickFragmentEditorTrigger($editorFrame, $window, e, MSG.label_new_fragment, true);
    });

    $editorFrame.resizable({
        alsoResize: "#markItUpFragment-content-editor, #fragment-content-editor",
        minWidth: 581,
        minHeight: 234,
    });

	$editorFrame.draggable({
    	handle:"#editor-title-bar, #editor-button-bar"
    	, cursor:"move"
    });

    $("#editor-title-bar").dblclick(function() {
    	toggleFragmentEditor($editorFrame, $(this));
    });

    $("#fragment-content-editor").markItUp(markItUpSettings, {});
}

function toggleFragmentEditor(frame, bar) {
	var target = bar.next();
	var ph = frame.data("prev-h");
	frame.data("prev-h", frame.height());
	if (target.is(":visible")) {
		var w = frame.width();
		if (frame.hasClass("ui-resizable"))
			frame.resizable("disable");
		target.hide();
		frame.width(w);
		frame.data("min-h", frame.css("min-height"));
		frame.css("min-height", bar.height());
		frame.height(bar.height());
	}
	else {
		if (frame.hasClass("ui-resizable"))
			frame.resizable("enable");
		target.show();
		frame.height(ph);
		frame.css("min-height", frame.data("min-h"));
	}
}

function onClickFragmentEditorTrigger($editorFrame, $window, e, title, forNewFragment) {
	var editorHeight = Math.max($editorFrame.height(), 696);
    var top = ($window.height() - editorHeight) / 2 + $window.scrollTop() + "px";

    if (forNewFragment) {
    	var el = $("#fragment-editor-form\\:title-input");
        el.val(null);
        el.watermark(MSG.label_title);

        el = $("#fragment-editor-form\\:tags-input");
        el.val(null);
        el.watermark(MSG.how_to_input_tags);

        $("#fragment-content-editor").val(null);
    }

    $editorFrame.lightbox_me({
        centered:false
        , showOverlay:false
        , lightboxSpeed:"slow"
        , closeSelector:"#editor-frame-close-button"
        , closeEsc:false
        , modalCSS:{ position:"absolute", top:top, left:"30%" }
        , onClose:clearFragmentEditor
    });

    var titleBar = $("#editor-title-bar");
    if (titleBar.next().is(":visible") == false) {
    	toggleFragmentEditor($editorFrame, titleBar);
    }

    $("#editor-title").text(title);

    e.preventDefault();
}

function clearFragmentEditor() {
    $("#fragment-content-editor").val(null);
    $("#fragment-editor-form\\:id-placeholder-for-fragment").val(null);

    // If the preview pop-up is open, close it
    var frgOverlay = $("#fragment-overlay");
    if (frgOverlay.find(".fragment-header").length == 0) {
        frgOverlay.trigger("close");
    }

    // [NOTE] without the following code,
    // jQuery UI Autocomplete object won't disapper even after its parent is closed
    $("#fragment-editor-form\\:tags-input").autocomplete("widget").hide();
}

function parseMarkdown(inputText) {
    return marked(inputText);
}

function openFragmentEditorForEdit(event) {
	var menu = $("#frg-context-menu");
	var target = menu.data("target-frg");
	var fragmentId = target.attr("_fid");
	var panelId = findPanel(target);

	$("#fragment-editor-form\\:id-placeholder-for-fragment").val(fragmentId);

    var el = target.find(".-cvz-data-title");
    $("#fragment-editor-form\\:title-input").val(el.text());

    el = target.find(".-cvz-data-tags");
    $("#fragment-editor-form\\:tags-input").val(el.text());

    el = target.find(".-cvz-data-content");
    $("#fragment-content-editor").val(el.text());

    var $window = $(window);
	var $editorFrame = $("#editor-frame");
    onClickFragmentEditorTrigger($editorFrame, $window, event, MSG.label_edit, false);
}

function prepareFragmentContent() {
    var $editor = $("#fragment-content-editor");
    var srcContent = $editor.val();
    $("#fragment-editor-form\\:content-placeholder").val(srcContent);
    return srcContent;
}

function previewFragment() {
	$("#fragment-overlay").lightbox_me({
        centered:false
        , showOverlay:false
        , lightboxSpeed:"fast"
        , closeSelector:"#fragment-overlay-close-button"
        , closeEsc:true
        , modalCSS:{ position:"fixed", bottom: '2%', right: '2%' }
    });

	$("#fragment-overlay-title").text(MSG.label_preview);

	var srcContent = prepareFragmentContent();
    var outputHtml = $("").wrapInner(translateFragmentContent(srcContent));
    postprocessFragmentContent($("#fragment-overlay-content").html(outputHtml));
}

function autocompleteForTypingTags() {
	var tagSuggestions = [];
    $("#tag-palette-flat .each-tag").each(function (index, value) {
        tagSuggestions.push($(value).find(".each-tag-name").text());
    });

    var split = function (input) {
        return input.trim().split(/\,\s*/);
    }

    function alreadyTyped(typedTags, suggestion) {
    	for (var i=0; i<typedTags.length; ++i) {
    		if (typedTags[i] == suggestion) return true;
    	}
    	return false;
    }

    $("#fragment-editor-form\\:tags-input, #fragment-group-form\\:search-panel\\:tag-keywords").autocomplete({
        source: function(req, res) {
            var tags = split(req.term);
            if (tags.length && tags[tags.length-1] == "") {
                tags.pop();
            }
            var typed = tags[tags.length-1];
            tags.pop();
            var prefix = "";
            for (var i=0; i<tags.length; ++i) {
                prefix += tags[i] + ",";
            }
            var output = [];
            for (var i=0; i<tagSuggestions.length; ++i) {
                var suggestion = tagSuggestions[i];
                if (suggestion.indexOf(typed) == 0 && ! alreadyTyped(tags, suggestion)) {
                    output.push(suggestion);
                }
            }
            res(output);
        },
        focus: function () {
            // prevent value inserted on focus
            return false;
        },
        select: function (event, ui) {
            var terms = split(this.value);
            // remove the current input
            terms.pop();
            // add the selected item
            terms.push(ui.item.value);
            // add placeholder to get the comma-and-space at the end
            this.value = terms.join(", ") + ", ";
            return false;
        },
        autoFocus: true,
    });
}
<commit_msg>[bug fix] back button appeared on fragment preview popup<commit_after>function setupFragmentEditor() {
	var $window = $(window);
	var $editorFrame = $("#editor-frame");

	$("#new-fragment-editor-trigger, .new-fragment-editor-trigger-on-toolbar").click(function (e) {
		onClickFragmentEditorTrigger($editorFrame, $window, e, MSG.label_new_fragment, true);
    });

    $editorFrame.resizable({
        alsoResize: "#markItUpFragment-content-editor, #fragment-content-editor",
        minWidth: 581,
        minHeight: 234,
    });

	$editorFrame.draggable({
    	handle:"#editor-title-bar, #editor-button-bar"
    	, cursor:"move"
    });

    $("#editor-title-bar").dblclick(function() {
    	toggleFragmentEditor($editorFrame, $(this));
    });

    $("#fragment-content-editor").markItUp(markItUpSettings, {});
}

function toggleFragmentEditor(frame, bar) {
	var target = bar.next();
	var ph = frame.data("prev-h");
	frame.data("prev-h", frame.height());
	if (target.is(":visible")) {
		var w = frame.width();
		if (frame.hasClass("ui-resizable"))
			frame.resizable("disable");
		target.hide();
		frame.width(w);
		frame.data("min-h", frame.css("min-height"));
		frame.css("min-height", bar.height());
		frame.height(bar.height());
	}
	else {
		if (frame.hasClass("ui-resizable"))
			frame.resizable("enable");
		target.show();
		frame.height(ph);
		frame.css("min-height", frame.data("min-h"));
	}
}

function onClickFragmentEditorTrigger($editorFrame, $window, e, title, forNewFragment) {
	var editorHeight = Math.max($editorFrame.height(), 696);
    var top = ($window.height() - editorHeight) / 2 + $window.scrollTop() + "px";

    if (forNewFragment) {
    	var el = $("#fragment-editor-form\\:title-input");
        el.val(null);
        el.watermark(MSG.label_title);

        el = $("#fragment-editor-form\\:tags-input");
        el.val(null);
        el.watermark(MSG.how_to_input_tags);

        $("#fragment-content-editor").val(null);
    }

    $editorFrame.lightbox_me({
        centered:false
        , showOverlay:false
        , lightboxSpeed:"slow"
        , closeSelector:"#editor-frame-close-button"
        , closeEsc:false
        , modalCSS:{ position:"absolute", top:top, left:"30%" }
        , onClose:clearFragmentEditor
    });

    var titleBar = $("#editor-title-bar");
    if (titleBar.next().is(":visible") == false) {
    	toggleFragmentEditor($editorFrame, titleBar);
    }

    $("#editor-title").text(title);

    e.preventDefault();
}

function clearFragmentEditor() {
    $("#fragment-content-editor").val(null);
    $("#fragment-editor-form\\:id-placeholder-for-fragment").val(null);

    // If the preview pop-up is open, close it
    var frgOverlay = $("#fragment-overlay");
    if (frgOverlay.find(".fragment-header").length == 0) {
        frgOverlay.trigger("close");
    }

    // [NOTE] without the following code,
    // jQuery UI Autocomplete object won't disapper even after its parent is closed
    $("#fragment-editor-form\\:tags-input").autocomplete("widget").hide();
}

function parseMarkdown(inputText) {
    return marked(inputText);
}

function openFragmentEditorForEdit(event) {
	var menu = $("#frg-context-menu");
	var target = menu.data("target-frg");
	var fragmentId = target.attr("_fid");
	var panelId = findPanel(target);

	$("#fragment-editor-form\\:id-placeholder-for-fragment").val(fragmentId);

    var el = target.find(".-cvz-data-title");
    $("#fragment-editor-form\\:title-input").val(el.text());

    el = target.find(".-cvz-data-tags");
    $("#fragment-editor-form\\:tags-input").val(el.text());

    el = target.find(".-cvz-data-content");
    $("#fragment-content-editor").val(el.text());

    var $window = $(window);
	var $editorFrame = $("#editor-frame");
    onClickFragmentEditorTrigger($editorFrame, $window, event, MSG.label_edit, false);
}

function prepareFragmentContent() {
    var $editor = $("#fragment-content-editor");
    var srcContent = $editor.val();
    $("#fragment-editor-form\\:content-placeholder").val(srcContent);
    return srcContent;
}

function previewFragment() {
	$("#fragment-overlay").lightbox_me({
        centered:false
        , showOverlay:false
        , lightboxSpeed:"fast"
        , closeSelector:"#fragment-overlay-close-button"
        , closeEsc:true
        , modalCSS:{ position:"fixed", bottom: '2%', right: '2%' }
    });

	$("#fragmen-overlay-back-button").hide();

	$("#fragment-overlay-title").text(MSG.label_preview);

	var srcContent = prepareFragmentContent();
    var outputHtml = $("").wrapInner(translateFragmentContent(srcContent));
    postprocessFragmentContent($("#fragment-overlay-content").html(outputHtml));
}

function autocompleteForTypingTags() {
	var tagSuggestions = [];
    $("#tag-palette-flat .each-tag").each(function (index, value) {
        tagSuggestions.push($(value).find(".each-tag-name").text());
    });

    var split = function (input) {
        return input.trim().split(/\,\s*/);
    }

    function alreadyTyped(typedTags, suggestion) {
    	for (var i=0; i<typedTags.length; ++i) {
    		if (typedTags[i] == suggestion) return true;
    	}
    	return false;
    }

    $("#fragment-editor-form\\:tags-input, #fragment-group-form\\:search-panel\\:tag-keywords").autocomplete({
        source: function(req, res) {
            var tags = split(req.term);
            if (tags.length && tags[tags.length-1] == "") {
                tags.pop();
            }
            var typed = tags[tags.length-1];
            tags.pop();
            var prefix = "";
            for (var i=0; i<tags.length; ++i) {
                prefix += tags[i] + ",";
            }
            var output = [];
            for (var i=0; i<tagSuggestions.length; ++i) {
                var suggestion = tagSuggestions[i];
                if (suggestion.indexOf(typed) == 0 && ! alreadyTyped(tags, suggestion)) {
                    output.push(suggestion);
                }
            }
            res(output);
        },
        focus: function () {
            // prevent value inserted on focus
            return false;
        },
        select: function (event, ui) {
            var terms = split(this.value);
            // remove the current input
            terms.pop();
            // add the selected item
            terms.push(ui.item.value);
            // add placeholder to get the comma-and-space at the end
            this.value = terms.join(", ") + ", ";
            return false;
        },
        autoFocus: true,
    });
}
<|endoftext|>


<commit_before>import { parse, validate } from "protagonist";
import { Namespace } from "api-elements";

const namespace = new Namespace();

const load = async (source, options = {}) => {
  const refract = await parse(source, options);
  return namespace.fromRefract(refract);
};

export { load, parse, validate };
<commit_msg>wrap fromRefract function<commit_after>import { parse, validate } from "protagonist";
import { Namespace } from "api-elements";

const namespace = new Namespace();
const fromRefract = result => {
  return namespace.fromRefract(result);
};

export { parse, validate, fromRefract };
<|endoftext|>


<commit_before>/**
 * Copyright 2013 Google, Inc. All Rights Reserved.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/**
 * @fileoverview Firefox extension main script.
 *
 * @author benvanik@google.com (Ben Vanik)
 */

var pageMod = require('sdk/page-mod');
var prefs = require('sdk/preferences/service');
var self = require('sdk/self');
var ss = require('sdk/simple-storage');
var tabs = require('sdk/tabs');
var timers = require('sdk/timers');
var widgets = require('sdk/widget');
var MatchPattern;
try {
  MatchPattern = require('sdk/util/match-pattern').MatchPattern;
} catch (e) {
  MatchPattern = require('sdk/page-mod/match-pattern').MatchPattern;
}

const {Cc, Ci, Cu} = require('chrome');
Cu.import('resource://gre/modules/Services.jsm');

var prefRoot = 'extensions.' + self.id + '.';

function setTemporaryPreference(key, value) {
  var json = prefs.get(prefRoot + 'modifiedPrefs', '{}');
  var modifiedPrefs = JSON.parse(json);
  if (!modifiedPrefs.hasOwnProperty(key)) {
    modifiedPrefs[key] = prefs.get(key);
  }
  prefs.set(prefRoot + 'modifiedPrefs', JSON.stringify(modifiedPrefs));

  prefs.set(key, value);
};
function resetTemporaryPreference(key) {
  var json = prefs.get(prefRoot + 'modifiedPrefs', '{}');
  var modifiedPrefs = JSON.parse(json);
  if (modifiedPrefs.hasOwnProperty(key)) {
    var originalValue = modifiedPrefs[key];
    prefs.set(key, originalValue);
    delete modifiedPrefs[key];
    prefs.set(prefRoot + 'modifiedPrefs', JSON.stringify(modifiedPrefs));
  }
};
function resetTemporaryPreferences() {
  var json = prefs.get(prefRoot + 'modifiedPrefs', '{}');
  var modifiedPrefs = JSON.parse(json);
  for (var key in modifiedPrefs) {
    prefs.set(key, modifiedPrefs[key]);
    prefs.set(prefRoot + 'modifiedPrefs', '{}');
  }
};

function getAllEnabledPageUrls() {
  var pageStore = ss.storage.pageStore || {};
  var urls = [];
  for (var key in pageStore) {
    if (pageStore[key]['enabled']) {
      urls.push(key);
    }
  }
  return urls;
};

function getPagePreferences(url) {
  var pageStore = ss.storage.pageStore || {};
  return pageStore[url];
};

function setPagePreference(url, key, value) {
  var pageStore = ss.storage.pageStore || {};
  var entry = pageStore[url];
  if (!entry) {
    entry = {};
  }
  if (entry[key] == value) {
    return false;
  }
  entry[key] = value;
  pageStore[url] = entry;
  ss.storage.pageStore = pageStore;
  return true;
};

/**
 * A list of all workers currently injected and active.
 * @type {!Array.<InjectedTab>}
 */
var injectedTabs = [];

/**
 * Reference count for the memory observer.
 * @type {number}
 */
var memoryObservationCount = 0;

/**
 * Handler for memory observation events.
 * @param {*} subject Anything.
 * @param {string} topic Event name.
 * @param {[type]} json JSON string of GC data.
 */
function handleMemoryEvent(subject, topic, json) {
  if (topic != 'garbage-collection-statistics') {
    return;
  }

  // Broadcast the event to all active workers.
  // We avoid parsing it here and let the pages handle it.
  for each (injectedTab in injectedTabs) {
    injectedTab.queueDebuggerRecord(['gc', json]);
  }
};

/**
 * Acquires a reference to the memory observer, starting it if this is the first
 * reference.
 */
function acquireMemoryObserver() {
  // Only enable on first use.
  ++memoryObservationCount;
  if (memoryObservationCount > 1) {
    return;
  }

  // Enable memory notification.
  setTemporaryPreference('javascript.options.mem.notify', true);

  // Listen for events.
  Services.obs.addObserver(
      handleMemoryEvent, 'garbage-collection-statistics', false);
};

/**
 * Releases a reference to the memory observer, stopping it if this was the
 * last reference.
 */
function releaseMemoryObserver() {
  // Only disable on last use.
  if (!memoryObservationCount) {
    return;
  }
  --memoryObservationCount;
  if (memoryObservationCount) {
    return;
  }

  // Unlisten for events.
  Services.obs.removeObserver(
      handleMemoryEvent, 'garbage-collection-statistics', false);

  // Reset memory notification to its original state.
  resetTemporaryPreference('javascript.options.mem.notify');
};

/**
 * All active page mods mapped by canonical URL.
 * @type {!Object.<PageMod>}
 */
var activePageMods = {};

/**
 * Canonicalizes a URL so that it can be matched against.
 * @param {string} url URL.
 * @return {string} Canonical URL.
 */
function getCanonicalUrl(url) {
  // Trim the #fragment. We are unique to query string, though.
  var hashIndex = url.indexOf('#');
  if (hashIndex != -1) {
    url = url.substring(0, hashIndex);
  }
  return url;
};

/**
 * Reloads all tabs that match the given URL (or #fragment variants).
 * @param {string} url URL of tabs to reload.
 */
function reloadTabsMatchingUrl(url) {
  var patterns = [
    new MatchPattern(url),
    new MatchPattern(url + '#*')
  ];
  for each (var tab in tabs) {
    var matchesAny = false;
    for each (var pattern in patterns) {
      if (pattern.test(tab.url)) {
        matchesAny = true;
        break;
      }
    }
    if (matchesAny) {
      tab.reload();
    }
  }
};

/**
 *
 * @param {string} url URL to test.
 * @return {boolean} True if injection is enabled for the given URL.
 */
function isInjectionEnabledForUrl(url) {
  var url = getCanonicalUrl(url);
  return !!activePageMods[url];
};

var wtfScriptContents = self.data.load('wtf_trace_web_js_compiled.js');

/**
 * Injected tab wrapper.
 * @param {string} url Canonical page URL.
 * @param {!Worker} worker Content script worker.
 * @constructor
 */
var InjectedTab = function(url, worker) {
  /**
   * Page URL.
   * @type {string}
   */
  this.url = url;

  /**
   * Firefox tab handle.
   * @type {!Tab}
   */
  this.tab = worker.tab;

  /**
   * Page worker running the content script.
   * @type {!Worker}
   */
  this.worker = worker;

  /**
   * Pending debugger records.
   * These are batched up so that we don't throw too many messages at the page.
   * @type {!Array.<!Object>}
   * @private
   */
  this.debuggerRecords_ = [];

  /**
   * Periodic timer to transmit debugger data.
   * @type {number}
   * @private
   */
  this.debuggerTransmitId_ = timers.setInterval((function() {
    var records = this.debuggerRecords_;
    if (records.length) {
      this.debuggerRecords_ = [];
      this.worker.port.emit('extension-event', JSON.stringify({
        'command': 'debugger_data',
        'records': records
      }));
    }
  }).bind(this), 1000);

  // Start watching GC events.
  acquireMemoryObserver();
};
InjectedTab.prototype.dispose = function() {
  // Clear off timer.
  if (this.debuggerTransmitId_ != -1) {
    timers.clearInterval(this.debuggerTransmitId_);
  }

  // End watching GC events.
  releaseMemoryObserver();
};
InjectedTab.prototype.queueDebuggerRecord = function(record) {
  this.debuggerRecords_.push(record);
};
InjectedTab.prototype.dispatchEvent = function(data) {
  switch (data['command']) {
    // Reloads the tab (bypassing cache).
    case 'reload':
      // TODO(benvanik): ensure this bypasses the cache.
      this.tab.reload();
      break;

    // Updates the tab settings.
    case 'save_settings':
      if (setPagePreference(this.url, 'options', data['content'])) {
        if (data['reload']) {
          disableInjectionForUrl(this.url, true);
          enableInjectionForUrl(this.url);
        }
      }
      break;

    // Pops up a UI with the given snapshot data.
    case 'show_snapshot':
      // data['page_url'],
      // data['new_window'],
      // data['content_types'],
      // data['content_sources'],
      // data['content_urls'],
      // data['content_length']
      break;

    // Grabs any pending data.
    case 'clear_debugger_data':
      this.debuggerRecords_ = [];
      break;
    case 'get_debugger_data':
      if (this.debuggerRecords_.length) {
        var records = this.debuggerRecords_;
        this.debuggerRecords_ = [];
        this.worker.emit('extension-event', {
          'command': 'debugger_data',
          'request_id': data['request_id'],
          'records': records
        });
      }
      break;
  }
};

/**
 * Enables injection for a URL and reloads tabs with that URL.
 * @param {string} url URL to activate on.
 */
function enableInjectionForUrl(url) {
  var url = getCanonicalUrl(url);
  if (activePageMods[url]) {
    return;
  }

  // Enable injection.
  setPagePreference(url, 'enabled', true);

  // Grab current options. Note that if they change we need to re-enable
  // injection to update the pagemod.
  var pagePrefs = getPagePreferences(url);
  var storedOptions = {};
  try {
    storedOptions = JSON.parse(pagePrefs.options);
  } catch (e) {
  }
  // Override defaults with specified values.
  // Except for a few that we don't want to track across session.
  var pageOptions = {
    // The presence of this indicates that the options come from the injector.
    'wtf.injector': true,

    // Larger buffers mean less waste when doing recordings with a large amount
    // of data (like WebGL captures).
    'wtf.trace.session.bufferSize': 6 * 1024 * 1024,

    // This is pretty excessive, but keeps us from truncating WebGL traces.
    // After this limit the file likely won't load due to v8 memory limits
    // anyway.
    'wtf.trace.session.maximumMemoryUsage': 512 * 1024 * 1024,

    // TODO(benvanik): endpoints
    // 'wtf.hud.app.mode': '',
    // 'wtf.hud.app.endpoint': '',

    'wtf.trace.provider.firefoxDebug.present': true
  };
  for (var key in storedOptions) {
    switch (key) {
      case 'wtf.injector':
      case 'wtf.hud.app.mode':
      case 'wtf.hud.app.endpoint':
      case 'wtf.addons':
      case 'wtf.trace.provider.firefoxDebug.present':
        continue;
    }
    pageOptions[key] = storedOptions[key];
  }

  // Create a page mod for the given URLs.
  var mod = pageMod.PageMod({
    include: [
      url,
      url + '#*'
    ],
    contentScriptFile: self.data.url('content-script.js'),
    contentScriptWhen: 'start',
    contentScriptOptions: {
      // Accessible as self.options in the content script.
      wtfScriptContents: wtfScriptContents,
      wtfOptions: pageOptions
    },
    attachTo: 'top',
    onAttach: function(worker) {
      var injectedTab = new InjectedTab(url, worker);
      injectedTabs.push(injectedTab);
      console.log('added worker for ' + url);

      worker.port.on('page-event', function(data) {
        injectedTab.dispatchEvent(JSON.parse(data));
      });

      worker.on('detach', function() {
        console.log('detached worker for ' + url);
        injectedTab.dispose();
        for (var n = 0; n < injectedTabs.length; n++) {
          if (injectedTabs[n].worker == worker) {
            injectedTabs.splice(n, 1);
            break;
          }
        }

      });
    }
  });
  activePageMods[url] = mod;

  // Find tabs with the URL and reload.
  reloadTabsMatchingUrl(url);
};

/**
 * Disables injection for a URL and reloads tabs with that URL.
 * @param {string} url URL to activate on.
 * @param {boolean=} opt_skipReload Whether to skip reloading of pages.
 */
function disableInjectionForUrl(url, opt_skipReload) {
  var url = getCanonicalUrl(url);

  // Disable injection.
  setPagePreference(url, 'enabled', false);

  // Find existing page mod and disable.
  // This will detach workers in those tabs.
  var mod = activePageMods[url];
  if (!mod) {
    return;
  }
  mod.destroy();
  delete activePageMods[url];

  if (!opt_skipReload) {
    // Find tabs with the URL and reload.
    reloadTabsMatchingUrl(url);
  }
};

var widget = widgets.Widget({
  id: 'wtf-toggle',
  label: 'Toggle Web Tracing Framework',
  contentURL: self.data.url('assets/icons/wtf-32.png'),
  onClick: function() {
    var url = tabs.activeTab.url;
    if (!isInjectionEnabledForUrl(url)) {
      enableInjectionForUrl(url);
    } else {
      disableInjectionForUrl(url);
    }
  }
});

exports.main = function(options, callbacks) {
  // Reset any temporary preferences that weren't properly reset last unload,
  // such as in the case of a browser crash.
  resetTemporaryPreferences();

  // Enable all pagemods for injected pages now.
  // This will reload any that are currently open.
  for each (var url in getAllEnabledPageUrls()) {
    enableInjectionForUrl(url);
  }
};

exports.onUnload = function (reason) {
  resetTemporaryPreferences();
};
<commit_msg>Adding context menu controls.<commit_after>/**
 * Copyright 2013 Google, Inc. All Rights Reserved.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/**
 * @fileoverview Firefox extension main script.
 *
 * @author benvanik@google.com (Ben Vanik)
 */

var contextMenu = require('sdk/context-menu');
var pageMod = require('sdk/page-mod');
var prefs = require('sdk/preferences/service');
var self = require('sdk/self');
var ss = require('sdk/simple-storage');
var tabs = require('sdk/tabs');
var timers = require('sdk/timers');
var widgets = require('sdk/widget');
var MatchPattern;
try {
  MatchPattern = require('sdk/util/match-pattern').MatchPattern;
} catch (e) {
  MatchPattern = require('sdk/page-mod/match-pattern').MatchPattern;
}

const {Cc, Ci, Cu} = require('chrome');
Cu.import('resource://gre/modules/Services.jsm');

var prefRoot = 'extensions.' + self.id + '.';

function setTemporaryPreference(key, value) {
  var json = prefs.get(prefRoot + 'modifiedPrefs', '{}');
  var modifiedPrefs = JSON.parse(json);
  if (!modifiedPrefs.hasOwnProperty(key)) {
    modifiedPrefs[key] = prefs.get(key);
  }
  prefs.set(prefRoot + 'modifiedPrefs', JSON.stringify(modifiedPrefs));

  prefs.set(key, value);
};
function resetTemporaryPreference(key) {
  var json = prefs.get(prefRoot + 'modifiedPrefs', '{}');
  var modifiedPrefs = JSON.parse(json);
  if (modifiedPrefs.hasOwnProperty(key)) {
    var originalValue = modifiedPrefs[key];
    prefs.set(key, originalValue);
    delete modifiedPrefs[key];
    prefs.set(prefRoot + 'modifiedPrefs', JSON.stringify(modifiedPrefs));
  }
};
function resetTemporaryPreferences() {
  var json = prefs.get(prefRoot + 'modifiedPrefs', '{}');
  var modifiedPrefs = JSON.parse(json);
  for (var key in modifiedPrefs) {
    prefs.set(key, modifiedPrefs[key]);
    prefs.set(prefRoot + 'modifiedPrefs', '{}');
  }
};

function getAllEnabledPageUrls() {
  var pageStore = ss.storage.pageStore || {};
  var urls = [];
  for (var key in pageStore) {
    if (pageStore[key]['enabled']) {
      urls.push(key);
    }
  }
  return urls;
};

function getPagePreferences(url) {
  var pageStore = ss.storage.pageStore || {};
  return pageStore[url];
};

function setPagePreference(url, key, value) {
  var pageStore = ss.storage.pageStore || {};
  var entry = pageStore[url];
  if (!entry) {
    entry = {};
  }
  if (entry[key] == value) {
    return false;
  }
  entry[key] = value;
  pageStore[url] = entry;
  ss.storage.pageStore = pageStore;
  return true;
};

function resetPagePreferences(url) {
  var pageStore = ss.storage.pageStore || {};
  if (!pageStore[url]) {
    return false;
  }
  delete pageStore[url];
  return true;
};

/**
 * A list of all workers currently injected and active.
 * @type {!Array.<InjectedTab>}
 */
var injectedTabs = [];

/**
 * Reference count for the memory observer.
 * @type {number}
 */
var memoryObservationCount = 0;

/**
 * Handler for memory observation events.
 * @param {*} subject Anything.
 * @param {string} topic Event name.
 * @param {[type]} json JSON string of GC data.
 */
function handleMemoryEvent(subject, topic, json) {
  if (topic != 'garbage-collection-statistics') {
    return;
  }

  // Broadcast the event to all active workers.
  // We avoid parsing it here and let the pages handle it.
  for each (injectedTab in injectedTabs) {
    injectedTab.queueDebuggerRecord(['gc', json]);
  }
};

/**
 * Acquires a reference to the memory observer, starting it if this is the first
 * reference.
 */
function acquireMemoryObserver() {
  // Only enable on first use.
  ++memoryObservationCount;
  if (memoryObservationCount > 1) {
    return;
  }

  // Enable memory notification.
  setTemporaryPreference('javascript.options.mem.notify', true);

  // Listen for events.
  Services.obs.addObserver(
      handleMemoryEvent, 'garbage-collection-statistics', false);
};

/**
 * Releases a reference to the memory observer, stopping it if this was the
 * last reference.
 */
function releaseMemoryObserver() {
  // Only disable on last use.
  if (!memoryObservationCount) {
    return;
  }
  --memoryObservationCount;
  if (memoryObservationCount) {
    return;
  }

  // Unlisten for events.
  Services.obs.removeObserver(
      handleMemoryEvent, 'garbage-collection-statistics', false);

  // Reset memory notification to its original state.
  resetTemporaryPreference('javascript.options.mem.notify');
};

/**
 * All active page mods mapped by canonical URL.
 * @type {!Object.<PageMod>}
 */
var activePageMods = {};

/**
 * Canonicalizes a URL so that it can be matched against.
 * @param {string} url URL.
 * @return {string} Canonical URL.
 */
function getCanonicalUrl(url) {
  // Trim the #fragment. We are unique to query string, though.
  var hashIndex = url.indexOf('#');
  if (hashIndex != -1) {
    url = url.substring(0, hashIndex);
  }
  return url;
};

/**
 * Reloads all tabs that match the given URL (or #fragment variants).
 * @param {string} url URL of tabs to reload.
 */
function reloadTabsMatchingUrl(url) {
  var patterns = [
    new MatchPattern(url),
    new MatchPattern(url + '#*')
  ];
  for each (var tab in tabs) {
    var matchesAny = false;
    for each (var pattern in patterns) {
      if (pattern.test(tab.url)) {
        matchesAny = true;
        break;
      }
    }
    if (matchesAny) {
      tab.reload();
    }
  }
};

/**
 *
 * @param {string} url URL to test.
 * @return {boolean} True if injection is enabled for the given URL.
 */
function isInjectionEnabledForUrl(url) {
  var url = getCanonicalUrl(url);
  return !!activePageMods[url];
};

var wtfScriptContents = self.data.load('wtf_trace_web_js_compiled.js');

/**
 * Injected tab wrapper.
 * @param {string} url Canonical page URL.
 * @param {!Worker} worker Content script worker.
 * @constructor
 */
var InjectedTab = function(url, worker) {
  /**
   * Page URL.
   * @type {string}
   */
  this.url = url;

  /**
   * Firefox tab handle.
   * @type {!Tab}
   */
  this.tab = worker.tab;

  /**
   * Page worker running the content script.
   * @type {!Worker}
   */
  this.worker = worker;

  /**
   * Pending debugger records.
   * These are batched up so that we don't throw too many messages at the page.
   * @type {!Array.<!Object>}
   * @private
   */
  this.debuggerRecords_ = [];

  /**
   * Periodic timer to transmit debugger data.
   * @type {number}
   * @private
   */
  this.debuggerTransmitId_ = timers.setInterval((function() {
    var records = this.debuggerRecords_;
    if (records.length) {
      this.debuggerRecords_ = [];
      this.worker.port.emit('extension-event', JSON.stringify({
        'command': 'debugger_data',
        'records': records
      }));
    }
  }).bind(this), 1000);

  // Start watching GC events.
  acquireMemoryObserver();
};
InjectedTab.prototype.dispose = function() {
  // Clear off timer.
  if (this.debuggerTransmitId_ != -1) {
    timers.clearInterval(this.debuggerTransmitId_);
  }

  // End watching GC events.
  releaseMemoryObserver();
};
InjectedTab.prototype.queueDebuggerRecord = function(record) {
  this.debuggerRecords_.push(record);
};
InjectedTab.prototype.dispatchEvent = function(data) {
  switch (data['command']) {
    // Reloads the tab (bypassing cache).
    case 'reload':
      // TODO(benvanik): ensure this bypasses the cache.
      this.tab.reload();
      break;

    // Updates the tab settings.
    case 'save_settings':
      if (setPagePreference(this.url, 'options', data['content'])) {
        if (data['reload']) {
          disableInjectionForUrl(this.url, true);
          enableInjectionForUrl(this.url);
        }
      }
      break;

    // Pops up a UI with the given snapshot data.
    case 'show_snapshot':
      // data['page_url'],
      // data['new_window'],
      // data['content_types'],
      // data['content_sources'],
      // data['content_urls'],
      // data['content_length']
      break;

    // Grabs any pending data.
    case 'clear_debugger_data':
      this.debuggerRecords_ = [];
      break;
    case 'get_debugger_data':
      if (this.debuggerRecords_.length) {
        var records = this.debuggerRecords_;
        this.debuggerRecords_ = [];
        this.worker.emit('extension-event', {
          'command': 'debugger_data',
          'request_id': data['request_id'],
          'records': records
        });
      }
      break;
  }
};

/**
 * Enables injection for a URL and reloads tabs with that URL.
 * @param {string} url URL to activate on.
 */
function enableInjectionForUrl(url) {
  var url = getCanonicalUrl(url);
  if (activePageMods[url]) {
    return;
  }

  // Enable injection.
  setPagePreference(url, 'enabled', true);

  // Grab current options. Note that if they change we need to re-enable
  // injection to update the pagemod.
  var pagePrefs = getPagePreferences(url);
  var storedOptions = {};
  try {
    storedOptions = JSON.parse(pagePrefs.options);
  } catch (e) {
  }
  // Override defaults with specified values.
  // Except for a few that we don't want to track across session.
  var pageOptions = {
    // The presence of this indicates that the options come from the injector.
    'wtf.injector': true,

    // Larger buffers mean less waste when doing recordings with a large amount
    // of data (like WebGL captures).
    'wtf.trace.session.bufferSize': 6 * 1024 * 1024,

    // This is pretty excessive, but keeps us from truncating WebGL traces.
    // After this limit the file likely won't load due to v8 memory limits
    // anyway.
    'wtf.trace.session.maximumMemoryUsage': 512 * 1024 * 1024,

    // TODO(benvanik): endpoints
    // 'wtf.hud.app.mode': '',
    // 'wtf.hud.app.endpoint': '',

    'wtf.trace.provider.firefoxDebug.present': true
  };
  for (var key in storedOptions) {
    switch (key) {
      case 'wtf.injector':
      case 'wtf.hud.app.mode':
      case 'wtf.hud.app.endpoint':
      case 'wtf.addons':
      case 'wtf.trace.provider.firefoxDebug.present':
        continue;
    }
    pageOptions[key] = storedOptions[key];
  }

  // Create a page mod for the given URLs.
  var mod = pageMod.PageMod({
    include: [
      url,
      url + '#*'
    ],
    contentScriptFile: self.data.url('content-script.js'),
    contentScriptWhen: 'start',
    contentScriptOptions: {
      // Accessible as self.options in the content script.
      wtfScriptContents: wtfScriptContents,
      wtfOptions: pageOptions
    },
    attachTo: 'top',
    onAttach: function(worker) {
      var injectedTab = new InjectedTab(url, worker);
      injectedTabs.push(injectedTab);
      console.log('added worker for ' + url);

      worker.port.on('page-event', function(data) {
        injectedTab.dispatchEvent(JSON.parse(data));
      });

      worker.on('detach', function() {
        console.log('detached worker for ' + url);
        injectedTab.dispose();
        for (var n = 0; n < injectedTabs.length; n++) {
          if (injectedTabs[n].worker == worker) {
            injectedTabs.splice(n, 1);
            break;
          }
        }

      });
    }
  });
  activePageMods[url] = mod;

  // Find tabs with the URL and reload.
  reloadTabsMatchingUrl(url);
};

/**
 * Disables injection for a URL and reloads tabs with that URL.
 * @param {string} url URL to activate on.
 * @param {boolean=} opt_skipReload Whether to skip reloading of pages.
 */
function disableInjectionForUrl(url, opt_skipReload) {
  var url = getCanonicalUrl(url);

  // Disable injection.
  setPagePreference(url, 'enabled', false);

  // Find existing page mod and disable.
  // This will detach workers in those tabs.
  var mod = activePageMods[url];
  if (!mod) {
    return;
  }
  mod.destroy();
  delete activePageMods[url];

  if (!opt_skipReload) {
    // Find tabs with the URL and reload.
    reloadTabsMatchingUrl(url);
  }
};

/**
 * Toggles injection for the currently active tab URL.
 */
function toggleInjectionForActiveTab() {
  var url = tabs.activeTab.url;
  if (!isInjectionEnabledForUrl(url)) {
    enableInjectionForUrl(url);
  } else {
    disableInjectionForUrl(url);
  }
};

/**
 * Sets up the page context menu.
 */
function setupContextMenu() {
  var enableContextMenuItem = contextMenu.Item({
    label: 'Enable For This URL',
    data: 'toggle',
    contentScript:
        "self.on('context', function(node) {" +
        "  var hasWtf = !!document.querySelector('.wtf_a');" +
        "  return !hasWtf ? 'Enable For This URL' : 'Disable For This URL';" +
        "});"
  });
  var resetSettingsContextMenuItem = contextMenu.Item({
    label: 'Reset Settings For This URL',
    data: 'reset'
  });
  var rootContextMenuItem = contextMenu.Menu({
    label: 'Tracing Framework',
    items: [
      enableContextMenuItem,
      resetSettingsContextMenuItem
    ],
    contentScript:
        "self.on('click', function(node, data) {" +
        "  self.postMessage(data);" +
        "});",
    onMessage: function(action) {
      switch (action) {
        case 'toggle':
          toggleInjectionForActiveTab();
          break;
        case 'reset':
          var url = getCanonicalUrl(tabs.activeTab.url);
          setPagePreference(url, 'options', '');
          if (isInjectionEnabledForUrl(url)) {
            disableInjectionForUrl(url, true);
            enableInjectionForUrl(url);
          }
          break;
      }
    }
  });
};

/**
 * Sets up the addon bar widget.
 */
function setupAddonBarWidget() {
  var widget = widgets.Widget({
    id: 'wtf-toggle',
    label: 'Toggle Web Tracing Framework',
    contentURL: self.data.url('assets/icons/wtf-32.png'),
    onClick: function() {
      toggleInjectionForActiveTab();
    }
  });
};

exports.main = function(options, callbacks) {
  // Reset any temporary preferences that weren't properly reset last unload,
  // such as in the case of a browser crash.
  resetTemporaryPreferences();

  // Setup UI/etc.
  setupContextMenu();
  setupAddonBarWidget();

  // Enable all pagemods for injected pages now.
  // This will reload any that are currently open.
  for each (var url in getAllEnabledPageUrls()) {
    enableInjectionForUrl(url);
  }
};

exports.onUnload = function (reason) {
  resetTemporaryPreferences();
};
<|endoftext|>