<commit_before>import warning from 'fbjs/lib/warning';
import KPLineBreaker from '@react-pdf/linebreaker';
import FontSubstitutionEngine from '@react-pdf/font-substitution';
import {
  Path,
  LayoutEngine,
  AttributedString,
  Container,
  TextRenderer,
  JustificationEngine,
} from '@react-pdf/textkit';
import upperFirst from 'lodash.upperfirst';
import Font from '../font';

// Global layout engine
// It's created dynamically because it may accept a custom hyphenation callback
let LAYOUT_ENGINE;
const INFINITY = 99999;

class TextEngine {
  constructor(element) {
    this.element = element;
    this._container = null;
    this.start = 0;
    this.end = 0;
    this.computed = false;
  }

  get container() {
    const lines = this._container.blocks.reduce(
      (acc, block) => [...acc, ...block.lines],
      [],
    );

    return {
      ...this._container,
      blocks: [{ lines: lines.splice(this.start, this.end) }],
    };
  }

  get layoutEngine() {
    if (!LAYOUT_ENGINE) {
      const shrinkWhitespaceFactor = { before: -0.5, after: -0.5 };

      LAYOUT_ENGINE = new LayoutEngine({
        lineBreaker: new KPLineBreaker(Font.getHyphenationCallback()),
        fontSubstitutionEngine: new FontSubstitutionEngine(),
        justificationEngine: new JustificationEngine({
          shrinkWhitespaceFactor,
        }),
      });
    }

    return LAYOUT_ENGINE;
  }

  get lines() {
    if (!this.container) {
      return [];
    }

    return this.container.blocks.reduce(
      (acc, block) => [...acc, ...block.lines],
      [],
    );
  }

  get height() {
    if (!this._container) {
      return -1;
    }

    return this.lines.reduce((acc, line) => acc + line.height, 0);
  }

  get width() {
    if (!this._container) {
      return -1;
    }

    return Math.max(...this.lines.map(line => line.advanceWidth));
  }

  get attributedString() {
    const fragments = [];
    const {
      color = 'black',
      fontFamily = 'Helvetica',
      fontSize = 18,
      textAlign = 'left',
      position,
      top,
      bottom,
      align,
      lineHeight,
      textDecoration,
      textDecorationColor,
      textDecorationStyle,
      textTransform,
    } = this.element.getComputedStyles();

    warning(
      !align,
      '"align" style prop will be deprecated on future versions. Please use "textAlign" instead in Text node',
    );

    this.element.children.forEach(child => {
      if (typeof child === 'string') {
        const obj = Font.getFont(fontFamily);
        const font = obj ? obj.data : fontFamily;
        const string = this.transformText(child, textTransform);

        fragments.push({
          string,
          attributes: {
            font,
            color,
            fontSize,
            align: textAlign,
            link: this.element.src,
            underlineStyle: textDecorationStyle,
            underline: textDecoration === 'underline',
            underlineColor: textDecorationColor || color,
            lineHeight: lineHeight ? lineHeight * fontSize : null,
            yOffset: position === 'relative' ? -top || bottom || 0 : null,
          },
        });
      } else {
        if (child.engine) {
          fragments.push(...child.engine.attributedString);
        }
      }
    });

    return fragments;
  }

  lineIndexAtHeight(height) {
    let counter = 0;
    for (let i = 0; i < this.lines.length; i++) {
      const line = this.lines[i];

      if (counter + line.height > height) {
        return i;
      }

      counter += line.height;
    }

    return this.lines.length;
  }

  splice(height) {
    const result = this.clone();
    const index = this.lineIndexAtHeight(height);

    result.start = index;
    result.end = this.end;
    this.end = index;

    return result;
  }

  clone() {
    const result = new TextEngine(this.element);
    result.computed = this.computed;
    result._container = this._container;
    return result;
  }

  transformText(text, transformation) {
    switch (transformation) {
      case 'uppercase':
        return text.toUpperCase();
      case 'lowercase':
        return text.toLowerCase();
      case 'capitalize':
        return upperFirst(text);
      default:
        return text;
    }
  }

  layout(width, dirty) {
    if (this.computed) return;
    const path = new Path().rect(0, 0, width, INFINITY);
    const container = new Container(path);
    const string = AttributedString.fromFragments(this.attributedString);

    // Do the actual text layout
    this.layoutEngine.layout(string, [container]);

    // Get the total amount of rendered lines
    const linesCount = container.blocks.reduce(
      (acc, block) => acc + block.lines.length,
      0,
    );

    this.computed = true;
    this._container = container;
    this.end = linesCount + 1;
  }

  render() {
    const margin = this.element.margin;
    const padding = this.element.padding;
    const { top, left } = this.element.getAbsoluteLayout();

    // We translate lines based on Yoga container
    const initialX = this.lines[0] ? this.lines[0].rect.y : 0;

    this.lines.forEach(line => {
      line.rect.x += left + margin.left + padding.left;
      line.rect.y += top + margin.top + padding.top - initialX;
    });

    const renderer = new TextRenderer(this.element.root, {
      outlineLines: false,
    });
    renderer.render(this.container);
  }
}

export default TextEngine;
<commit_msg>Trim attributted string before layout<commit_after>import warning from 'fbjs/lib/warning';
import KPLineBreaker from '@react-pdf/linebreaker';
import FontSubstitutionEngine from '@react-pdf/font-substitution';
import {
  Path,
  LayoutEngine,
  AttributedString,
  Container,
  TextRenderer,
  JustificationEngine,
} from '@react-pdf/textkit';
import upperFirst from 'lodash.upperfirst';
import Font from '../font';

// Global layout engine
// It's created dynamically because it may accept a custom hyphenation callback
let LAYOUT_ENGINE;
const INFINITY = 99999;

class TextEngine {
  constructor(element) {
    this.element = element;
    this._container = null;
    this.start = 0;
    this.end = 0;
    this.computed = false;
  }

  get container() {
    const lines = this._container.blocks.reduce(
      (acc, block) => [...acc, ...block.lines],
      [],
    );

    return {
      ...this._container,
      blocks: [{ lines: lines.splice(this.start, this.end) }],
    };
  }

  get layoutEngine() {
    if (!LAYOUT_ENGINE) {
      const shrinkWhitespaceFactor = { before: -0.5, after: -0.5 };

      LAYOUT_ENGINE = new LayoutEngine({
        lineBreaker: new KPLineBreaker(Font.getHyphenationCallback()),
        fontSubstitutionEngine: new FontSubstitutionEngine(),
        justificationEngine: new JustificationEngine({
          shrinkWhitespaceFactor,
        }),
      });
    }

    return LAYOUT_ENGINE;
  }

  get lines() {
    if (!this.container) {
      return [];
    }

    return this.container.blocks.reduce(
      (acc, block) => [...acc, ...block.lines],
      [],
    );
  }

  get height() {
    if (!this._container) {
      return -1;
    }

    return this.lines.reduce((acc, line) => acc + line.height, 0);
  }

  get width() {
    if (!this._container) {
      return -1;
    }

    return Math.max(...this.lines.map(line => line.advanceWidth));
  }

  get attributedString() {
    const fragments = [];
    const {
      color = 'black',
      fontFamily = 'Helvetica',
      fontSize = 18,
      textAlign = 'left',
      position,
      top,
      bottom,
      align,
      lineHeight,
      textDecoration,
      textDecorationColor,
      textDecorationStyle,
      textTransform,
    } = this.element.getComputedStyles();

    warning(
      !align,
      '"align" style prop will be deprecated on future versions. Please use "textAlign" instead in Text node',
    );

    this.element.children.forEach(child => {
      if (typeof child === 'string') {
        const obj = Font.getFont(fontFamily);
        const font = obj ? obj.data : fontFamily;
        const string = this.transformText(child, textTransform);

        fragments.push({
          string,
          attributes: {
            font,
            color,
            fontSize,
            align: textAlign,
            link: this.element.src,
            underlineStyle: textDecorationStyle,
            underline: textDecoration === 'underline',
            underlineColor: textDecorationColor || color,
            lineHeight: lineHeight ? lineHeight * fontSize : null,
            yOffset: position === 'relative' ? -top || bottom || 0 : null,
          },
        });
      } else {
        if (child.engine) {
          fragments.push(...child.engine.attributedString);
        }
      }
    });

    return fragments;
  }

  lineIndexAtHeight(height) {
    let counter = 0;
    for (let i = 0; i < this.lines.length; i++) {
      const line = this.lines[i];

      if (counter + line.height > height) {
        return i;
      }

      counter += line.height;
    }

    return this.lines.length;
  }

  splice(height) {
    const result = this.clone();
    const index = this.lineIndexAtHeight(height);

    result.start = index;
    result.end = this.end;
    this.end = index;

    return result;
  }

  clone() {
    const result = new TextEngine(this.element);
    result.computed = this.computed;
    result._container = this._container;
    return result;
  }

  transformText(text, transformation) {
    switch (transformation) {
      case 'uppercase':
        return text.toUpperCase();
      case 'lowercase':
        return text.toLowerCase();
      case 'capitalize':
        return upperFirst(text);
      default:
        return text;
    }
  }

  layout(width, dirty) {
    if (this.computed) return;
    const path = new Path().rect(0, 0, width, INFINITY);
    const container = new Container(path);
    const string = AttributedString.fromFragments(this.attributedString).trim();

    // Do the actual text layout
    this.layoutEngine.layout(string, [container]);

    // Get the total amount of rendered lines
    const linesCount = container.blocks.reduce(
      (acc, block) => acc + block.lines.length,
      0,
    );

    this.computed = true;
    this._container = container;
    this.end = linesCount + 1;
  }

  render() {
    const margin = this.element.margin;
    const padding = this.element.padding;
    const { top, left } = this.element.getAbsoluteLayout();

    // We translate lines based on Yoga container
    const initialX = this.lines[0] ? this.lines[0].rect.y : 0;

    this.lines.forEach(line => {
      line.rect.x += left + margin.left + padding.left;
      line.rect.y += top + margin.top + padding.top - initialX;
    });

    const renderer = new TextRenderer(this.element.root, {
      outlineLines: false,
    });
    renderer.render(this.container);
  }
}

export default TextEngine;
<|endoftext|>


<commit_before><commit_msg>add naviation steps between tabs to browser history<commit_after><|endoftext|>


<commit_before>import Ember from 'ember';
import flatten from './utils/flatten';

function isRelationshipManaged(relationship){
    return relationship.relationshipMeta.options.managed;
}

export default Ember.Mixin.create({
    /* Public API changes */
    isDirty: Ember.computed('hasDirtyAttributes', '_hasDirtyRelationships', function(){
        return this.get('hasDirtyAttributes') || this.get('_hasDirtyRelationships');
    }),

    rollback(){
        if (this.get('hasDirtyAttributes')){
            this.rollbackAttributes();
        }
        if (this.get('_hasDirtyRelationships')){
            this._rollbackRelationships();
        }
    },

    save(...args){
        return this._super(...args).then( savedModel => {
            savedModel._commitModelAndManagedRelationships();
            return savedModel;
        })
    },

    /* private API */

    _commitModelAndManagedRelationships(commitThisModel = false){
        if (commitThisModel){
        	this._internalModel.adapterWillCommit();
            this._internalModel.adapterDidCommit();
        }

        this.eachRelationship((name, relationship) => {
            if (relationship.options.managed){
                if (relationship.kind == 'hasMany'){
                    this._commitManagedHasMany(name);
                } else if (relationship.kind =='belongsTo'){
                    this._commitManagedBelongsTo(name);

                } else {
                    throw new Error(`this relationship type is not supported ${relationship.kind}`);
                }
            }
        });
    },

    _commitManagedBelongsTo(name){
        let currentValue = this.get(name).content;
        debugger;
        if (currentValue && currentValue._commitModelAndManagedRelationships){
            currentValue._commitModelAndManagedRelationships(true);
        }
    },

    _commitManagedHasMany(name){
        let currentValue = this.get(name).content;
        if (currentValue){
            currentValue.toArray().forEach((model) => {
                if (model && model._commitModelAndManagedRelationships){
                    model._commitModelAndManagedRelationships(true)
                }
            });
        }

    },

    _rollbackRelationships(){
        this.eachRelationship((name, relationship) => {
            if (relationship.options.referenced  || relationship.options.managed){
                if (relationship.kind == 'hasMany'){
                    this._rollbackHasManyRelation(name);
                } else if (relationship.kind =='belongsTo'){
                    this._rollbackBelongsToRelation(name);

                } else {
                    throw new Error(`this relationship type is not supported ${relationship.kind}`);
                }
            }
        });
    },

    _rollbackBelongsToRelation(name){
        let relationship = this.belongsTo(name).belongsToRelationship;
        if (relationship.hasLoaded && this._isBelongsToDirty(relationship)){
            let originalValue = null;
            if (relationship.canonicalState){
                originalValue = this.store.peekRecord(relationship.canonicalState.modelName, relationship.canonicalState.id);
            }

            let currentValue = this.get(name).content;
            this.set(name, originalValue);

            if (originalValue && isRelationshipManaged(relationship) && originalValue.get('isDirty')){
                originalValue.rollback();
            }
        }
    },

    _rollbackHasManyRelation(name){
        let relationship = this.hasMany(name).hasManyRelationship;

        let currentValue = Ember.A(this.get(relationship.key).content);

        currentValue.clear();

        if (relationship.isPolymorphic){
            let originalValueIdsAndTypes = Ember.A(relationship.canonicalState.map(m => {
                return { id: m.id, type:m.modelName }
            }));
            originalValueIdsAndTypes.forEach((e) => {
                currentValue.addObject(this.store.peekRecord(e.type, e.id));
            })
        } else {
            let originalValueIds = Ember.A(relationship.canonicalState.map(m => m.id));

            currentValue.addObjects(this.store.peekAll(relationship.belongsToType).filter(model => {
                return originalValueIds.contains(model.id)
            }));
        }

        if (isRelationshipManaged(relationship)){
            currentValue.forEach( model => {
                if (model.get('isDirty')){
                    model.rollback();
                }
            })
        }
    },

    _defineRelationshipComputedProperty: Ember.on('init', function(){
        const related = Ember.A();

        this.eachRelationship((name, relationship) => {
            if (relationship.options.referenced || relationship.options.managed){
                if (!relationship.options.async){
                    throw new Error('Managed relationships mixin works only with async relationships');
                }

                let relationshipData = {
                    name: name,
                    kind: relationship.kind,
                    managed: Boolean(relationship.options.managed)
                }

                if (relationship.kind == 'hasMany'){
                    relationshipData.keys = [`${name}.[]`];

                    if (relationshipData.managed){
                        relationshipData.keys.push(`${name}.@each.isDirty`);
                    }

                } else if (relationship.kind =='belongsTo'){
                    relationshipData.keys = [name];

                    if (relationshipData.managed){
                        relationshipData.keys.push(`${name}.isDirty`);
                    }

                } else {
                    throw new Error(`this relationship type is not supported ${relationship.kind}`);
                }
                related.addObject(relationshipData);
            }
        });

        if (!Ember.isEmpty(related)){
            Ember.defineProperty(this, '_hasDirtyRelationships', Ember.computed.apply(null, [...flatten(related.mapBy('keys')), function(){
                return this._isAtLeastOneBelongsToDirty(related) || this._isAtLeastOneHasManyDirty(related);
            }]));
        }
    }),

    _isAtLeastOneBelongsToDirty(relationships){
        let belongsTos = relationships.filterBy('kind', 'belongsTo');
        let belongsToRelations = belongsTos.map((r) => this.belongsTo(r.name).belongsToRelationship);

        //We filter out the ones which are not loaded yet, because there cannot be a change from the user side
        let belongsToLoadedRelations = Ember.A(belongsToRelations).filterBy('hasLoaded', true);

        return Ember.A(belongsToLoadedRelations).any((relation) => this._isBelongsToDirty(relation));
    },

    _isAtLeastOneHasManyDirty(relationships){
        let hasManys = relationships.filterBy('kind', 'hasMany');
        let hasManyRelations = hasManys.map((r) => this.hasMany(r.name).hasManyRelationship);

        //We filter out the ones which are not loaded yet, because there cannot be a change from the user side
        let hasManyLoadedRelations = Ember.A(hasManyRelations).filterBy('hasLoaded', true);

        return Ember.A(hasManyLoadedRelations).any((relation) => this._isHasManyDirty(relation));

    },

    _isBelongsToDirty(belongsToRelationship){
        //Working just on async relations
        let currentValue = this.get(belongsToRelationship.key).content;

        function isReferenceChanged(){
            if (belongsToRelationship.canonicalState){
                return !currentValue || belongsToRelationship.canonicalState.id !== currentValue.get('id') ;
            } else {
                return Boolean(currentValue);
            }
        }

        function isTheManegedEntityDirty(){
            if (!currentValue){
                return false;
            }
            return currentValue.get('isDirty');
        }

        return isRelationshipManaged(belongsToRelationship) ? isReferenceChanged() || isTheManegedEntityDirty() : isReferenceChanged();
    },

    _isHasManyDirty(hasManyRelationship){
        //Working just on async relations
        let currentValue = this.get(hasManyRelationship.key).content;

        function isReferenceChanged(){
            return hasManyRelationship.canonicalState.length !== currentValue.length || Ember.A(hasManyRelationship.canonicalState).any((e, i) => e === currentValue[i]);
        }

        function isTheManegedEntityDirty(){
            return currentValue.any(model => model.get('isDirty') );
        }
        return isRelationshipManaged(hasManyRelationship) ? isReferenceChanged() || isTheManegedEntityDirty() : isReferenceChanged();

    },
});
<commit_msg>Add support for polymorphic has many records<commit_after>import Ember from 'ember';
import flatten from './utils/flatten';

function isRelationshipManaged(relationship){
    return relationship.relationshipMeta.options.managed;
}

export default Ember.Mixin.create({
    /* Public API changes */
    isDirty: Ember.computed('hasDirtyAttributes', '_hasDirtyRelationships', function(){
        return this.get('hasDirtyAttributes') || this.get('_hasDirtyRelationships');
    }),

    rollback(){
        if (this.get('hasDirtyAttributes')){
            this.rollbackAttributes();
        }
        if (this.get('_hasDirtyRelationships')){
            this._rollbackRelationships();
        }
    },

    save(...args){
        return this._super(...args).then( savedModel => {
            savedModel._commitModelAndManagedRelationships();
            return savedModel;
        })
    },

    /* private API */

    _commitModelAndManagedRelationships(commitThisModel = false){
        if (commitThisModel){
        	this._internalModel.adapterWillCommit();
            this._internalModel.adapterDidCommit();
        }

        this.eachRelationship((name, relationship) => {
            if (relationship.options.managed){
                if (relationship.kind == 'hasMany'){
                    this._commitManagedHasMany(name);
                } else if (relationship.kind =='belongsTo'){
                    this._commitManagedBelongsTo(name);

                } else {
                    throw new Error(`this relationship type is not supported ${relationship.kind}`);
                }
            }
        });
    },

    _commitManagedBelongsTo(name){
        let currentValue = this.get(name).content;
        if (currentValue && currentValue._commitModelAndManagedRelationships){
            currentValue._commitModelAndManagedRelationships(true);
        }
    },

    _commitManagedHasMany(name){
        let currentValue = this.get(name).content;
        if (currentValue){
            currentValue.toArray().forEach((model) => {
                if (model && model._commitModelAndManagedRelationships){
                    model._commitModelAndManagedRelationships(true)
                }
            });
        }

    },

    _rollbackRelationships(){
        this.eachRelationship((name, relationship) => {
            if (relationship.options.referenced  || relationship.options.managed){
                if (relationship.kind == 'hasMany'){
                    this._rollbackHasManyRelation(name);
                } else if (relationship.kind =='belongsTo'){
                    this._rollbackBelongsToRelation(name);

                } else {
                    throw new Error(`this relationship type is not supported ${relationship.kind}`);
                }
            }
        });
    },

    _rollbackBelongsToRelation(name){
        let relationship = this.belongsTo(name).belongsToRelationship;
        if (relationship.hasLoaded && this._isBelongsToDirty(relationship)){
            let originalValue = null;
            if (relationship.canonicalState){
                originalValue = this.store.peekRecord(relationship.canonicalState.modelName, relationship.canonicalState.id);
            }

            let currentValue = this.get(name).content;
            this.set(name, originalValue);

            if (originalValue && isRelationshipManaged(relationship) && originalValue.get('isDirty')){
                originalValue.rollback();
            }
        }
    },

    _rollbackHasManyRelation(name){
        let relationship = this.hasMany(name).hasManyRelationship;

        let currentValue = Ember.A(this.get(relationship.key).content);

        currentValue.clear();

        if (relationship.isPolymorphic){
            let originalValueIdsAndTypes = Ember.A(relationship.canonicalState.map(m => {
                return { id: m.id, type:m.modelName }
            }));
            originalValueIdsAndTypes.forEach((e) => {
                currentValue.addObject(this.store.peekRecord(e.type, e.id));
            })
        } else {
            let originalValueIds = Ember.A(relationship.canonicalState.map(m => m.id));

            currentValue.addObjects(this.store.peekAll(relationship.belongsToType).filter(model => {
                return originalValueIds.contains(model.id)
            }));
        }

        if (isRelationshipManaged(relationship)){
            currentValue.forEach( model => {
                if (model.get('isDirty')){
                    model.rollback();
                }
            })
        }
    },

    _defineRelationshipComputedProperty: Ember.on('init', function(){
        const related = Ember.A();

        this.eachRelationship((name, relationship) => {
            if (relationship.options.referenced || relationship.options.managed){
                if (!relationship.options.async){
                    throw new Error('Managed relationships mixin works only with async relationships');
                }

                let relationshipData = {
                    name: name,
                    kind: relationship.kind,
                    managed: Boolean(relationship.options.managed)
                }

                if (relationship.kind == 'hasMany'){
                    relationshipData.keys = [`${name}.[]`];

                    if (relationshipData.managed){
                        relationshipData.keys.push(`${name}.@each.isDirty`);
                    }

                } else if (relationship.kind =='belongsTo'){
                    relationshipData.keys = [name];

                    if (relationshipData.managed){
                        relationshipData.keys.push(`${name}.isDirty`);
                    }

                } else {
                    throw new Error(`this relationship type is not supported ${relationship.kind}`);
                }
                related.addObject(relationshipData);
            }
        });

        if (!Ember.isEmpty(related)){
            Ember.defineProperty(this, '_hasDirtyRelationships', Ember.computed.apply(null, [...flatten(related.mapBy('keys')), function(){
                return this._isAtLeastOneBelongsToDirty(related) || this._isAtLeastOneHasManyDirty(related);
            }]));
        }
    }),

    _isAtLeastOneBelongsToDirty(relationships){
        let belongsTos = relationships.filterBy('kind', 'belongsTo');
        let belongsToRelations = belongsTos.map((r) => this.belongsTo(r.name).belongsToRelationship);

        //We filter out the ones which are not loaded yet, because there cannot be a change from the user side
        let belongsToLoadedRelations = Ember.A(belongsToRelations).filterBy('hasLoaded', true);

        return Ember.A(belongsToLoadedRelations).any((relation) => this._isBelongsToDirty(relation));
    },

    _isAtLeastOneHasManyDirty(relationships){
        let hasManys = relationships.filterBy('kind', 'hasMany');
        let hasManyRelations = hasManys.map((r) => this.hasMany(r.name).hasManyRelationship);

        //We filter out the ones which are not loaded yet, because there cannot be a change from the user side
        let hasManyLoadedRelations = Ember.A(hasManyRelations).filterBy('hasLoaded', true);

        return Ember.A(hasManyLoadedRelations).any((relation) => this._isHasManyDirty(relation));

    },

    _isBelongsToDirty(belongsToRelationship){
        //Working just on async relations
        let currentValue = this.get(belongsToRelationship.key).content;

        function isReferenceChanged(){
            if (belongsToRelationship.canonicalState){
                return !currentValue || belongsToRelationship.canonicalState.id !== currentValue.get('id') ;
            } else {
                return Boolean(currentValue);
            }
        }

        function isTheManegedEntityDirty(){
            if (!currentValue){
                return false;
            }
            return currentValue.get('isDirty');
        }

        return isRelationshipManaged(belongsToRelationship) ? isReferenceChanged() || isTheManegedEntityDirty() : isReferenceChanged();
    },

    _isHasManyDirty(hasManyRelationship){
        //Working just on async relations
        let currentValue = this.get(hasManyRelationship.key).content;

        function isReferenceChanged(){
            return hasManyRelationship.canonicalState.length !== currentValue.length || Ember.A(hasManyRelationship.canonicalState).any((e, i) => e === currentValue[i]);
        }

        function isTheManegedEntityDirty(){
            return currentValue.any(model => model.get('isDirty') );
        }
        return isRelationshipManaged(hasManyRelationship) ? isReferenceChanged() || isTheManegedEntityDirty() : isReferenceChanged();

    },
});
<|endoftext|>


<commit_before>;(function ($, undefined) {
    "use strict";

    // Dependencies
    if ($ === undefined) {
        throw "jQuery should be defined to use jagger";
    }

    var pluginName = "jagger",
        prefix      = pluginName + "-",
        classPrefix = "." + prefix;

    var selectors = {
        taggeable:         "img",
        pin:               classPrefix + "pin",
        container:         classPrefix + "pin-template-container",
        onHover:           classPrefix + "pin-on-hover",
        templateContainer: classPrefix + "template-container",
        templateLocation:  ""
    };

    var removeDot = function(text) {
        return text.replace(/^\./, "");
    };

    // jagger defaults
    var defaults = {
        selectors: selectors,
        template: "#" + prefix + "template",
        leaveTemplatesOpen: false,
        showPreviousElementsOnHover: false,
        pinElement: function(instance) {
            return "";
        }
    };

    /* ==============================================
        Jagger Class
       ============================================== */

    function Jagger( element, options ) {
        // Set the default options
        this.options = $.extend({}, defaults, options);

        // Handy shortcut
        this.selectors = $.extend({}, defaults.selectors, this.options.selectors);

        // Container jquery element
        this.$el = $(element);

        this.addHandlers();

        return this;
    }

    Jagger.prototype = {
        addHandlers: function() {
            if(this.options.forceBoth) {
                this._onHover();
                this._onClick();
            } else if(this.options.showPreviousElementsOnHover) {
                this._onHover();
            } else {
                this._onClick();
            }
            return this;
        },
        _onHover: function() {
            var selectors = this.selectors;

            $(selectors.container).on("mouseenter.jagger", function() {
                $(this).find(selectors.onHover).fadeIn("fast");
            }).on("mouseleave.jagger", function() {
                $(this).find(selectors.onHover).stop().fadeOut('fast');
            });
        },
        _onClick: function() {
            var self = this;
            var selectors = this.selectors;

            this.$el.children(selectors.taggeable).on("click.jagger", function(event) {
                var $container = self.getContainer();
                var $template  = self.getTemplate();
                var $pin       = self.getPin();

                // Mouse position onclick
                var mouseCoords = {
                    x: event.clientX,
                    y: event.clientY
                };

                var pinPosition      = self.determinePinPosition(mouseCoords);
                var templatePosition = self.determineTemplatePosition(pinPosition);

                if(!self.options.leaveTemplatesOpen) {
                    // Close previously opened templates
                    $(selectors.templateLocation).find(selectors.templateContainer).hide();
                    self.$el.find(selectors.templateContainer).hide();
                }

                // Add the css and save a reference for later use
                $pin.css(pinPosition).data("template", $template);
                $template.css(templatePosition).data("pin", $pin);

                // if a template location exists use it to append the template there, otherwise use the container
                if(selectors.templateLocation) {
                    $(selectors.templateLocation).append($template);
                } else {
                    $container.append($template);
                }

                // Append the pin and append the conainer to the DOM
                $container.append($pin).appendTo(self.$el);

                self.$el.trigger("jagger:elementsAdded", [ pinPosition, templatePosition ]);
            });
            return this;
        },
        getContainer: function() {
            var pinTemplateContainer = document.createElement("div");
            pinTemplateContainer.className = removeDot(this.selectors.container);

            return $(pinTemplateContainer);
        },
        getPin: function() {
            var pin = this.options.pinElement;

            if($.isFunction(pin)) {
                pin = pin(this);
            }
            pin = pin || defaults.pinElement;

            var $pin = (typeof pin !== "string" && "jquery" in pin) ? pin : $(pin);

            return this._setPinHandlers( $pin );
        },
        getTemplate: function() {
            var templateContainer = document.createElement("div");

            templateContainer.className = removeDot(this.selectors.templateContainer);
            templateContainer.innerHTML = $(this.options.template).html();

            return this._setTemplateHandlers( $(templateContainer) );
        },
        determinePinPosition: function(mouseCoords) {
            var elCoords = this.getElOffset();
            return {
                position: "absolute",
                top : mouseCoords.y - elCoords.y,
                left: mouseCoords.x - elCoords.x
            };
        },
        determineTemplatePosition: function(pinPosition) {
            //Fixed for now
            return {
                position: "absolute",
                top:  pinPosition.top - 10,
                left: pinPosition.left + 30
            };
        },
        getElOffset: function() {
            var $window  = $(window);
            var elOffset = this.$el.offset();
            return {
                x: elOffset.left - $window.scrollLeft(),
                y: elOffset.top  - $window.scrollTop()
            };
        },
        _setPinHandlers: function($pin) {
            var $el = this.$el;
            var selectors = this.selectors;

            return $pin.on("click.jagger", function() {
                $el.find(selectors.templateContainer).hide();
                $(selectors.templateLocation).find(selectors.templateContainer).hide();

                $pin.data("template").show();
                return false;
            });
        },
        _setTemplateHandlers: function($template) {
            var selectors = this.selectors;

            return $template.on("jagger:deleteTemplate", function() {
                // Remove the pin
                $template.data("pin").parents(selectors.container).remove();

                if(selectors.templateLocation) {
                    $template.remove();
                }
            });
        },
        remove: function() {
            this.$el.removeData(pluginName);

            this.$el.children(this.selectors.taggeable).off(".jagger");
            $(this.selectors.templateContainer).off("jagger:deleteTemplate");
            $(this.selectors.pin + ", " + this.selectors.container).off(".jagger");

            return this.$el;
        }
    };

    /* ==============================================
        jQuery Plugin initialization
       ============================================== */

    $.fn[pluginName] = function ( options ) {
        var isMethod = typeof options === 'string';
        if(isMethod) {
            var methodArguments = Array.prototype.slice.call(arguments, 1);
        }

        return this.each(function() {
            // When the first argument is a string, call a method on the instance with that string as the method name
            // Otherwise instantiate the plugin
            if (isMethod) {
                var instance = $.data(this, pluginName);
                if (!instance) {
                    throw "Method called on jagger before instantiation ";
                }
                if ( !$.isFunction(instance[options]) ) {
                    throw "The method: " + options + " was not found in jagger";
                }

                instance[options].apply(instance, methodArguments);
            } else {
                // Create only one instance
                if ( !$.data(this, pluginName) ) {
                   $.data(this, pluginName, new Jagger( this, options ));
                }
            }
        });
    };

})( jQuery );<commit_msg>Methods to toggle between the hover and tagging states<commit_after>;(function ($, undefined) {
    "use strict";

    // Dependencies
    if ($ === undefined) {
        throw "jQuery should be defined to use jagger";
    }

    var pluginName = "jagger",
        prefix      = pluginName + "-",
        classPrefix = "." + prefix;

    var selectors = {
        taggeable:         "img",
        pin:               classPrefix + "pin",
        container:         classPrefix + "pin-template-container",
        onHover:           classPrefix + "pin-on-hover",
        templateContainer: classPrefix + "template-container",
        templateLocation:  ""
    };

    var removeDot = function(text) {
        return text.replace(/^\./, "");
    };

    // jagger defaults
    var defaults = {
        selectors: selectors,
        template: "#" + prefix + "template",
        leaveTemplatesOpen: false,
        showPreviousElementsOnHover: false,
        pinElement: function(instance) {
            return "";
        }
    };

    /* ==============================================
        Jagger Class
       ============================================== */

    function Jagger( element, options ) {
        // Set the default options
        this.options = $.extend({}, defaults, options);

        // Handy shortcut
        this.selectors = $.extend({}, defaults.selectors, this.options.selectors);

        // Container jquery element
        this.$el = $(element);

        this.addHandlers();

        return this;
    }

    Jagger.prototype = {
        addHandlers: function() {
            if(this.options.forceBoth) {
                this._onHover();
                this._onClick();
            } else if(this.options.showPreviousElementsOnHover) {
                this._onHover();
            } else {
                this._onClick();
            }
            return this;
        },
        _onHover: function() {
            var selectors = this.selectors;

            $(selectors.container).on("mouseenter.jagger", function() {
                $(this).find(selectors.onHover).fadeIn("fast");
            }).on("mouseleave.jagger", function() {
                $(this).find(selectors.onHover).stop().fadeOut('fast');
            });
        },
        _onClick: function() {
            var self = this;
            var selectors = this.selectors;

            this.$el.children(selectors.taggeable).on("click.jagger", function(event) {
                var $container = self.getContainer();
                var $template  = self.getTemplate();
                var $pin       = self.getPin();

                // Mouse position onclick
                var mouseCoords = {
                    x: event.clientX,
                    y: event.clientY
                };

                var pinPosition      = self.determinePinPosition(mouseCoords);
                var templatePosition = self.determineTemplatePosition(pinPosition);

                if(!self.options.leaveTemplatesOpen) {
                    self.hideTemplates();
                }

                // Add the css and save a reference for later use
                $pin.css(pinPosition).data("template", $template);
                $template.css(templatePosition).data("pin", $pin);

                // if a template location exists use it to append the template there, otherwise use the container
                if(selectors.templateLocation) {
                    $(selectors.templateLocation).append($template);
                } else {
                    $container.append($template);
                }

                // Append the pin and append the conainer to the DOM
                $container.append($pin).appendTo(self.$el);

                self.$el.trigger("jagger:elementsAdded", [ pinPosition, templatePosition ]);
            });
            return this;
        },
        getContainer: function() {
            var pinTemplateContainer = document.createElement("div");
            pinTemplateContainer.className = removeDot(this.selectors.container);

            return $(pinTemplateContainer);
        },
        getPin: function() {
            var pin = this.options.pinElement;

            if($.isFunction(pin)) {
                pin = pin(this);
            }
            pin = pin || defaults.pinElement;

            var $pin = (typeof pin !== "string" && "jquery" in pin) ? pin : $(pin);

            return this.setPinHandlers( $pin );
        },
        getTemplate: function() {
            var templateContainer = document.createElement("div");

            templateContainer.className = removeDot(this.selectors.templateContainer);
            templateContainer.innerHTML = $(this.options.template).html();

            return this.setTemplateHandlers( $(templateContainer) );
        },
        determinePinPosition: function(mouseCoords) {
            var elCoords = this.getElOffset();
            return {
                position: "absolute",
                top : mouseCoords.y - elCoords.y,
                left: mouseCoords.x - elCoords.x
            };
        },
        determineTemplatePosition: function(pinPosition) {
            //Fixed for now
            return {
                position: "absolute",
                top:  pinPosition.top - 10,
                left: pinPosition.left + 30
            };
        },
        getElOffset: function() {
            var $window  = $(window);
            var elOffset = this.$el.offset();
            return {
                x: elOffset.left - $window.scrollLeft(),
                y: elOffset.top  - $window.scrollTop()
            };
        },
        setPinHandlers: function($pin) {
            var self = this;

            return $pin.on("click.jagger", function() {
                self.hideTemplates();

                $(this).data("template").show();
                return false;
            });
        },
        setTemplateHandlers: function($template) {
            var selectors = this.selectors;

            return $template.on("jagger:deleteTemplate", function() {
                var $this = $(this);

                // Remove the pin
                $this.data("pin").parents(selectors.container).remove();

                if(selectors.templateLocation) {
                    $this.remove();
                }
            });
        },
        hideTemplates: function() {
            var selectors = this.selectors;

            // Close every template
            $(selectors.templateLocation).find(selectors.templateContainer).hide();
            this.$el.find(selectors.templateContainer).hide();
        },
        remove: function() {
            this.$el.removeData(pluginName);

            this.removeEvents();

            return this.$el;
        },
        removeEvents: function() {
            this.$el.children(this.selectors.taggeable).off(".jagger");
            $(this.selectors.templateContainer).off("jagger:deleteTemplate");
            $(this.selectors.pin + ", " + this.selectors.container).off(".jagger");
        },
        onlyTagging: function() {
            this.removeEvents();

            this.setPinHandlers( $(this.selectors.pin) );
            this.setTemplateHandlers( $(this.selectors.templateContainer) );

            this._onClick();
        },
        onlyHover: function() {
            this.removeEvents();
            this.hideTemplates();
            this._onHover();
        }
    };

    /* ==============================================
        jQuery Plugin initialization
       ============================================== */

    $.fn[pluginName] = function ( options ) {
        var isMethod = typeof options === 'string';
        if(isMethod) {
            var methodArguments = Array.prototype.slice.call(arguments, 1);
        }

        return this.each(function() {
            // When the first argument is a string, call a method on the instance with that string as the method name
            // Otherwise instantiate the plugin
            if (isMethod) {
                var instance = $.data(this, pluginName);
                if (!instance) {
                    throw "Method called on jagger before instantiation ";
                }
                if ( !$.isFunction(instance[options]) ) {
                    throw "The method: " + options + " was not found in jagger";
                }

                instance[options].apply(instance, methodArguments);
            } else {
                // Create only one instance
                if ( !$.data(this, pluginName) ) {
                   $.data(this, pluginName, new Jagger( this, options ));
                }
            }
        });
    };

})( jQuery );<|endoftext|>


<commit_before>var lumbarLoader = exports.loader = {
  loadPrefix: lumbarLoadPrefix || '',

  loadModule: function(moduleName, callback) {
    var loadCount = 0,
        expected = 1,
        allInit = false;
    function complete() {
      loadCount++;
      if (allInit && loadCount >= expected) {
        callback();
      }
    }

    expected += lumbarLoader.loadCSS(moduleName, complete);
    expected += lumbarLoader.loadJS(moduleName, complete);

    // If everything was done sync then fire away
    allInit = true;
    complete();
  },

  loadInlineCSS: function(content) {
    var style = document.createElement('style');
    style.textContent = content;
    document.body.appendChild(style);
    return style;
  }
};

function loadResources(moduleName, field, attr, create) {
  var module = moduleName === 'base' ? lumbarLoader.map.base : lumbarLoader.modules[moduleName], // Special case for the base case
      field = module[field] || [],
      loaded = [];

  if (Array.isArray ? !Array.isArray(field) :field.toString() !== '[object Array]') {
    field = [field];
  }
  for (var i = 0, len = field.length; i < len; i++) {
    var object = field[i];
    var href = checkLoadResource(object, attr);
    if (href) {
      var el = create(href);
      if (el && el.nodeType === 1) {
        document.body.appendChild(el);
      }
      loaded.push(el);
    }
  }
  return loaded;
}

var devicePixelRatio = window.devicePixelRatio || 1;
function checkLoadResource(object, attr) {
  var href = lumbarLoader.loadPrefix + (object.href || object);
  if ((!object.maxRatio || devicePixelRatio < object.maxRatio) && (!object.minRatio || object.minRatio <= devicePixelRatio)) {
    if (document.querySelector('[' + attr + '="' + href + '"]')) {
      return;
    }

    return href;
  }
}

exports.moduleMap = function(map, loadPrefix) {
  lumbarLoader.map = map;
  lumbarLoader.modules = map.modules;
  lumbarLoader.loadPrefix = loadPrefix || lumbarLoader.loadPrefix;
};
<commit_msg>fix error when lumbarLoadPrefix is undefined<commit_after>var lumbarLoader = exports.loader = {
  loadPrefix: typeof lumbarLoadPrefix === 'undefined' ? '' : lumbarLoadPrefix,

  loadModule: function(moduleName, callback) {
    var loadCount = 0,
        expected = 1,
        allInit = false;
    function complete() {
      loadCount++;
      if (allInit && loadCount >= expected) {
        callback();
      }
    }

    expected += lumbarLoader.loadCSS(moduleName, complete);
    expected += lumbarLoader.loadJS(moduleName, complete);

    // If everything was done sync then fire away
    allInit = true;
    complete();
  },

  loadInlineCSS: function(content) {
    var style = document.createElement('style');
    style.textContent = content;
    document.body.appendChild(style);
    return style;
  }
};

function loadResources(moduleName, field, attr, create) {
  var module = moduleName === 'base' ? lumbarLoader.map.base : lumbarLoader.modules[moduleName], // Special case for the base case
      field = module[field] || [],
      loaded = [];

  if (Array.isArray ? !Array.isArray(field) :field.toString() !== '[object Array]') {
    field = [field];
  }
  for (var i = 0, len = field.length; i < len; i++) {
    var object = field[i];
    var href = checkLoadResource(object, attr);
    if (href) {
      var el = create(href);
      if (el && el.nodeType === 1) {
        document.body.appendChild(el);
      }
      loaded.push(el);
    }
  }
  return loaded;
}

var devicePixelRatio = window.devicePixelRatio || 1;
function checkLoadResource(object, attr) {
  var href = lumbarLoader.loadPrefix + (object.href || object);
  if ((!object.maxRatio || devicePixelRatio < object.maxRatio) && (!object.minRatio || object.minRatio <= devicePixelRatio)) {
    if (document.querySelector('[' + attr + '="' + href + '"]')) {
      return;
    }

    return href;
  }
}

exports.moduleMap = function(map, loadPrefix) {
  lumbarLoader.map = map;
  lumbarLoader.modules = map.modules;
  lumbarLoader.loadPrefix = loadPrefix || lumbarLoader.loadPrefix;
};
<|endoftext|>


<commit_before>#!/bin/sh
// 2>/dev/null; exec "$(command -v nodejs || command -v node)" "$0" "$@"

'use strict';

var EventSource = require('eventsource'),
    spawn   = require('child_process').spawn,
    request = require('request'),
    fs      = require('fs'),
    async   = require('async'),
    os      = require('os'),
    net     = require('net'),
    usage   = require('usage'),
    args    = require('./args')(),
    ip      = '127.0.0.1',
    port    = args.port,
    host    = 'http://' + ip + ':' + port,
    impFile = __dirname + '/../' + args.implementation + '/server',
    cmdArgs = ['--port', args.port],
    cmd     = impFile,
    pid;

if (!args.pid && !args.implementation && !args.cmd) {
    console.error('You must specify either --implementation, --cmd or --pid');
    process.exit(1);
} else if (args.implementation && !fs.existsSync(impFile)) {
    console.error('Implementation does not seem to exist (' + args.implementation + '/server)');
    process.exit(2);
} else if (args.cmd) {
    cmdArgs = args.cmd.split(' ');
    cmd = cmdArgs.shift();
}

if (!args.pid) {
    var sse = spawn(cmd, cmdArgs);
    sse.stdout.pipe(process.stdout);
    sse.stderr.pipe(process.stderr);
}

pid = args.pid || sse.pid;

setInterval(gatherStats, args.probeInterval);

function gatherStats() {
    async.parallel({
        systemInfo: getSystemInfo,
        networkTiming: getNetworkTiming,
        connectionCount: getConnectionCount
    }, function(err, results) {
        if (err) {
            return;
        }
        reportStats(results);
    });
}

function getSystemInfo(callback) {
    usage.lookup(pid, { keepHistory: true }, function(err, result) {
        if (err) {
            console.error('Failed to get system info', err);
            return callback(err);
        }

        result.loadAvg   = os.loadavg();
        result.hostname  = os.hostname();
        result.ssePort   = args.port;
        result.freeMem   = os.freemem();

        callback(null, result);
    });
}

function getNetworkTiming(callback) {
    var responseTime;
    var es = new EventSource(host + '/sse');
    var start = Date.now();
    var afterResponseTimeMeasured = function(error) {
      if (error) {
        callback(error);
        return;
      }
      fetchDeliveryTiming(function(deliveryTime) {
        callback(null, {
            responseTime: responseTime,
            deliveryTime: deliveryTime
        });
      });
    };
    es.onopen = function() {
        responseTime = Date.now() - start;
    };
    es.onerror = function(err) {
        console.error('Failed to request /sse when probing for response time', err);
        es.close();
        callback(err);
    };
    es.onmessage = function(msg) {
        es.close();
        afterResponseTimeMeasured(null, responseTime);
    };
}

var fetchDeliveryTiming = (function() {
    var deliveryTimingRunning = false;
    var cb = function() {};
    function getDeliveryTiming(callback) {
        cb = callback;
        if (deliveryTimingRunning) return;
        deliveryTimingRunning = true;
        var es = new EventSource(host + '/sse');
        var start = Date.now();
        var timeoutId;
        var scheduleBroadcast = function() {
            timeoutId = setTimeout(function() {
                var client = net.connect({host: ip, port: port}, function() {
                    client.setNoDelay(true);
                    var payload = ''+Date.now();
                    client.write('POST /broadcast HTTP/1.1\r\n' +
                                 'Host: ' + ip + '\r\n' +
                                 'Content-Type: text/plain\r\n' +
                                 'Content-Length: ' + payload.length + '\r\n' +
                                 '\r\n' +
                                 payload);
                });
            }, 1000);
        }

        es.onopen = function() {
            scheduleBroadcast();
        };
        es.onerror = function(err) {
            deliveryTimingRunning = false;
            console.error('Failed to request /sse when probing for delivery time', err);
            if (timeoutId != -1) {
              clearTimeout(timeoutId);
              timeoutId = -1;
            }
            es.close();
        };
        es.onmessage = function(msg) {
            cb(Date.now() - parseInt(msg.data, 10));
            scheduleBroadcast();
        };
    }
    return getDeliveryTiming;
})();

function getConnectionCount(callback) {
    request(host + '/connections', function(err, res, body) {
        callback(err, body | 0);
    });
}

function reportStats(stats) {
    stats.timestamp = Date.now();
    request({
        uri: args.endpoint,
        method: 'POST',
        json: stats
    }, onStatsReported);
}

function onStatsReported(err, res) {
    if (err || res.statusCode >= 400) {
        return console.error('Failed reporting statistics: ', err || 'HTTP ' + res.statusCode);
    }
}
<commit_msg>Revert "reporter callback fix"<commit_after>#!/bin/sh
// 2>/dev/null; exec "$(command -v nodejs || command -v node)" "$0" "$@"

'use strict';

var EventSource = require('eventsource'),
    spawn   = require('child_process').spawn,
    request = require('request'),
    fs      = require('fs'),
    async   = require('async'),
    os      = require('os'),
    net     = require('net'),
    usage   = require('usage'),
    args    = require('./args')(),
    ip      = '127.0.0.1',
    port    = args.port,
    host    = 'http://' + ip + ':' + port,
    impFile = __dirname + '/../' + args.implementation + '/server',
    cmdArgs = ['--port', args.port],
    cmd     = impFile,
    pid;

if (!args.pid && !args.implementation && !args.cmd) {
    console.error('You must specify either --implementation, --cmd or --pid');
    process.exit(1);
} else if (args.implementation && !fs.existsSync(impFile)) {
    console.error('Implementation does not seem to exist (' + args.implementation + '/server)');
    process.exit(2);
} else if (args.cmd) {
    cmdArgs = args.cmd.split(' ');
    cmd = cmdArgs.shift();
}

if (!args.pid) {
    var sse = spawn(cmd, cmdArgs);
    sse.stdout.pipe(process.stdout);
    sse.stderr.pipe(process.stderr);
}

pid = args.pid || sse.pid;

setInterval(gatherStats, args.probeInterval);

function gatherStats() {
    async.parallel({
        systemInfo: getSystemInfo,
        networkTiming: getNetworkTiming,
        connectionCount: getConnectionCount
    }, function(err, results) {
        if (err) {
            return;
        }
        reportStats(results);
    });
}

function getSystemInfo(callback) {
    usage.lookup(pid, { keepHistory: true }, function(err, result) {
        if (err) {
            console.error('Failed to get system info', err);
            return callback(err);
        }

        result.loadAvg   = os.loadavg();
        result.hostname  = os.hostname();
        result.ssePort   = args.port;
        result.freeMem   = os.freemem();

        callback(null, result);
    });
}

function getNetworkTiming(callback) {
    var responseTime;
    var es = new EventSource(host + '/sse');
    var start = Date.now();
    var afterResponseTimeMeasured = function(error) {
      if (error) {
        callback(error);
        return;
      }
      fetchDeliveryTiming(function(deliveryTime) {
        callback(null, {
            responseTime: responseTime,
            deliveryTime: deliveryTime
        });
      });
    };
    es.onopen = function() {
        responseTime = Date.now() - start;
    };
    es.onerror = function(err) {
        console.error('Failed to request /sse when probing for response time', err);
        es.close();
        callback(err);
    };
    es.onmessage = function(msg) {
        es.close();
        callback(null, responseTime);
    };
}

var fetchDeliveryTiming = (function() {
    var deliveryTimingRunning = false;
    var cb = function() {};
    function getDeliveryTiming(callback) {
        cb = callback;
        if (deliveryTimingRunning) return;
        deliveryTimingRunning = true;
        var es = new EventSource(host + '/sse');
        var start = Date.now();
        var timeoutId;
        var scheduleBroadcast = function() {
            timeoutId = setTimeout(function() {
                var client = net.connect({host: ip, port: port}, function() {
                    client.setNoDelay(true);
                    var payload = ''+Date.now();
                    client.write('POST /broadcast HTTP/1.1\r\n' +
                                 'Host: ' + ip + '\r\n' +
                                 'Content-Type: text/plain\r\n' +
                                 'Content-Length: ' + payload.length + '\r\n' +
                                 '\r\n' +
                                 payload);
                });
            }, 1000);
        }

        es.onopen = function() {
            scheduleBroadcast();
        };
        es.onerror = function(err) {
            deliveryTimingRunning = false;
            console.error('Failed to request /sse when probing for delivery time', err);
            if (timeoutId != -1) {
              clearTimeout(timeoutId);
              timeoutId = -1;
            }
            es.close();
        };
        es.onmessage = function(msg) {
            cb(Date.now() - parseInt(msg.data, 10));
            scheduleBroadcast();
        };
    }
    return getDeliveryTiming;
})();

function getConnectionCount(callback) {
    request(host + '/connections', function(err, res, body) {
        callback(err, body | 0);
    });
}

function reportStats(stats) {
    stats.timestamp = Date.now();
    request({
        uri: args.endpoint,
        method: 'POST',
        json: stats
    }, onStatsReported);
}

function onStatsReported(err, res) {
    if (err || res.statusCode >= 400) {
        return console.error('Failed reporting statistics: ', err || 'HTTP ' + res.statusCode);
    }
}
<|endoftext|>


<commit_before>import Service from '@ember/service';
import { inject as service } from '@ember/service';

export default Service.extend({
  currentUser: service(),
  permissionMatrix: service(),
  async canDoInSchool(school, capability) {
    const currentUser = await this.get('currentUser');
    const isRoot = await currentUser.get('isRoot');
    if (isRoot) {
      return true;
    }
    const permissionMatrix = this.get('permissionMatrix');
    const rolesInSchool = await currentUser.getRolesInSchool(school);

    return permissionMatrix.hasPermission(school, capability, rolesInSchool);
  },
  async canUpdateCourse(course) {
    const currentUser = await this.get('currentUser');
    const permissionMatrix = this.get('permissionMatrix');
    if (course.get('locked') || course.get('archived')) {
      return false;
    }
    const school = await course.get('school');

    if (await this.canDoInSchool(school, 'CAN_UPDATE_ALL_COURSES')) {
      return true;
    }

    const rolesInCourse = await currentUser.getRolesInCourse(course);
    return await permissionMatrix.hasPermission(school, 'CAN_UPDATE_THEIR_COURSES', rolesInCourse);
  },
  async canDeleteCourse(course) {
    const currentUser = await this.get('currentUser');
    const permissionMatrix = this.get('permissionMatrix');
    if (course.get('locked') || course.get('archived')) {
      return false;
    }

    const school = await course.get('school');
    if (await this.canDoInSchool(school, 'CAN_DELETE_ALL_COURSES')) {
      return true;
    }

    const rolesInCourse = await currentUser.getRolesInCourse(course);
    return await permissionMatrix.hasPermission(school, 'CAN_DELETE_THEIR_COURSES', rolesInCourse);
  },
  async canCreateCourse(school) {
    return this.canDoInSchool(school, 'CAN_CREATE_COURSES');
  },
  async canUnlockCourse(course) {
    const currentUser = await this.get('currentUser');
    const permissionMatrix = this.get('permissionMatrix');

    const school = await course.get('school');
    if (await this.canDoInSchool(school, 'CAN_UNLOCK_ALL_COURSES')) {
      return true;
    }

    const rolesInCourse = await currentUser.getRolesInCourse(course);
    return await permissionMatrix.hasPermission(school, 'CAN_UNLOCK_THEIR_COURSES', rolesInCourse);
  },
  async canUpdateSession(session) {
    const currentUser = await this.get('currentUser');
    const permissionMatrix = this.get('permissionMatrix');

    const course = await session.get('course');

    if (course.get('locked') || course.get('archived')) {
      return false;
    }

    const school = await course.get('school');
    if (await this.canDoInSchool(school, 'CAN_UPDATE_ALL_SESSIONS')) {
      return true;
    }

    const rolesInSession = await currentUser.getRolesInSession(session);
    if (await permissionMatrix.hasPermission(school, 'CAN_UPDATE_THEIR_SESSIONS', rolesInSession)) {
      return true;
    }

    return this.canUpdateCourse(course);
  },
  async canDeleteSession(session) {
    const currentUser = await this.get('currentUser');
    const permissionMatrix = this.get('permissionMatrix');

    const course = await session.get('course');

    if (course.get('locked') || course.get('archived')) {
      return false;
    }

    const school = await course.get('school');
    if (await this.canDoInSchool(school, 'CAN_DELETE_ALL_SESSIONS')) {
      return true;
    }

    const rolesInSession = await currentUser.getRolesInSession(session);
    if (await permissionMatrix.hasPermission(school, 'CAN_DELETE_THEIR_SESSIONS', rolesInSession)) {
      return true;
    }

    return this.canUpdateCourse(course);
  },
  async canCreateSession(course) {
    if (course.get('locked') || course.get('archived')) {
      return false;
    }

    const school = await course.get('school');
    if (await this.canDoInSchool(school, 'CAN_CREATE_SESSIONS')) {
      return true;
    }

    return this.canUpdateCourse(course);
  },
  async canUpdateSessionType(sessionType) {
    const school = await sessionType.get('school');
    return this.canDoInSchool(school, 'CAN_UPDATE_SESSION_TYPES');
  },
  async canDeleteSessionType(sessionType) {
    const school = await sessionType.get('school');
    return this.canDoInSchool(school, 'CAN_DELETE_SESSION_TYPES');
  },
  async canUpdateSessionTypeInSchool(school) {
    return this.canDoInSchool(school, 'CAN_UPDATE_SESSION_TYPES');
  },
  async canDeleteSessionTypeInSchool(school) {
    return this.canDoInSchool(school, 'CAN_DELETE_SESSION_TYPES');
  },
  async canCreateSessionType(school) {
    return this.canDoInSchool(school, 'CAN_CREATE_SESSION_TYPES');
  },
  async canUpdateDepartment(department) {
    const school = await department.get('school');
    return this.canDoInSchool(school, 'CAN_UPDATE_DEPARTMENTS');
  },
  async canDeleteDepartment(department) {
    const school = await department.get('school');
    return this.canDoInSchool(school, 'CAN_DELETE_DEPARTMENTS');
  },
  async canUpdateDepartmentInSchool(school) {
    return this.canDoInSchool(school, 'CAN_UPDATE_DEPARTMENTS');
  },
  async canDeleteDepartmentInSchool(school) {
    return this.canDoInSchool(school, 'CAN_DELETE_DEPARTMENTS');
  },
  async canCreateDepartment(school) {
    return this.canDoInSchool(school, 'CAN_CREATE_DEPARTMENTS');
  },
  async canUpdateProgram(program) {
    const currentUser = await this.get('currentUser');
    const permissionMatrix = this.get('permissionMatrix');

    const school = await program.get('school');
    if (await this.canDoInSchool(school, 'CAN_UPDATE_ALL_PROGRAMS')) {
      return true;
    }

    const rolesInProgram = await currentUser.getRolesInProgram(program);
    return await permissionMatrix.hasPermission(school, 'CAN_UPDATE_THEIR_PROGRAMS', rolesInProgram);
  },
  async canDeleteProgram(program) {
    const currentUser = await this.get('currentUser');
    const permissionMatrix = this.get('permissionMatrix');
    const school = await program.get('school');
    if (await this.canDoInSchool(school, 'CAN_DELETE_ALL_PROGRAMS')) {
      return true;
    }

    const rolesInProgram = await currentUser.getRolesInProgram(program);
    return await permissionMatrix.hasPermission(school, 'CAN_DELETE_THEIR_PROGRAMS', rolesInProgram);
  },
  async canCreateProgram(school) {
    return this.canDoInSchool(school, 'CAN_CREATE_PROGRAMS');
  },
  async canUpdateProgramYear(programYear) {
    const currentUser = await this.get('currentUser');
    const permissionMatrix = this.get('permissionMatrix');

    const program = await programYear.get('program');
    const school = await program.get('school');
    if (await this.canDoInSchool(school, 'CAN_UPDATE_ALL_PROGRAM_YEARS')) {
      return true;
    }
    const rolesInProgramYear = await currentUser.getRolesInProgramYear(programYear);
    if (await permissionMatrix.hasPermission(school, 'CAN_UPDATE_THEIR_PROGRAM_YEARS', rolesInProgramYear)) {
      return true;
    }

    return this.canUpdateProgram(program);
  },
  async canDeleteProgramYear(programYear) {
    const currentUser = await this.get('currentUser');
    const permissionMatrix = this.get('permissionMatrix');

    const program = await programYear.get('program');
    const school = await program.get('school');
    if (await this.canDoInSchool(school, 'CAN_DELETE_ALL_PROGRAM_YEARS')) {
      return true;
    }
    const rolesInProgramYear = await currentUser.getRolesInProgramYear(programYear);
    if (await permissionMatrix.hasPermission(school, 'CAN_DELETE_THEIR_PROGRAM_YEARS', rolesInProgramYear)) {
      return true;
    }

    return this.canUpdateProgram(program);
  },
  async canCreateProgramYear(program) {
    const school = await program.get('school');
    if (await this.canDoInSchool(school, 'CAN_CREATE_PROGRAM_YEARS')) {
      return true;
    }
    return this.canUpdateProgram(program);
  },
  async canUnlockProgramYear(programYear) {
    const currentUser = await this.get('currentUser');
    const permissionMatrix = this.get('permissionMatrix');
    const program = await programYear.get('program');
    const school = await program.get('school');
    if (await this.canDoInSchool(school, 'CAN_UNLOCK_ALL_PROGRAM_YEARS')) {
      return true;
    }
    const rolesInProgramYear = await currentUser.getRolesInProgramYear(programYear);
    if (await permissionMatrix.hasPermission(school, 'CAN_UNLOCK_THEIR_PROGRAM_YEARS', rolesInProgramYear)) {
      return true;
    }

    return this.canUpdateProgram(program);
  },
  async canUpdateSchoolConfig(school) {
    return this.canDoInSchool(school, 'CAN_UPDATE_SCHOOL_CONFIGS');
  },
  async canUpdateSchool(school) {
    return this.canDoInSchool(school, 'CAN_UPDATE_SCHOOLS');
  },
  async canUpdateCompetency(competency) {
    const school = await competency.get('school');
    return this.canDoInSchool(school, 'CAN_UPDATE_COMPETENCIES');
  },
  async canDeleteCompetency(competency) {
    const school = await competency.get('school');
    return this.canDoInSchool(school, 'CAN_DELETE_COMPETENCIES');
  },
  async canUpdateCompetencyInSchool(school) {
    return this.canDoInSchool(school, 'CAN_UPDATE_COMPETENCIES');
  },
  async canDeleteCompetencyInSchool(school) {
    return this.canDoInSchool(school, 'CAN_DELETE_COMPETENCIES');
  },
  async canCreateCompetency(school) {
    return this.canDoInSchool(school, 'CAN_CREATE_COMPETENCIES');
  },
  async canUpdateVocabulary(vocabulary) {
    const school = await vocabulary.get('school');
    return this.canDoInSchool(school, 'CAN_UPDATE_VOCABULARIES');
  },
  async canDeleteVocabulary(vocabulary) {
    const school = await vocabulary.get('school');
    return this.canDoInSchool(school, 'CAN_DELETE_VOCABULARIES');
  },
  async canUpdateVocabularyInSchool(school) {
    return this.canDoInSchool(school, 'CAN_UPDATE_VOCABULARIES');
  },
  async canDeleteVocabularyInSchool(school) {
    return this.canDoInSchool(school, 'CAN_DELETE_VOCABULARIES');
  },
  async canCreateVocabulary(school) {
    return this.canDoInSchool(school, 'CAN_CREATE_VOCABULARIES');
  },
  async canUpdateTerm(term) {
    const school = await term.get('school');
    return this.canDoInSchool(school, 'CAN_UPDATE_TERMS');
  },
  async canDeleteTerm(term) {
    const school = await term.get('school');
    return this.canDoInSchool(school, 'CAN_DELETE_TERMS');
  },
  async canUpdateTermInSchool(school) {
    return this.canDoInSchool(school, 'CAN_UPDATE_TERMS');
  },
  async canDeleteTermInSchool(school) {
    return this.canDoInSchool(school, 'CAN_DELETE_TERMS');
  },
  async canCreateTerm(school) {
    return this.canDoInSchool(school, 'CAN_CREATE_TERMS');
  },
  async canUpdateInstructorGroup(instructorGroup) {
    const school = await instructorGroup.get('school');
    return this.canDoInSchool(school, 'CAN_UPDATE_INSTRUCTOR_GROUPS');
  },
  async canDeleteInstructorGroup(instructorGroup) {
    const school = await instructorGroup.get('school');
    return this.canDoInSchool(school, 'CAN_DELETE_INSTRUCTOR_GROUPS');
  },
  async canCreateInstructorGroup(school) {
    return this.canDoInSchool(school, 'CAN_CREATE_INSTRUCTOR_GROUPS');
  },
  async canUpdateInstructorGroupInSchool(school) {
    return this.canDoInSchool(school, 'CAN_UPDATE_INSTRUCTOR_GROUPS');
  },
  async canDeleteInstructorGroupInSchool(school) {
    return this.canDoInSchool(school, 'CAN_DELETE_INSTRUCTOR_GROUPS');
  },
  async canUpdateCurriculumInventoryReport(curriculumInventoryReport) {
    const currentUser = await this.get('currentUser');
    const permissionMatrix = this.get('permissionMatrix');

    if (curriculumInventoryReport.get('isFinalized')) {
      return false;
    }

    const program = await curriculumInventoryReport.get('program');
    const school = await program.get('school');
    if (await this.canDoInSchool(school, 'CAN_UPDATE_ALL_CURRICULUM_INVENTORY_REPORTS')) {
      return true;
    }
    const rolesInReport = await currentUser.getRolesInCurriculumInventoryReport(curriculumInventoryReport);

    return permissionMatrix.hasPermission(school, 'CAN_UPDATE_THEIR_CURRICULUM_INVENTORY_REPORTS', rolesInReport);
  },
  async canDeleteCurriculumInventoryReport(curriculumInventoryReport) {
    const currentUser = await this.get('currentUser');
    const permissionMatrix = this.get('permissionMatrix');

    if (curriculumInventoryReport.get('isFinalized')) {
      return false;
    }

    const program = await curriculumInventoryReport.get('program');
    const school = await program.get('school');
    if (await this.canDoInSchool(school, 'CAN_DELETE_ALL_CURRICULUM_INVENTORY_REPORTS')) {
      return true;
    }

    const rolesInReport = await currentUser.getRolesInCurriculumInventoryReport(curriculumInventoryReport);
    return permissionMatrix.hasPermission(school, 'CAN_DELETE_THEIR_CURRICULUM_INVENTORY_REPORTS', rolesInReport);
  },
  async canCreateCurriculumInventoryReport(school) {
    return this.canDoInSchool(school, 'CAN_CREATE_CURRICULUM_INVENTORY_REPORTS');
  },
  async canUpdateLearnerGroup(learnerGroup) {
    const school = await learnerGroup.get('school');
    return this.canDoInSchool(school, 'CAN_UPDATE_LEARNER_GROUPS');
  },
  async canDeleteLearnerGroup(learnerGroup) {
    const school = await learnerGroup.get('school');
    return this.canDoInSchool(school, 'CAN_DELETE_LEARNER_GROUPS');
  },
  async canUpdateLearnerGroupInSchool(school) {
    return this.canDoInSchool(school, 'CAN_UPDATE_LEARNER_GROUPS');
  },
  async canDeleteLearnerGroupInSchool(school) {
    return this.canDoInSchool(school, 'CAN_DELETE_LEARNER_GROUPS');
  },
  async canCreateLearnerGroup(school) {
    return this.canDoInSchool(school, 'CAN_CREATE_LEARNER_GROUPS');
  },
  async canUpdateUser(user) {
    const school = await user.get('school');
    return this.canDoInSchool(school, 'CAN_UPDATE_USERS');
  },
  async canDeleteUser(user) {
    const school = await user.get('school');
    return this.canDoInSchool(school, 'CAN_DELETE_USERS');
  },
  async canCreateUser(school) {
    return this.canDoInSchool(school, 'CAN_CREATE_USERS');
  },
  async canUpdateUserInSchool(school) {
    return this.canDoInSchool(school, 'CAN_UPDATE_USERS');
  },
});
<commit_msg>Add canUpdateAllCoursesInSchool to permissionCheccker<commit_after>import Service from '@ember/service';
import { inject as service } from '@ember/service';

export default Service.extend({
  currentUser: service(),
  permissionMatrix: service(),
  async canDoInSchool(school, capability) {
    const currentUser = await this.get('currentUser');
    const isRoot = await currentUser.get('isRoot');
    if (isRoot) {
      return true;
    }
    const permissionMatrix = this.get('permissionMatrix');
    const rolesInSchool = await currentUser.getRolesInSchool(school);

    return permissionMatrix.hasPermission(school, capability, rolesInSchool);
  },
  async canUpdateCourse(course) {
    const currentUser = await this.get('currentUser');
    const permissionMatrix = this.get('permissionMatrix');
    if (course.get('locked') || course.get('archived')) {
      return false;
    }
    const school = await course.get('school');

    if (await this.canDoInSchool(school, 'CAN_UPDATE_ALL_COURSES')) {
      return true;
    }

    const rolesInCourse = await currentUser.getRolesInCourse(course);
    return await permissionMatrix.hasPermission(school, 'CAN_UPDATE_THEIR_COURSES', rolesInCourse);
  },
  async canUpdateAllCoursesInSchool(school) {
    return this.canDoInSchool(school, 'CAN_UPDATE_ALL_COURSES');
  },
  async canDeleteCourse(course) {
    const currentUser = await this.get('currentUser');
    const permissionMatrix = this.get('permissionMatrix');
    if (course.get('locked') || course.get('archived')) {
      return false;
    }

    const school = await course.get('school');
    if (await this.canDoInSchool(school, 'CAN_DELETE_ALL_COURSES')) {
      return true;
    }

    const rolesInCourse = await currentUser.getRolesInCourse(course);
    return await permissionMatrix.hasPermission(school, 'CAN_DELETE_THEIR_COURSES', rolesInCourse);
  },
  async canCreateCourse(school) {
    return this.canDoInSchool(school, 'CAN_CREATE_COURSES');
  },
  async canUnlockCourse(course) {
    const currentUser = await this.get('currentUser');
    const permissionMatrix = this.get('permissionMatrix');

    const school = await course.get('school');
    if (await this.canDoInSchool(school, 'CAN_UNLOCK_ALL_COURSES')) {
      return true;
    }

    const rolesInCourse = await currentUser.getRolesInCourse(course);
    return await permissionMatrix.hasPermission(school, 'CAN_UNLOCK_THEIR_COURSES', rolesInCourse);
  },
  async canUpdateSession(session) {
    const currentUser = await this.get('currentUser');
    const permissionMatrix = this.get('permissionMatrix');

    const course = await session.get('course');

    if (course.get('locked') || course.get('archived')) {
      return false;
    }

    const school = await course.get('school');
    if (await this.canDoInSchool(school, 'CAN_UPDATE_ALL_SESSIONS')) {
      return true;
    }

    const rolesInSession = await currentUser.getRolesInSession(session);
    if (await permissionMatrix.hasPermission(school, 'CAN_UPDATE_THEIR_SESSIONS', rolesInSession)) {
      return true;
    }

    return this.canUpdateCourse(course);
  },
  async canDeleteSession(session) {
    const currentUser = await this.get('currentUser');
    const permissionMatrix = this.get('permissionMatrix');

    const course = await session.get('course');

    if (course.get('locked') || course.get('archived')) {
      return false;
    }

    const school = await course.get('school');
    if (await this.canDoInSchool(school, 'CAN_DELETE_ALL_SESSIONS')) {
      return true;
    }

    const rolesInSession = await currentUser.getRolesInSession(session);
    if (await permissionMatrix.hasPermission(school, 'CAN_DELETE_THEIR_SESSIONS', rolesInSession)) {
      return true;
    }

    return this.canUpdateCourse(course);
  },
  async canCreateSession(course) {
    if (course.get('locked') || course.get('archived')) {
      return false;
    }

    const school = await course.get('school');
    if (await this.canDoInSchool(school, 'CAN_CREATE_SESSIONS')) {
      return true;
    }

    return this.canUpdateCourse(course);
  },
  async canUpdateSessionType(sessionType) {
    const school = await sessionType.get('school');
    return this.canDoInSchool(school, 'CAN_UPDATE_SESSION_TYPES');
  },
  async canDeleteSessionType(sessionType) {
    const school = await sessionType.get('school');
    return this.canDoInSchool(school, 'CAN_DELETE_SESSION_TYPES');
  },
  async canUpdateSessionTypeInSchool(school) {
    return this.canDoInSchool(school, 'CAN_UPDATE_SESSION_TYPES');
  },
  async canDeleteSessionTypeInSchool(school) {
    return this.canDoInSchool(school, 'CAN_DELETE_SESSION_TYPES');
  },
  async canCreateSessionType(school) {
    return this.canDoInSchool(school, 'CAN_CREATE_SESSION_TYPES');
  },
  async canUpdateDepartment(department) {
    const school = await department.get('school');
    return this.canDoInSchool(school, 'CAN_UPDATE_DEPARTMENTS');
  },
  async canDeleteDepartment(department) {
    const school = await department.get('school');
    return this.canDoInSchool(school, 'CAN_DELETE_DEPARTMENTS');
  },
  async canUpdateDepartmentInSchool(school) {
    return this.canDoInSchool(school, 'CAN_UPDATE_DEPARTMENTS');
  },
  async canDeleteDepartmentInSchool(school) {
    return this.canDoInSchool(school, 'CAN_DELETE_DEPARTMENTS');
  },
  async canCreateDepartment(school) {
    return this.canDoInSchool(school, 'CAN_CREATE_DEPARTMENTS');
  },
  async canUpdateProgram(program) {
    const currentUser = await this.get('currentUser');
    const permissionMatrix = this.get('permissionMatrix');

    const school = await program.get('school');
    if (await this.canDoInSchool(school, 'CAN_UPDATE_ALL_PROGRAMS')) {
      return true;
    }

    const rolesInProgram = await currentUser.getRolesInProgram(program);
    return await permissionMatrix.hasPermission(school, 'CAN_UPDATE_THEIR_PROGRAMS', rolesInProgram);
  },
  async canDeleteProgram(program) {
    const currentUser = await this.get('currentUser');
    const permissionMatrix = this.get('permissionMatrix');
    const school = await program.get('school');
    if (await this.canDoInSchool(school, 'CAN_DELETE_ALL_PROGRAMS')) {
      return true;
    }

    const rolesInProgram = await currentUser.getRolesInProgram(program);
    return await permissionMatrix.hasPermission(school, 'CAN_DELETE_THEIR_PROGRAMS', rolesInProgram);
  },
  async canCreateProgram(school) {
    return this.canDoInSchool(school, 'CAN_CREATE_PROGRAMS');
  },
  async canUpdateProgramYear(programYear) {
    const currentUser = await this.get('currentUser');
    const permissionMatrix = this.get('permissionMatrix');

    const program = await programYear.get('program');
    const school = await program.get('school');
    if (await this.canDoInSchool(school, 'CAN_UPDATE_ALL_PROGRAM_YEARS')) {
      return true;
    }
    const rolesInProgramYear = await currentUser.getRolesInProgramYear(programYear);
    if (await permissionMatrix.hasPermission(school, 'CAN_UPDATE_THEIR_PROGRAM_YEARS', rolesInProgramYear)) {
      return true;
    }

    return this.canUpdateProgram(program);
  },
  async canDeleteProgramYear(programYear) {
    const currentUser = await this.get('currentUser');
    const permissionMatrix = this.get('permissionMatrix');

    const program = await programYear.get('program');
    const school = await program.get('school');
    if (await this.canDoInSchool(school, 'CAN_DELETE_ALL_PROGRAM_YEARS')) {
      return true;
    }
    const rolesInProgramYear = await currentUser.getRolesInProgramYear(programYear);
    if (await permissionMatrix.hasPermission(school, 'CAN_DELETE_THEIR_PROGRAM_YEARS', rolesInProgramYear)) {
      return true;
    }

    return this.canUpdateProgram(program);
  },
  async canCreateProgramYear(program) {
    const school = await program.get('school');
    if (await this.canDoInSchool(school, 'CAN_CREATE_PROGRAM_YEARS')) {
      return true;
    }
    return this.canUpdateProgram(program);
  },
  async canUnlockProgramYear(programYear) {
    const currentUser = await this.get('currentUser');
    const permissionMatrix = this.get('permissionMatrix');
    const program = await programYear.get('program');
    const school = await program.get('school');
    if (await this.canDoInSchool(school, 'CAN_UNLOCK_ALL_PROGRAM_YEARS')) {
      return true;
    }
    const rolesInProgramYear = await currentUser.getRolesInProgramYear(programYear);
    if (await permissionMatrix.hasPermission(school, 'CAN_UNLOCK_THEIR_PROGRAM_YEARS', rolesInProgramYear)) {
      return true;
    }

    return this.canUpdateProgram(program);
  },
  async canUpdateSchoolConfig(school) {
    return this.canDoInSchool(school, 'CAN_UPDATE_SCHOOL_CONFIGS');
  },
  async canUpdateSchool(school) {
    return this.canDoInSchool(school, 'CAN_UPDATE_SCHOOLS');
  },
  async canUpdateCompetency(competency) {
    const school = await competency.get('school');
    return this.canDoInSchool(school, 'CAN_UPDATE_COMPETENCIES');
  },
  async canDeleteCompetency(competency) {
    const school = await competency.get('school');
    return this.canDoInSchool(school, 'CAN_DELETE_COMPETENCIES');
  },
  async canUpdateCompetencyInSchool(school) {
    return this.canDoInSchool(school, 'CAN_UPDATE_COMPETENCIES');
  },
  async canDeleteCompetencyInSchool(school) {
    return this.canDoInSchool(school, 'CAN_DELETE_COMPETENCIES');
  },
  async canCreateCompetency(school) {
    return this.canDoInSchool(school, 'CAN_CREATE_COMPETENCIES');
  },
  async canUpdateVocabulary(vocabulary) {
    const school = await vocabulary.get('school');
    return this.canDoInSchool(school, 'CAN_UPDATE_VOCABULARIES');
  },
  async canDeleteVocabulary(vocabulary) {
    const school = await vocabulary.get('school');
    return this.canDoInSchool(school, 'CAN_DELETE_VOCABULARIES');
  },
  async canUpdateVocabularyInSchool(school) {
    return this.canDoInSchool(school, 'CAN_UPDATE_VOCABULARIES');
  },
  async canDeleteVocabularyInSchool(school) {
    return this.canDoInSchool(school, 'CAN_DELETE_VOCABULARIES');
  },
  async canCreateVocabulary(school) {
    return this.canDoInSchool(school, 'CAN_CREATE_VOCABULARIES');
  },
  async canUpdateTerm(term) {
    const school = await term.get('school');
    return this.canDoInSchool(school, 'CAN_UPDATE_TERMS');
  },
  async canDeleteTerm(term) {
    const school = await term.get('school');
    return this.canDoInSchool(school, 'CAN_DELETE_TERMS');
  },
  async canUpdateTermInSchool(school) {
    return this.canDoInSchool(school, 'CAN_UPDATE_TERMS');
  },
  async canDeleteTermInSchool(school) {
    return this.canDoInSchool(school, 'CAN_DELETE_TERMS');
  },
  async canCreateTerm(school) {
    return this.canDoInSchool(school, 'CAN_CREATE_TERMS');
  },
  async canUpdateInstructorGroup(instructorGroup) {
    const school = await instructorGroup.get('school');
    return this.canDoInSchool(school, 'CAN_UPDATE_INSTRUCTOR_GROUPS');
  },
  async canDeleteInstructorGroup(instructorGroup) {
    const school = await instructorGroup.get('school');
    return this.canDoInSchool(school, 'CAN_DELETE_INSTRUCTOR_GROUPS');
  },
  async canCreateInstructorGroup(school) {
    return this.canDoInSchool(school, 'CAN_CREATE_INSTRUCTOR_GROUPS');
  },
  async canUpdateInstructorGroupInSchool(school) {
    return this.canDoInSchool(school, 'CAN_UPDATE_INSTRUCTOR_GROUPS');
  },
  async canDeleteInstructorGroupInSchool(school) {
    return this.canDoInSchool(school, 'CAN_DELETE_INSTRUCTOR_GROUPS');
  },
  async canUpdateCurriculumInventoryReport(curriculumInventoryReport) {
    const currentUser = await this.get('currentUser');
    const permissionMatrix = this.get('permissionMatrix');

    if (curriculumInventoryReport.get('isFinalized')) {
      return false;
    }

    const program = await curriculumInventoryReport.get('program');
    const school = await program.get('school');
    if (await this.canDoInSchool(school, 'CAN_UPDATE_ALL_CURRICULUM_INVENTORY_REPORTS')) {
      return true;
    }
    const rolesInReport = await currentUser.getRolesInCurriculumInventoryReport(curriculumInventoryReport);

    return permissionMatrix.hasPermission(school, 'CAN_UPDATE_THEIR_CURRICULUM_INVENTORY_REPORTS', rolesInReport);
  },
  async canDeleteCurriculumInventoryReport(curriculumInventoryReport) {
    const currentUser = await this.get('currentUser');
    const permissionMatrix = this.get('permissionMatrix');

    if (curriculumInventoryReport.get('isFinalized')) {
      return false;
    }

    const program = await curriculumInventoryReport.get('program');
    const school = await program.get('school');
    if (await this.canDoInSchool(school, 'CAN_DELETE_ALL_CURRICULUM_INVENTORY_REPORTS')) {
      return true;
    }

    const rolesInReport = await currentUser.getRolesInCurriculumInventoryReport(curriculumInventoryReport);
    return permissionMatrix.hasPermission(school, 'CAN_DELETE_THEIR_CURRICULUM_INVENTORY_REPORTS', rolesInReport);
  },
  async canCreateCurriculumInventoryReport(school) {
    return this.canDoInSchool(school, 'CAN_CREATE_CURRICULUM_INVENTORY_REPORTS');
  },
  async canUpdateLearnerGroup(learnerGroup) {
    const school = await learnerGroup.get('school');
    return this.canDoInSchool(school, 'CAN_UPDATE_LEARNER_GROUPS');
  },
  async canDeleteLearnerGroup(learnerGroup) {
    const school = await learnerGroup.get('school');
    return this.canDoInSchool(school, 'CAN_DELETE_LEARNER_GROUPS');
  },
  async canUpdateLearnerGroupInSchool(school) {
    return this.canDoInSchool(school, 'CAN_UPDATE_LEARNER_GROUPS');
  },
  async canDeleteLearnerGroupInSchool(school) {
    return this.canDoInSchool(school, 'CAN_DELETE_LEARNER_GROUPS');
  },
  async canCreateLearnerGroup(school) {
    return this.canDoInSchool(school, 'CAN_CREATE_LEARNER_GROUPS');
  },
  async canUpdateUser(user) {
    const school = await user.get('school');
    return this.canDoInSchool(school, 'CAN_UPDATE_USERS');
  },
  async canDeleteUser(user) {
    const school = await user.get('school');
    return this.canDoInSchool(school, 'CAN_DELETE_USERS');
  },
  async canCreateUser(school) {
    return this.canDoInSchool(school, 'CAN_CREATE_USERS');
  },
  async canUpdateUserInSchool(school) {
    return this.canDoInSchool(school, 'CAN_UPDATE_USERS');
  },
});
<|endoftext|>