<commit_before>describe('SimpleSlider', function() {

  'use strict';

  var testDivCount = 0;
  var _consoleWarn;

  // Helper functions to create dummy elements

  var createEmptyDiv = function() {

    var newDiv = document.createElement('div');
    newDiv.id = 'test-div-' + testDivCount;
    newDiv.style.width = '480px';
    newDiv.style.height = '200px';
    testDivCount++;

    return newDiv;

  };

  var getNewDiv = function(numChild) {

    var newDiv = createEmptyDiv();

    addChildrenDivs(newDiv, numChild);

    return newDiv;

  };

  var addChildrenDivs = function(newDiv, numChild) {

    var childrenNum = numChild ? numChild : Math.ceil( Math.random()*10 );
    while (--childrenNum >= 0) {
      newDiv.appendChild(document.createElement('div'));
    }

  };

  var getNewSlider = function(options, numChild) {

    var testDiv = getNewDiv(numChild);
    var ss = new SimpleSlider(testDiv, options);

    return ss;

  };

  var createEmptySlider = function() {

    _consoleWarn = console.warn;
    console.warn = function() {};

    return new SimpleSlider(createEmptyDiv());

  };

  var disposeEmptySlider = function(ss) {
    ss.dispose();
    console.warn = _consoleWarn;
  };

  it('should be able to create a new instance', function() {

    var ss = getNewSlider();
    expect(typeof ss).toEqual('object');
    expect(ss instanceof SimpleSlider).toBeTruthy();

    ss.dispose();

  });

  it('should throw an warning if using an empty html element', function() {

    spyOn(console, 'warn');
    var ss = new SimpleSlider(document.createElement('div'));
    expect(console.warn).toHaveBeenCalled();

    ss.dispose();

  });

  it('default properties should match', function() {

    // Test default values
    var ss = getNewSlider();
    var width = parseInt(ss.containerElem.style.width, 10);
    expect(ss.trProp).toEqual('left');
    expect(ss.trTime).toEqual(0.5);
    expect(ss.delay).toEqual(3000);
    expect(ss.startVal).toEqual(-width);
    expect(ss.visVal).toEqual(0);
    expect(ss.endVal).toEqual(width);
    expect(ss.autoPlay).toEqual(true);
    expect(ss.ease).toEqual(SimpleSlider.defaultEase);

    ss.dispose();

  });

  it('properties should be defined properly', function() {

    // Test some custom values
    var ss = getNewSlider({
      transitionProperty: 'left',
      transitionDuration: 1,
      transitionDelay: 2,
      startValue: 300,
      visibleValue: 200,
      endValue: 100,
      autoPlay:false
    });
    expect(ss.trProp).toEqual('left');
    expect(ss.trTime).toEqual(1);
    expect(ss.delay).toEqual(2000);
    expect(ss.startVal).toEqual(300);
    expect(ss.visVal).toEqual(200);
    expect(ss.endVal).toEqual(100);
    expect(ss.autoPlay).toEqual(false);

    ss.dispose();

  });

  it('should accept strings for boolean values', function() {

    // Test some custom values
    var ss = getNewSlider({
      autoPlay:'false'
    });
    expect(ss.autoPlay).toEqual(false);

    ss.dispose();

  });

  it('should work when partialy declaring properties', function() {

    // Partially defined values
    var ss = getNewSlider({
      transitionProperty: 'top',
      startValue: -100,
      autoPlay:false
    });
    expect(ss.trProp).toEqual('top');
    expect(ss.startVal).toEqual(-100);
    expect(ss.autoPlay).toEqual(false);

    ss.dispose();

  });

  it('after init should contain imgs data', function() {

    var newDiv = getNewDiv();
    var ss = new SimpleSlider(newDiv);
    var countChildren = newDiv.children.length-1;

    expect(ss.imgs.length).toEqual(newDiv.children.length);

    while (countChildren>=0) {
      expect(ss.imgs).toContain(newDiv.children[countChildren]);
      countChildren--;
    }

    ss.dispose();

  });

  it('should set initial styling on elements', function () {

    var ss = getNewSlider({}, 5);

    expect(ss.containerElem.style.position).toEqual('relative');
    expect(ss.containerElem.style.overflow).toEqual('hidden');

    var i = ss.containerElem.children.length;

    while (--i >= 0) {
      expect(ss.imgs[i].style.position).toEqual('absolute');
      expect(ss.imgs[i].style.top).toEqual('0px');

      // Only the first one should be on visible state
      if (i === 0) {
        expect(ss.imgs[i].style.left).toEqual('0px');
      } else {
        expect(ss.imgs[i].style.left).toEqual('-480px');
      }
    }

    ss.dispose();

  });

  it('should be able to get px units correctly', function() {

    var ss = getNewSlider({
      transitionProperty: 'left',
      endValue: '300px'
    }, 5);

    expect(ss.unit).toEqual('px');

    ss.dispose();

  });

  it('should be able to get em units correctly', function() {

    // Also tests with em and a smaller number
    var ss = getNewSlider({
      transitionProperty: 'width',
      startValue: '3em'
    }, 3);

    expect(ss.unit).toEqual('em');

    ss.dispose();

  });

  it('should be able to get % units correctly', function() {

    // Should also get when using visibleValue
    var ss = getNewSlider({
      visibleValue: '100%'
    }, 3);

    expect(ss.unit).toEqual('%');

    ss.dispose();

  });

  describe('.reset()', function() {

    it('should reset original style values', function() {

      var ss = getNewSlider({
        autoPlay: false
      }, 5);

      ss.change(3);

      ss.reset();

      expect(ss.containerElem.style.position).toEqual('relative');
      expect(ss.containerElem.style.overflow).toEqual('hidden');
      expect(ss.containerElem.style.display).toEqual('block');

      ss.dispose();

    });

    it('should start imgs control', function() {

      var ss = getNewSlider({
        autoPlay: false
      }, 5);

      ss.change(3);

      ss.reset();

      expect(ss.imgs.length).toEqual(5);

      ss.dispose();

    });

    it('should set actualIndex to 0', function() {

      var ss = getNewSlider({
        autoPlay: false
      }, 5);

      ss.change(3);

      ss.reset();

      expect(ss.actualIndex).toEqual(0);

      ss.dispose();

    });

    it('should set initial image properties values', function() {

      var ss = getNewSlider({
        autoPlay: false
      }, 5);

      ss.change(3);

      ss.reset();

      expect(ss.imgs[0].style[ss.trProp]).toEqual(ss.visVal.toString() + ss.unit);
      expect(ss.imgs[1].style[ss.trProp]).toEqual(ss.startVal.toString() + ss.unit);

      ss.dispose();

    });

    it('should nullify inserted, removed objects', function() {

      var ss = getNewSlider({
        autoPlay: false
      }, 5);

      ss.change(3);

      ss.reset();

      expect(ss.inserted).toEqual(null);
      expect(ss.removed).toEqual(null);

      ss.dispose();

    });

  });

  describe('.configSlideshow()', function() {

    it('should return false if there are no images', function() {

      var ss = createEmptySlider();

      expect(ss.configSlideshow()).toEqual(false);

      disposeEmptySlider(ss);

    });

    it('should not create change image interval if there are no images', function() {

      var ss = createEmptySlider();

      ss.configSlideshow();

      expect(ss.interval).toEqual(null);

      disposeEmptySlider(ss);

    });

    it('should create interval when configuring a valid slider', function() {

      var ss = getNewSlider({}, 5);

      ss.configSlideshow();

      expect(ss.interval).not.toEqual(null);

      ss.dispose();

    });

  });

  describe('.next()', function() {

    it('should change to next slide', function() {

      var ss = getNewSlider({autoPlay:false}, 5);
      var initialIndex = ss.actualIndex;

      ss.next();

      expect(ss.actualIndex).toEqual(initialIndex + 1);

      ss.dispose();

    });

    it('should change to first slide when current slide is the last in the set', function() {

      var ss = getNewSlider({autoPlay:false}, 5);

      ss.actualIndex = 4;

      ss.next();

      expect(ss.actualIndex).toEqual(0);

      ss.dispose();

    });

  });

  describe('.prev()', function() {

    it('should change to previous slide', function() {

      var ss = getNewSlider({autoPlay:false}, 5);

      ss.actualIndex = 1;

      ss.prev();

      expect(ss.actualIndex).toEqual(0);

      ss.dispose();

    });

    it('should change to last slide when current slide is the first in the set', function() {

      var ss = getNewSlider({autoPlay:false}, 5);

      ss.prev();

      expect(ss.actualIndex).toEqual(ss.imgs.length - 1);

      ss.dispose();

    });

  });

  describe('.pauseAutoPlay()', function() {

    it('should clear slide change setInterval', function() {

      var ss = getNewSlider({}, 5);

      expect(ss.interval).not.toEqual(null);

      ss.pauseAutoPlay();

      expect(ss.interval).toEqual(null);

      ss.dispose();

    });

    it('should define this.remainingTime variable', function() {

      var ss = getNewSlider({}, 5);

      ss.pauseAutoPlay();

      expect(ss.remainingTime).toEqual(ss.delay);

      ss.dispose();

    });

    it('should do nothing when autoPlay is disabled', function() {

      var ss = getNewSlider({autoPlay:false}, 5);

      ss.pauseAutoPlay();

      ss.dispose();

    });

  });

  describe('.resumeAutoPlay()', function() {

    it('should re-enable slide changing setInterval', function () {

      var ss = getNewSlider({}, 5);

      ss.pauseAutoPlay();

      expect(ss.interval).toEqual(null);

      ss.resumeAutoPlay();

      expect(ss.interval).not.toEqual(null);

      ss.dispose();

    });

    it('should do nothing when autoPlay is disabled', function() {

      var ss = getNewSlider({autoPlay:false}, 5);

      ss.resumeAutoPlay();

      ss.dispose();

    });

  });

  describe('.remove()', function() {

    it('should trigger startAnim with correct values', function() {

      var ss = getNewSlider({
        autoPlay: false
      }, 5);

      spyOn(ss, 'startAnim');

      ss.remove(0);

      expect(ss.startAnim).toHaveBeenCalledWith(ss.imgs[0], ss.visVal, ss.endVal);

      ss.dispose();

    });

  });

  describe('.insert()', function() {

    it('should trigger startAnim with correct values', function() {

      var ss = getNewSlider({
        autoPlay: false
      }, 5);

      spyOn(ss, 'startAnim');

      ss.insert(1);

      expect(ss.startAnim).toHaveBeenCalledWith(ss.imgs[1], ss.startVal, ss.visVal);

      ss.dispose();

    });

  });

  describe('.nextIndex()', function() {

    it('should return next index value on carousel', function() {

      var ss = getNewSlider({
        autoPlay: false
      }, 5);

      // Original value should always be zero
      expect(ss.actualIndex).toEqual(0);

      // Next index should not increment actualIndex value
      expect(ss.nextIndex()).toEqual(1);

      ss.dispose();

    });

    it('should return first item index when it is on last item', function() {

      var ss = getNewSlider({
        autoPlay: false
      }, 5);

      ss.change(4);

      expect(ss.nextIndex()).toEqual(0);

      ss.dispose();

    });

    it('should not increment actualIndex value', function() {

      var ss = getNewSlider({
        autoPlay: false
      }, 5);

      // Next index should not increment actualIndex value
      expect(ss.nextIndex()).toEqual(1);
      expect(ss.nextIndex()).toEqual(1);

      ss.dispose();

    });

  });

  describe('.dispose()', function() {

    it('should dispose created instances', function() {

      var ss = getNewSlider({
        transitionProperty: 'opacity'
      }, 5);

      ss.dispose();

      expect(ss.imgs).toEqual(null);
      expect(ss.actualIndex).toEqual(null);
      expect(ss.interval).toEqual(null);
      expect(ss.containerElem).toEqual(null);

    });

    it('dispose should clear autoplay interval', function(done) {

      var ss = getNewSlider({
        autoPlay: true,
        transitionProperty: 'opacity'
      }, 5);

      // spy on change method
      spyOn(ss, 'change');

      ss.dispose();

      setTimeout(function() {
        expect(ss.change).not.toHaveBeenCalled();
        done();
      }, (ss.delay) + 1);

    });

  });

});
<commit_msg>Fixed randomly failing test<commit_after>describe('SimpleSlider', function() {

  'use strict';

  var testDivCount = 0;
  var _consoleWarn;

  // Helper functions to create dummy elements

  var createEmptyDiv = function() {

    var newDiv = document.createElement('div');
    newDiv.id = 'test-div-' + testDivCount;
    newDiv.style.width = '480px';
    newDiv.style.height = '200px';
    testDivCount++;

    return newDiv;

  };

  var getNewDiv = function(numChild) {

    var newDiv = createEmptyDiv();

    addChildrenDivs(newDiv, numChild);

    return newDiv;

  };

  var addChildrenDivs = function(newDiv, numChild) {

    var childrenNum = numChild ? numChild : Math.ceil( Math.random()*10 );
    while (--childrenNum >= 0) {
      newDiv.appendChild(document.createElement('div'));
    }

  };

  var getNewSlider = function(options, numChild) {

    var testDiv = getNewDiv(numChild);
    var ss = new SimpleSlider(testDiv, options);

    return ss;

  };

  var createEmptySlider = function() {

    _consoleWarn = console.warn;
    console.warn = function() {};

    return new SimpleSlider(createEmptyDiv());

  };

  var disposeEmptySlider = function(ss) {
    ss.dispose();
    console.warn = _consoleWarn;
  };

  it('should be able to create a new instance', function() {

    var ss = getNewSlider();
    expect(typeof ss).toEqual('object');
    expect(ss instanceof SimpleSlider).toBeTruthy();

    ss.dispose();

  });

  it('should throw an warning if using an empty html element', function() {

    spyOn(console, 'warn');
    var ss = new SimpleSlider(document.createElement('div'));
    expect(console.warn).toHaveBeenCalled();

    ss.dispose();

  });

  it('default properties should match', function() {

    // Test default values
    var ss = getNewSlider();
    var width = parseInt(ss.containerElem.style.width, 10);
    expect(ss.trProp).toEqual('left');
    expect(ss.trTime).toEqual(0.5);
    expect(ss.delay).toEqual(3000);
    expect(ss.startVal).toEqual(-width);
    expect(ss.visVal).toEqual(0);
    expect(ss.endVal).toEqual(width);
    expect(ss.autoPlay).toEqual(true);
    expect(ss.ease).toEqual(SimpleSlider.defaultEase);

    ss.dispose();

  });

  it('properties should be defined properly', function() {

    // Test some custom values
    var ss = getNewSlider({
      transitionProperty: 'left',
      transitionDuration: 1,
      transitionDelay: 2,
      startValue: 300,
      visibleValue: 200,
      endValue: 100,
      autoPlay:false
    });
    expect(ss.trProp).toEqual('left');
    expect(ss.trTime).toEqual(1);
    expect(ss.delay).toEqual(2000);
    expect(ss.startVal).toEqual(300);
    expect(ss.visVal).toEqual(200);
    expect(ss.endVal).toEqual(100);
    expect(ss.autoPlay).toEqual(false);

    ss.dispose();

  });

  it('should accept strings for boolean values', function() {

    // Test some custom values
    var ss = getNewSlider({
      autoPlay:'false'
    });
    expect(ss.autoPlay).toEqual(false);

    ss.dispose();

  });

  it('should work when partialy declaring properties', function() {

    // Partially defined values
    var ss = getNewSlider({
      transitionProperty: 'top',
      startValue: -100,
      autoPlay:false
    });
    expect(ss.trProp).toEqual('top');
    expect(ss.startVal).toEqual(-100);
    expect(ss.autoPlay).toEqual(false);

    ss.dispose();

  });

  it('after init should contain imgs data', function() {

    var newDiv = getNewDiv();
    var ss = new SimpleSlider(newDiv);
    var countChildren = newDiv.children.length-1;

    expect(ss.imgs.length).toEqual(newDiv.children.length);

    while (countChildren>=0) {
      expect(ss.imgs).toContain(newDiv.children[countChildren]);
      countChildren--;
    }

    ss.dispose();

  });

  it('should set initial styling on elements', function () {

    var ss = getNewSlider({}, 5);

    expect(ss.containerElem.style.position).toEqual('relative');
    expect(ss.containerElem.style.overflow).toEqual('hidden');

    var i = ss.containerElem.children.length;

    while (--i >= 0) {
      expect(ss.imgs[i].style.position).toEqual('absolute');
      expect(ss.imgs[i].style.top).toEqual('0px');

      // Only the first one should be on visible state
      if (i === 0) {
        expect(ss.imgs[i].style.left).toEqual('0px');
      } else {
        expect(ss.imgs[i].style.left).toEqual('-480px');
      }
    }

    ss.dispose();

  });

  it('should be able to get px units correctly', function() {

    var ss = getNewSlider({
      transitionProperty: 'left',
      endValue: '300px'
    }, 5);

    expect(ss.unit).toEqual('px');

    ss.dispose();

  });

  it('should be able to get em units correctly', function() {

    // Also tests with em and a smaller number
    var ss = getNewSlider({
      transitionProperty: 'width',
      startValue: '3em'
    }, 3);

    expect(ss.unit).toEqual('em');

    ss.dispose();

  });

  it('should be able to get % units correctly', function() {

    // Should also get when using visibleValue
    var ss = getNewSlider({
      visibleValue: '100%'
    }, 3);

    expect(ss.unit).toEqual('%');

    ss.dispose();

  });

  describe('.reset()', function() {

    it('should reset original style values', function() {

      var ss = getNewSlider({
        autoPlay: false
      }, 5);

      ss.change(3);

      ss.reset();

      expect(ss.containerElem.style.position).toEqual('relative');
      expect(ss.containerElem.style.overflow).toEqual('hidden');
      expect(ss.containerElem.style.display).toEqual('block');

      ss.dispose();

    });

    it('should start imgs control', function() {

      var ss = getNewSlider({
        autoPlay: false
      }, 5);

      ss.change(3);

      ss.reset();

      expect(ss.imgs.length).toEqual(5);

      ss.dispose();

    });

    it('should set actualIndex to 0', function() {

      var ss = getNewSlider({
        autoPlay: false
      }, 5);

      ss.change(3);

      ss.reset();

      expect(ss.actualIndex).toEqual(0);

      ss.dispose();

    });

    it('should set initial image properties values', function() {

      var ss = getNewSlider({
        autoPlay: false
      }, 5);

      ss.change(3);

      ss.reset();

      expect(ss.imgs[0].style[ss.trProp]).toEqual(ss.visVal.toString() + ss.unit);
      expect(ss.imgs[1].style[ss.trProp]).toEqual(ss.startVal.toString() + ss.unit);

      ss.dispose();

    });

    it('should nullify inserted, removed objects', function() {

      var ss = getNewSlider({
        autoPlay: false
      }, 5);

      ss.change(3);

      ss.reset();

      expect(ss.inserted).toEqual(null);
      expect(ss.removed).toEqual(null);

      ss.dispose();

    });

  });

  describe('.configSlideshow()', function() {

    it('should return false if there are no images', function() {

      var ss = createEmptySlider();

      expect(ss.configSlideshow()).toEqual(false);

      disposeEmptySlider(ss);

    });

    it('should not create change image interval if there are no images', function() {

      var ss = createEmptySlider();

      ss.configSlideshow();

      expect(ss.interval).toEqual(null);

      disposeEmptySlider(ss);

    });

    it('should create interval when configuring a valid slider', function() {

      var ss = getNewSlider({}, 5);

      ss.configSlideshow();

      expect(ss.interval).not.toEqual(null);

      ss.dispose();

    });

  });

  describe('.next()', function() {

    it('should change to next slide', function() {

      var ss = getNewSlider({autoPlay:false}, 5);
      var initialIndex = ss.actualIndex;

      ss.next();

      expect(ss.actualIndex).toEqual(initialIndex + 1);

      ss.dispose();

    });

    it('should change to first slide when current slide is the last in the set', function() {

      var ss = getNewSlider({autoPlay:false}, 5);

      ss.actualIndex = 4;

      ss.next();

      expect(ss.actualIndex).toEqual(0);

      ss.dispose();

    });

  });

  describe('.prev()', function() {

    it('should change to previous slide', function() {

      var ss = getNewSlider({autoPlay:false}, 5);

      ss.actualIndex = 1;

      ss.prev();

      expect(ss.actualIndex).toEqual(0);

      ss.dispose();

    });

    it('should change to last slide when current slide is the first in the set', function() {

      var ss = getNewSlider({autoPlay:false}, 5);

      ss.prev();

      expect(ss.actualIndex).toEqual(ss.imgs.length - 1);

      ss.dispose();

    });

  });

  describe('.pauseAutoPlay()', function() {

    it('should clear slide change setInterval', function() {

      var ss = getNewSlider({}, 5);

      expect(ss.interval).not.toEqual(null);

      ss.pauseAutoPlay();

      expect(ss.interval).toEqual(null);

      ss.dispose();

    });

    it('should define this.remainingTime value', function() {

      var ss = getNewSlider({}, 5);

      ss.pauseAutoPlay();

      // Adds half a second of tolerances
      // it might be some miliseconds off sometimes
      expect(ss.remainingTime).toBeGreaterThan(ss.delay - 500);

      // But should never be over the origin delay
      expect(ss.remainingTime).toBeLessThan(ss.delay + 1);

      ss.dispose();

    });

    it('should do nothing when autoPlay is disabled', function() {

      var ss = getNewSlider({autoPlay:false}, 5);

      ss.pauseAutoPlay();

      ss.dispose();

    });

  });

  describe('.resumeAutoPlay()', function() {

    it('should re-enable slide changing setInterval', function () {

      var ss = getNewSlider({}, 5);

      ss.pauseAutoPlay();

      expect(ss.interval).toEqual(null);

      ss.resumeAutoPlay();

      expect(ss.interval).not.toEqual(null);

      ss.dispose();

    });

    it('should do nothing when autoPlay is disabled', function() {

      var ss = getNewSlider({autoPlay:false}, 5);

      ss.resumeAutoPlay();

      ss.dispose();

    });

  });

  describe('.remove()', function() {

    it('should trigger startAnim with correct values', function() {

      var ss = getNewSlider({
        autoPlay: false
      }, 5);

      spyOn(ss, 'startAnim');

      ss.remove(0);

      expect(ss.startAnim).toHaveBeenCalledWith(ss.imgs[0], ss.visVal, ss.endVal);

      ss.dispose();

    });

  });

  describe('.insert()', function() {

    it('should trigger startAnim with correct values', function() {

      var ss = getNewSlider({
        autoPlay: false
      }, 5);

      spyOn(ss, 'startAnim');

      ss.insert(1);

      expect(ss.startAnim).toHaveBeenCalledWith(ss.imgs[1], ss.startVal, ss.visVal);

      ss.dispose();

    });

  });

  describe('.nextIndex()', function() {

    it('should return next index value on carousel', function() {

      var ss = getNewSlider({
        autoPlay: false
      }, 5);

      // Original value should always be zero
      expect(ss.actualIndex).toEqual(0);

      // Next index should not increment actualIndex value
      expect(ss.nextIndex()).toEqual(1);

      ss.dispose();

    });

    it('should return first item index when it is on last item', function() {

      var ss = getNewSlider({
        autoPlay: false
      }, 5);

      ss.change(4);

      expect(ss.nextIndex()).toEqual(0);

      ss.dispose();

    });

    it('should not increment actualIndex value', function() {

      var ss = getNewSlider({
        autoPlay: false
      }, 5);

      // Next index should not increment actualIndex value
      expect(ss.nextIndex()).toEqual(1);
      expect(ss.nextIndex()).toEqual(1);

      ss.dispose();

    });

  });

  describe('.dispose()', function() {

    it('should dispose created instances', function() {

      var ss = getNewSlider({
        transitionProperty: 'opacity'
      }, 5);

      ss.dispose();

      expect(ss.imgs).toEqual(null);
      expect(ss.actualIndex).toEqual(null);
      expect(ss.interval).toEqual(null);
      expect(ss.containerElem).toEqual(null);

    });

    it('dispose should clear autoplay interval', function(done) {

      var ss = getNewSlider({
        autoPlay: true,
        transitionProperty: 'opacity'
      }, 5);

      // spy on change method
      spyOn(ss, 'change');

      ss.dispose();

      setTimeout(function() {
        expect(ss.change).not.toHaveBeenCalled();
        done();
      }, (ss.delay) + 1);

    });

  });

});
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import AUpageAlert from '@gov.au/page-alerts/lib/js/react.js'
import { withRouter, Switch, Route, BrowserRouter } from 'react-router-dom'
import Header from 'marketplace/components/SellerDashboard/Header'
import Messages from 'marketplace/components/SellerDashboard/Messages'
import Team from 'marketplace/components/SellerDashboard/Team'
import Categories from 'marketplace/components/SellerDashboard/Categories'
import Opportunities from 'marketplace/components/SellerDashboard/Opportunities'
import { loadSellerDashboard, removeUser } from 'marketplace/actions/sellerDashboardActions'
import LoadingIndicatorFullPage from 'shared/LoadingIndicatorFullPage/LoadingIndicatorFullPage'
import { ErrorBoxComponent } from 'shared/form/ErrorBox'
import { rootPath } from 'marketplace/routes'
import styles from '../main.scss'

class SellerDashboardPage extends Component {
  constructor(props) {
    super(props)
    this.state = {
      showRemoveAlert: false,
      toRemoveUser: null
    }

    this.handleRemoveClick = this.handleRemoveClick.bind(this)
  }

  componentDidMount() {
    this.props.loadInitialData()
  }

  componentDidUpdate = () => {
    if (this.state.showRemoveAlert) {
      window.scrollTo(0, this.deleteAlertRef.offsetTop)
      this.doNotRemove.focus()
    }
  }

  handleYesClick = () => {
    const { toRemoveUser } = this.state
    this.props.removeUser(toRemoveUser.id)
    this.setState({
      toRemoveUser: null,
      showRemoveAlert: false
    })
  }

  handleRemoveClick = item => {
    this.setState({
      toRemoveUser: item,
      showRemoveAlert: true
    })
  }

  handleNoClicked = () => {
    this.setState({
      toRemoveUser: null,
      showRemoveAlert: false
    })
  }

  render() {
    const { loading, supplier, errors, errorMessage } = this.props

    if (errors) {
      return (
        <ErrorBoxComponent
          title="A problem occurred when loading the supplier"
          errorMessage={errorMessage}
          setFocus={() => {}}
          form={{}}
          invalidFields={[]}
        />
      )
    }

    if (loading || !supplier) {
      return <LoadingIndicatorFullPage />
    }

    return (
      <BrowserRouter basename={`${rootPath}/seller-dashboard`}>

{supplier.name}

Dashboard

          {this.state.showRemoveAlert && (

              <div className={`${styles.marginTop1} col-xs-12 col-sm-7`}>
                <AUpageAlert as="warning">

 {
                      this.deleteAlertRef = ref
                    }}
                  >
                    {`Are you sure you want to remove ${this.state.toRemoveUser.name}?`}

                  <button
                    className={`${styles.marginRight1} ${styles.marginTop1} au-btn`}
                    onClick={this.handleYesClick}
                  >
                    Yes, remove
                  </button>
                  <button
                    className={`${styles.marginTop1} au-btn au-btn--secondary`}
                    onClick={this.handleNoClicked}
                    ref={ref => {
                      this.doNotRemove = ref
                    }}
                  >
                    Do not remove
                  </button>
                </AUpageAlert>

          )}
          <div className={`${styles.marginTop1} row`}>

              <a href={`/supplier/${supplier.code}`} className="au-btn right-button-margin">
                View profile

                Update profile

          <Switch>
            {supplier.is_recruiter_only && (
              <React.Fragment>
                <Route
                  exact
                  path="(/|/team)"
                  render={() => <Team {...this.props} removeClicked={this.handleRemoveClick} />}
                />
                <Route path="/notifications" render={() => <Messages {...this.props} />} />
              </React.Fragment>
            )}
            {!supplier.is_recruiter_only && (
              <React.Fragment>
                <Route exact path="(/|/opportunities)" render={() => <Opportunities {...this.props} />} />
                <Route exact path="(/categories)" render={() => <Categories {...this.props} />} />
                <Route path="/team" render={() => <Team {...this.props} removeClicked={this.handleRemoveClick} />} />
                <Route path="/notifications" render={() => <Messages {...this.props} />} />
              </React.Fragment>
            )}
          </Switch>

      </BrowserRouter>
    )
  }
}

SellerDashboardPage.propTypes = {
  match: PropTypes.object.isRequired
}

const mapStateToProps = state => ({
  supplier: state.sellerDashboard.supplier.supplier,
  loading: state.sellerDashboard.supplier.loading,
  loadedAt: state.sellerDashboard.supplier.loadedAt,
  errors: state.sellerDashboard.supplier.errors,
  messages: state.sellerDashboard.messages,
  errorMessage: state.app.errorMessage
})

const mapDispatchToProps = dispatch => ({
  loadInitialData: () => dispatch(loadSellerDashboard()),
  removeUser: userId => dispatch(removeUser(userId))
})

export default withRouter(
  connect(
    mapStateToProps,
    mapDispatchToProps
  )(SellerDashboardPage)
)
<commit_msg>[MAR-3103] remove category code (#871)<commit_after>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import AUpageAlert from '@gov.au/page-alerts/lib/js/react.js'
import { withRouter, Switch, Route, BrowserRouter } from 'react-router-dom'
import Header from 'marketplace/components/SellerDashboard/Header'
import Messages from 'marketplace/components/SellerDashboard/Messages'
import Team from 'marketplace/components/SellerDashboard/Team'
import Categories from 'marketplace/components/SellerDashboard/Categories'
import Opportunities from 'marketplace/components/SellerDashboard/Opportunities'
import { loadSellerDashboard, removeUser } from 'marketplace/actions/sellerDashboardActions'
import LoadingIndicatorFullPage from 'shared/LoadingIndicatorFullPage/LoadingIndicatorFullPage'
import { ErrorBoxComponent } from 'shared/form/ErrorBox'
import { rootPath } from 'marketplace/routes'
import styles from '../main.scss'

class SellerDashboardPage extends Component {
  constructor(props) {
    super(props)
    this.state = {
      showRemoveAlert: false,
      toRemoveUser: null
    }

    this.handleRemoveClick = this.handleRemoveClick.bind(this)
  }

  componentDidMount() {
    this.props.loadInitialData()
  }

  componentDidUpdate = () => {
    if (this.state.showRemoveAlert) {
      window.scrollTo(0, this.deleteAlertRef.offsetTop)
      this.doNotRemove.focus()
    }
  }

  handleYesClick = () => {
    const { toRemoveUser } = this.state
    this.props.removeUser(toRemoveUser.id)
    this.setState({
      toRemoveUser: null,
      showRemoveAlert: false
    })
  }

  handleRemoveClick = item => {
    this.setState({
      toRemoveUser: item,
      showRemoveAlert: true
    })
  }

  handleNoClicked = () => {
    this.setState({
      toRemoveUser: null,
      showRemoveAlert: false
    })
  }

  render() {
    const { loading, supplier, errors, errorMessage } = this.props

    if (errors) {
      return (
        <ErrorBoxComponent
          title="A problem occurred when loading the supplier"
          errorMessage={errorMessage}
          setFocus={() => {}}
          form={{}}
          invalidFields={[]}
        />
      )
    }

    if (loading || !supplier) {
      return <LoadingIndicatorFullPage />
    }

    return (
      <BrowserRouter basename={`${rootPath}/seller-dashboard`}>

{supplier.name}

Dashboard

          {this.state.showRemoveAlert && (

              <div className={`${styles.marginTop1} col-xs-12 col-sm-7`}>
                <AUpageAlert as="warning">

 {
                      this.deleteAlertRef = ref
                    }}
                  >
                    {`Are you sure you want to remove ${this.state.toRemoveUser.name}?`}

                  <button
                    className={`${styles.marginRight1} ${styles.marginTop1} au-btn`}
                    onClick={this.handleYesClick}
                  >
                    Yes, remove
                  </button>
                  <button
                    className={`${styles.marginTop1} au-btn au-btn--secondary`}
                    onClick={this.handleNoClicked}
                    ref={ref => {
                      this.doNotRemove = ref
                    }}
                  >
                    Do not remove
                  </button>
                </AUpageAlert>

          )}
          <div className={`${styles.marginTop1} row`}>

              <a href={`/supplier/${supplier.code}`} className="au-btn right-button-margin">
                View profile

                Update profile

          <Switch>
            <Route exact path="(/|/opportunities)" render={() => <Opportunities {...this.props} />} />
            {!supplier.is_recruiter_only && (
              <Route exact path="/categories" render={() => <Categories {...this.props} />} />
            )}
            <Route path="/team" render={() => <Team {...this.props} removeClicked={this.handleRemoveClick} />} />
            <Route path="/notifications" render={() => <Messages {...this.props} />} />
          </Switch>

      </BrowserRouter>
    )
  }
}

SellerDashboardPage.propTypes = {
  match: PropTypes.object.isRequired
}

const mapStateToProps = state => ({
  supplier: state.sellerDashboard.supplier.supplier,
  loading: state.sellerDashboard.supplier.loading,
  loadedAt: state.sellerDashboard.supplier.loadedAt,
  errors: state.sellerDashboard.supplier.errors,
  messages: state.sellerDashboard.messages,
  errorMessage: state.app.errorMessage
})

const mapDispatchToProps = dispatch => ({
  loadInitialData: () => dispatch(loadSellerDashboard()),
  removeUser: userId => dispatch(removeUser(userId))
})

export default withRouter(
  connect(
    mapStateToProps,
    mapDispatchToProps
  )(SellerDashboardPage)
)
<|endoftext|>


<commit_before>const interleave = require('../src/utils/tagged-template-literal').interleave
const hasInterpolationTag = require('../src/utils/tagged-template-literal').hasInterpolationTag
const parseInterpolationTag = require('../src/utils/tagged-template-literal').parseInterpolationTag
const extractScTagInformation = require('../src/utils/tagged-template-literal')
  .extractScTagInformation
const isLastDeclarationCompleted = require('../src/utils/general').isLastDeclarationCompleted
const nextNonWhitespaceChar = require('../src/utils/general').nextNonWhitespaceChar
const reverseString = require('../src/utils/general').reverseString
const isStylelintComment = require('../src/utils/general').isStylelintComment
const fixIndentation = require('../src/utils/general').fixIndentation
const extrapolateShortenedCommand = require('../src/utils/general').extrapolateShortenedCommand

describe('utils', () => {
  describe('interleave', () => {
    it('should return the value of the node if no interpolation exists', () => {
      const raw = 'color: blue;'
      const quasis = [
        {
          value: {
            raw
          }
        }
      ]
      expect(interleave(quasis, [])).toEqual(raw)
    })

    it('should variabelize an interpolation', () => {
      const quasis = [
        {
          value: {
            raw: '\n  display: block;\n  color: '
          }
        },
        {
          value: {
            raw: ';\n  background: blue;\n'
          }
        }
      ]
      const expressions = [
        {
          name: 'color'
        }
      ]
      expect(interleave(quasis, expressions)).toEqual(
        '\n  display: block;\n  color: $dummyValue;\n  background: blue;\n'
      )
    })

    it('converts interpolated expressions to dummy mixins', () => {
      const quasis = [
        {
          value: {
            raw: '\n  display: block;\n  '
          }
        },
        {
          value: {
            raw: '\n  background: blue;\n'
          }
        }
      ]
      const expressions = [
        {
          name: undefined
        }
      ]
      expect(interleave(quasis, expressions)).toEqual(
        '\n  display: block;\n  -styled-mixin0: dummyValue;\n  background: blue;\n'
      )
    })

    it('correctly converts several interpolations within a single property', () => {
      const quasis = [
        {
          value: {
            raw: '\n  display: block;\n  border: '
          }
        },
        {
          value: {
            raw: ' '
          }
        },
        {
          value: {
            raw: ' '
          }
        },
        {
          value: {
            raw: ';\n  background: blue;\n'
          }
        }
      ]
      const expressions = [
        {
          name: 'borderWidth'
        },
        {
          name: 'borderStyle'
        },
        {
          name: 'color'
        }
      ]
      expect(interleave(quasis, expressions)).toEqual(
        '\n  display: block;\n  border: $dummyValue $dummyValue $dummyValue;\n  background: blue;\n'
      )
    })

    it('correctly handles several interpolations in single line of css', () => {
      const quasis1 = [
        {
          value: {
            raw: '\n  display: '
          }
        },
        {
          value: {
            raw: '; background: '
          }
        },
        {
          value: {
            raw: ';\n'
          }
        }
      ]
      const expressions1 = [
        {
          name: 'display'
        },
        {
          name: 'background'
        }
      ]
      expect(interleave(quasis1, expressions1)).toEqual(
        '\n  display: $dummyValue; background: $dummyValue;\n'
      )

      const quasis2 = [
        {
          value: {
            raw: '\n  display: '
          }
        },
        {
          value: {
            raw: '; '
          }
        },
        {
          value: {
            raw: '\n'
          }
        }
      ]
      const expressions2 = [
        {
          name: 'display'
        },
        {
          name: undefined
        }
      ]
      expect(interleave(quasis2, expressions2)).toEqual(
        '\n  display: $dummyValue; -styled-mixin0: dummyValue;\n'
      )

      /**
       * It is important to also have this one as interleave would fail this if it simply
       * checked the previous quasi and not the previous processed text.
       * Here we also check the whole expression with and without a semi-colon in the quasi
       */
      const quasis3 = [
        {
          value: {
            raw: '\n  display: '
          }
        },
        {
          value: {
            raw: '; '
          }
        },
        {
          value: {
            raw: ' '
          }
        },
        {
          value: {
            raw: '\n'
          }
        }
      ]
      const expressions3 = [
        {
          name: 'display'
        },
        {
          name: undefined
        },
        {
          name: undefined
        }
      ]
      expect(interleave(quasis3, expressions3)).toEqual(
        '\n  display: $dummyValue; -styled-mixin0: dummyValue; -styled-mixin1: dummyValue;\n'
      )
    })
  })

  describe('reverseString', () => {
    const fn = reverseString

    it('reverses a string', () => {
      expect(fn('abcd')).toEqual('dcba')
    })

    it('handles empty string', () => {
      expect(fn('')).toEqual('')
    })
  })

  describe('nextNonWhitespaceChar', () => {
    const fn = nextNonWhitespaceChar

    it('handles empty string', () => {
      expect(fn('')).toBe(null)
    })

    it('handles all whitespace', () => {
      expect(fn('  \t \n  \t')).toBe(null)
    })

    it('handles no leading whitespace', () => {
      expect(fn('abc')).toBe('a')
    })

    it('handles spaces', () => {
      expect(fn('  b')).toBe('b')
    })

    it('handles tabs', () => {
      expect(fn('\tc')).toBe('c')
    })

    it('handles newlines', () => {
      expect(fn('\nd')).toBe('d')
    })

    it('handles a mix', () => {
      expect(fn(' \n\t\ra \t\r\nb')).toBe('a')
    })
  })

  describe('isLastDeclarationCompleted', () => {
    const fn = isLastDeclarationCompleted

    it('handles all whitespace', () => {
      expect(fn('   \n \n \t \r')).toBe(true)
    })

    it('handles empty string', () => {
      expect(fn('')).toBe(true)
    })

    it('handles one-line css', () => {
      const prevCSS = 'display: block; color: red '
      expect(fn(prevCSS)).toBe(false)

      expect(fn(`${prevCSS};`)).toBe(true)
    })

    it('handles multi-line css', () => {
      const prevCSS = `
        display: block;
        color: red`
      expect(fn(prevCSS)).toBe(false)

      expect(fn(`${prevCSS};\n`)).toBe(true)
    })

    it('handles irregular css', () => {
      const prevCSS = `display   :  block
           ;      color:
             red   `
      expect(fn(prevCSS)).toBe(false)

      expect(
        fn(`${prevCSS}

                ;

          `)
      ).toBe(true)
    })

    it('handles declaration blocks', () => {
      const prevCSS = `
        @media screen and (max-width: 600px) {
          display: block;
          color: red;
        }
      `
      expect(fn(prevCSS)).toBe(true)
    })

    it('handles being inside a declaration block', () => {
      const prevCSS = `
        span {
          `
      expect(fn(prevCSS)).toBe(true)
    })

    it('handles being preceded by a comment', () => {
      const prevCSS1 = `
        display: block; /* stylelint-disable */
        `
      expect(fn(prevCSS1)).toBe(true)

      const prevCSS2 = `
      display: block;
      /* stylelint-disable */
        `
      expect(fn(prevCSS2)).toBe(true)

      const prevCSS3 = `
        display: /* stylelint-disable */
        `
      expect(fn(prevCSS3)).toBe(false)

      const prevCSS4 = `
      display:
      /* stylelint-disable */
        `
      expect(fn(prevCSS4)).toBe(false)
    })
  })

  describe('isStylelintComment', () => {
    const fn = isStylelintComment

    it('should match general block ignores', () => {
      expect(fn('stylelint-disable')).toBe(true)

      expect(fn('stylelint-enable')).toBe(true)
    })

    it('should match block ignores with any arguments', () => {
      expect(fn('stylelint-enable some-rule')).toBe(true)

      expect(fn('stylelint-disable asdfsafdsa-fdsafd9a0fd9sa0f asfd8af afdsa7f')).toBe(true)
    })

    it("shouldn't match line specific ignores", () => {
      expect(fn('stylelint-disable-line')).toBe(false)

      expect(fn('stylelint-disable-next-line')).toBe(false)
    })

    it('should handle whitespace in start and end', () => {
      expect(fn('   \tstylelint-disable   \t')).toBe(true)
    })
  })

  describe('fixIndentation', () => {
    // We only check the one-line case for now as the rest is be covered thoroughly in hard.test.js
    it('leaves one-line css alone', () => {
      const test1 = 'display: block;'
      expect(fixIndentation(test1).text).toBe(test1)

      const test2 = '       display: block;'
      expect(fixIndentation(test2).text).toBe(test2)

      const test3 = '\t\tdisplay:block;'
      expect(fixIndentation(test3).text).toBe(test3)
    })
  })

  describe('hasInterpolationTag', () => {
    const fn = hasInterpolationTag
    it('works for starting comment', () => {
      const expression = {
        leadingComments: [{ value: ' sc-block ' }],
        trailingComments: []
      }
      expect(fn(expression)).toBe(true)
    })

    it('correctly identifies lack of tag', () => {
      const expression = {
        leadingComments: [{ value: 'some test value' }],
        trailingComments: [{ value: 'second test value' }]
      }
      expect(fn(expression)).toBe(false)
    })
  })

  describe.skip('parseInterpolationTag', () => {
    const fn = parseInterpolationTag
    it('correctly works as dummy', () => {
      // This is temporary
      const expression1 = {
        leadingComments: [{ value: ' sc-block ' }],
        trailingComments: []
      }
      expect(fn(expression1)).toBe('-interpolation-tag-mixin: test')

      const expression2 = {
        leadingComments: [{ value: 'some test value' }],
        trailingComments: [{ value: 'second test value' }]
      }
      expect(fn(expression2)).toBe(undefined)
    })
  })

  describe('extrapolateShortenedCommand', () => {
    const fn = extrapolateShortenedCommand
    const commands = ['hello', 'heaven', 'command']

    it('handles correctly shortened commands', () => {
      expect(fn(commands, 'hel')).toBe('hello')
      expect(fn(commands, 'hea')).toBe('heaven')
      expect(fn(commands, 'c')).toBe('command')
      expect(fn(commands, 'comm')).toBe('command')
    })

    it('handles full commands', () => {
      expect(fn(commands, 'hello')).toBe('hello')
      expect(fn(commands, 'heaven')).toBe('heaven')
      expect(fn(commands, 'command')).toBe('command')
    })

    it('rejects ambigously shortened commands', () => {
      expect(fn.bind(this, commands, 'h')).toThrow()
      expect(fn.bind(this, commands, 'he', '/path/to/file', { line: 4, column: 6 })).toThrow(
        /path\/to\/file line 4 column 6:/
      )
    })

    it('rejects nonsense', () => {
      expect(fn(commands, 'nonsense')).toBeNull()
      expect(fn(commands, 'asdfasfd')).toBeNull()
      expect(fn(commands, 'x')).toBeNull()
    })
  })

  describe('extractScTagInformation', () => {
    const fn = extractScTagInformation
    it('handles normal Sc Tag', () => {
      expect(fn(' sc-block ')).toEqual({
        command: 'block',
        customPlaceholder: undefined
      })

      expect(fn('sc-selector')).toEqual({
        command: 'selector',
        customPlaceholder: undefined
      })

      expect(fn('sc-block   ')).toEqual({
        command: 'block',
        customPlaceholder: undefined
      })

      expect(fn('sc-block    ')).toEqual({
        command: 'block',
        customPlaceholder: undefined
      })
    })

    it('handles custom placeholder', () => {
      expect(fn(' sc-custom "placeholder test"  ')).toEqual({
        command: 'custom',
        customPlaceholder: 'placeholder test'
      })

      expect(fn(" sc-custom 'placeholder test'  ")).toEqual({
        command: 'custom',
        customPlaceholder: 'placeholder test'
      })
    })
  })
})
<commit_msg>Completed tests<commit_after>const interleave = require('../src/utils/tagged-template-literal').interleave
const hasInterpolationTag = require('../src/utils/tagged-template-literal').hasInterpolationTag
const parseInterpolationTag = require('../src/utils/tagged-template-literal').parseInterpolationTag
const extractScTagInformation = require('../src/utils/tagged-template-literal')
  .extractScTagInformation
const isLastDeclarationCompleted = require('../src/utils/general').isLastDeclarationCompleted
const nextNonWhitespaceChar = require('../src/utils/general').nextNonWhitespaceChar
const reverseString = require('../src/utils/general').reverseString
const isStylelintComment = require('../src/utils/general').isStylelintComment
const fixIndentation = require('../src/utils/general').fixIndentation
const extrapolateShortenedCommand = require('../src/utils/general').extrapolateShortenedCommand

describe('utils', () => {
  describe('interleave', () => {
    it('should return the value of the node if no interpolation exists', () => {
      const raw = 'color: blue;'
      const quasis = [
        {
          value: {
            raw
          }
        }
      ]
      expect(interleave(quasis, [])).toEqual(raw)
    })

    it('should variabelize an interpolation', () => {
      const quasis = [
        {
          value: {
            raw: '\n  display: block;\n  color: '
          }
        },
        {
          value: {
            raw: ';\n  background: blue;\n'
          }
        }
      ]
      const expressions = [
        {
          name: 'color'
        }
      ]
      expect(interleave(quasis, expressions)).toEqual(
        '\n  display: block;\n  color: $dummyValue;\n  background: blue;\n'
      )
    })

    it('converts interpolated expressions to dummy mixins', () => {
      const quasis = [
        {
          value: {
            raw: '\n  display: block;\n  '
          }
        },
        {
          value: {
            raw: '\n  background: blue;\n'
          }
        }
      ]
      const expressions = [
        {
          name: undefined
        }
      ]
      expect(interleave(quasis, expressions)).toEqual(
        '\n  display: block;\n  -styled-mixin0: dummyValue;\n  background: blue;\n'
      )
    })

    it('correctly converts several interpolations within a single property', () => {
      const quasis = [
        {
          value: {
            raw: '\n  display: block;\n  border: '
          }
        },
        {
          value: {
            raw: ' '
          }
        },
        {
          value: {
            raw: ' '
          }
        },
        {
          value: {
            raw: ';\n  background: blue;\n'
          }
        }
      ]
      const expressions = [
        {
          name: 'borderWidth'
        },
        {
          name: 'borderStyle'
        },
        {
          name: 'color'
        }
      ]
      expect(interleave(quasis, expressions)).toEqual(
        '\n  display: block;\n  border: $dummyValue $dummyValue $dummyValue;\n  background: blue;\n'
      )
    })

    it('correctly handles several interpolations in single line of css', () => {
      const quasis1 = [
        {
          value: {
            raw: '\n  display: '
          }
        },
        {
          value: {
            raw: '; background: '
          }
        },
        {
          value: {
            raw: ';\n'
          }
        }
      ]
      const expressions1 = [
        {
          name: 'display'
        },
        {
          name: 'background'
        }
      ]
      expect(interleave(quasis1, expressions1)).toEqual(
        '\n  display: $dummyValue; background: $dummyValue;\n'
      )

      const quasis2 = [
        {
          value: {
            raw: '\n  display: '
          }
        },
        {
          value: {
            raw: '; '
          }
        },
        {
          value: {
            raw: '\n'
          }
        }
      ]
      const expressions2 = [
        {
          name: 'display'
        },
        {
          name: undefined
        }
      ]
      expect(interleave(quasis2, expressions2)).toEqual(
        '\n  display: $dummyValue; -styled-mixin0: dummyValue;\n'
      )

      /**
       * It is important to also have this one as interleave would fail this if it simply
       * checked the previous quasi and not the previous processed text.
       * Here we also check the whole expression with and without a semi-colon in the quasi
       */
      const quasis3 = [
        {
          value: {
            raw: '\n  display: '
          }
        },
        {
          value: {
            raw: '; '
          }
        },
        {
          value: {
            raw: ' '
          }
        },
        {
          value: {
            raw: '\n'
          }
        }
      ]
      const expressions3 = [
        {
          name: 'display'
        },
        {
          name: undefined
        },
        {
          name: undefined
        }
      ]
      expect(interleave(quasis3, expressions3)).toEqual(
        '\n  display: $dummyValue; -styled-mixin0: dummyValue; -styled-mixin1: dummyValue;\n'
      )
    })
  })

  describe('reverseString', () => {
    const fn = reverseString

    it('reverses a string', () => {
      expect(fn('abcd')).toEqual('dcba')
    })

    it('handles empty string', () => {
      expect(fn('')).toEqual('')
    })
  })

  describe('nextNonWhitespaceChar', () => {
    const fn = nextNonWhitespaceChar

    it('handles empty string', () => {
      expect(fn('')).toBe(null)
    })

    it('handles all whitespace', () => {
      expect(fn('  \t \n  \t')).toBe(null)
    })

    it('handles no leading whitespace', () => {
      expect(fn('abc')).toBe('a')
    })

    it('handles spaces', () => {
      expect(fn('  b')).toBe('b')
    })

    it('handles tabs', () => {
      expect(fn('\tc')).toBe('c')
    })

    it('handles newlines', () => {
      expect(fn('\nd')).toBe('d')
    })

    it('handles a mix', () => {
      expect(fn(' \n\t\ra \t\r\nb')).toBe('a')
    })
  })

  describe('isLastDeclarationCompleted', () => {
    const fn = isLastDeclarationCompleted

    it('handles all whitespace', () => {
      expect(fn('   \n \n \t \r')).toBe(true)
    })

    it('handles empty string', () => {
      expect(fn('')).toBe(true)
    })

    it('handles one-line css', () => {
      const prevCSS = 'display: block; color: red '
      expect(fn(prevCSS)).toBe(false)

      expect(fn(`${prevCSS};`)).toBe(true)
    })

    it('handles multi-line css', () => {
      const prevCSS = `
        display: block;
        color: red`
      expect(fn(prevCSS)).toBe(false)

      expect(fn(`${prevCSS};\n`)).toBe(true)
    })

    it('handles irregular css', () => {
      const prevCSS = `display   :  block
           ;      color:
             red   `
      expect(fn(prevCSS)).toBe(false)

      expect(
        fn(`${prevCSS}

                ;

          `)
      ).toBe(true)
    })

    it('handles declaration blocks', () => {
      const prevCSS = `
        @media screen and (max-width: 600px) {
          display: block;
          color: red;
        }
      `
      expect(fn(prevCSS)).toBe(true)
    })

    it('handles being inside a declaration block', () => {
      const prevCSS = `
        span {
          `
      expect(fn(prevCSS)).toBe(true)
    })

    it('handles being preceded by a comment', () => {
      const prevCSS1 = `
        display: block; /* stylelint-disable */
        `
      expect(fn(prevCSS1)).toBe(true)

      const prevCSS2 = `
      display: block;
      /* stylelint-disable */
        `
      expect(fn(prevCSS2)).toBe(true)

      const prevCSS3 = `
        display: /* stylelint-disable */
        `
      expect(fn(prevCSS3)).toBe(false)

      const prevCSS4 = `
      display:
      /* stylelint-disable */
        `
      expect(fn(prevCSS4)).toBe(false)
    })
  })

  describe('isStylelintComment', () => {
    const fn = isStylelintComment

    it('should match general block ignores', () => {
      expect(fn('stylelint-disable')).toBe(true)

      expect(fn('stylelint-enable')).toBe(true)
    })

    it('should match block ignores with any arguments', () => {
      expect(fn('stylelint-enable some-rule')).toBe(true)

      expect(fn('stylelint-disable asdfsafdsa-fdsafd9a0fd9sa0f asfd8af afdsa7f')).toBe(true)
    })

    it("shouldn't match line specific ignores", () => {
      expect(fn('stylelint-disable-line')).toBe(false)

      expect(fn('stylelint-disable-next-line')).toBe(false)
    })

    it('should handle whitespace in start and end', () => {
      expect(fn('   \tstylelint-disable   \t')).toBe(true)
    })
  })

  describe('fixIndentation', () => {
    // We only check the one-line case for now as the rest is be covered thoroughly in hard.test.js
    it('leaves one-line css alone', () => {
      const test1 = 'display: block;'
      expect(fixIndentation(test1).text).toBe(test1)

      const test2 = '       display: block;'
      expect(fixIndentation(test2).text).toBe(test2)

      const test3 = '\t\tdisplay:block;'
      expect(fixIndentation(test3).text).toBe(test3)
    })
  })

  describe('hasInterpolationTag', () => {
    const fn = hasInterpolationTag
    it('works for starting comment', () => {
      const expression = {
        leadingComments: [{ value: ' sc-block ' }],
        trailingComments: []
      }
      expect(fn(expression)).toBe(true)
    })

    it('correctly identifies lack of tag', () => {
      const expression = {
        leadingComments: [{ value: 'some test value' }],
        trailingComments: [{ value: 'second test value' }]
      }
      expect(fn(expression)).toBe(false)
    })

    it('handles tag not being first comment', () => {
      const expression = {
        leadingComments: [{ value: 'some test value' }, { value: 'second test value' }],
        trailingComments: [{ value: '  sc-s' }]
      }
      expect(fn(expression)).toBe(true)
    })
  })

  describe('parseInterpolationTag', () => {
    const fn = parseInterpolationTag
    const prepExpression = command => ({
      leadingComments: [
        { value: 'some test comment' },
        {
          value: `sc-${command}`,
          loc: {
            start: {
              line: 1,
              column: 3
            }
          }
        }
      ],
      trailingComments: []
    })
    it('handles the API', () => {
      const refExpression = prepExpression('ref')
      expect(fn(refExpression, 1, 'path/to/file')).toBe('div')

      const selectorExpression = prepExpression('selector')
      expect(fn(selectorExpression, 1, 'path/to/file')).toBe('div')

      const blockExpression = prepExpression('block')
      expect(fn(blockExpression, 1, 'path/to/file')).toBe('-styled-mixin1: dummyValue;')

      const declarationExpression = prepExpression('declaration')
      expect(fn(declarationExpression, 1, 'path/to/file')).toBe('-styled-mixin1: dummyValue;')

      const propertyExpression = prepExpression('property')
      expect(fn(propertyExpression, 1, 'path/to/file')).toBe('-styled-mixin1')

      const valueExpression = prepExpression('value')
      expect(fn(valueExpression, 1, 'path/to/file')).toBe('$dummyValue')

      const customExpression = prepExpression('custom " my test placeholder"')
      expect(fn(customExpression, 1, 'path/to/file')).toBe(' my test placeholder')
    })

    it('throws on invalid tag', () => {
      const invalidExpression = prepExpression('invalid')
      expect(fn.bind(null, invalidExpression, 1, 'path/to/file')).toThrow(
        /path\/to\/file line 1 column 3:\n.*invalid sc- tag/
      )
    })
  })

  describe('extrapolateShortenedCommand', () => {
    const fn = extrapolateShortenedCommand
    const commands = ['hello', 'heaven', 'command']

    it('handles correctly shortened commands', () => {
      expect(fn(commands, 'hel')).toBe('hello')
      expect(fn(commands, 'hea')).toBe('heaven')
      expect(fn(commands, 'c')).toBe('command')
      expect(fn(commands, 'comm')).toBe('command')
    })

    it('handles full commands', () => {
      expect(fn(commands, 'hello')).toBe('hello')
      expect(fn(commands, 'heaven')).toBe('heaven')
      expect(fn(commands, 'command')).toBe('command')
    })

    it('rejects ambigously shortened commands', () => {
      expect(fn.bind(this, commands, 'h')).toThrow()
      expect(fn.bind(this, commands, 'he', '/path/to/file', { line: 4, column: 6 })).toThrow(
        /path\/to\/file line 4 column 6:/
      )
    })

    it('rejects nonsense', () => {
      expect(fn(commands, 'nonsense')).toBeNull()
      expect(fn(commands, 'asdfasfd')).toBeNull()
      expect(fn(commands, 'x')).toBeNull()
    })
  })

  describe('extractScTagInformation', () => {
    const fn = extractScTagInformation
    it('handles normal Sc Tag', () => {
      expect(fn(' sc-block ')).toEqual({
        command: 'block',
        customPlaceholder: undefined
      })

      expect(fn('sc-selector')).toEqual({
        command: 'selector',
        customPlaceholder: undefined
      })

      expect(fn('sc-block   ')).toEqual({
        command: 'block',
        customPlaceholder: undefined
      })

      expect(fn('sc-block    ')).toEqual({
        command: 'block',
        customPlaceholder: undefined
      })
    })

    it('handles custom placeholder', () => {
      expect(fn(' sc-custom "placeholder test"  ')).toEqual({
        command: 'custom',
        customPlaceholder: 'placeholder test'
      })

      expect(fn(" sc-custom 'placeholder test'  ")).toEqual({
        command: 'custom',
        customPlaceholder: 'placeholder test'
      })
    })
  })
})
<|endoftext|>


<commit_before>var validation = require('../lib/utils/validation');
var expect = require('chai').expect;

var validData = [
  [0.827106,	32699512],
  [1.275007,	50550730],
  [1.435098,	56214886],
  [1.597657,	63010730],
  [1.748174,	68767902],
  [1.904614,	75539606]
];

describe('data validation', function() {
  it('should throw a TypeError when attribute is null', function() {
    var func = function() {
			validation.validate(null);
		};
    expect(func).to.throw(TypeError);
    expect(func).to.throw(/Attribute `values` must be a array/);
  });

  it('should throw a TypeError when attribute is a string', function() {
    var func = function() {
			validation.validate("");
		};
    expect(func).to.throw(TypeError);
    expect(func).to.throw(/Attribute `values` must be a array/);
  });

  it('should throw a TypeError when attribute is not set', function() {
    var func = function() {
			validation.validate();
		};
    expect(func).to.throw(TypeError);
    expect(func).to.throw(/Attribute `values` must be a array/);
  });

  it('should throw a TypeError when reciving a object', function() {
    var func = function() {
			validation.validate({});
		};
    expect(func).to.throw(TypeError);
    expect(func).to.throw(/Attribute `values` must be a array/);
  });

  it('should throw a TypeError when reciving a empty array', function() {
    var func = function() {
			validation.validate([]);
		};
    expect(func).to.throw(TypeError);
    expect(func).to.throw(/Attribute `values` cannot be a empty/);
  });

  it('should throw a TypeError when reciving a non 2 dimensional array', function() {
    var func = function() {
			validation.validate([1, 1, 3]);
		};
    expect(func).to.throw(TypeError);
    expect(func).to.throw(/Attribute `value` must be a array/);
  });

  it('should throw a TypeError when reciving a non full 2 dimensional array', function() {
    var func = function() {
			validation.validate([[1, 2], 1, 3]);
		};
    expect(func).to.throw(TypeError);
    expect(func).to.throw(/Attribute `value` must be a array/);
  });

  it('should throw a TypeError when reciving full 2 dimensional array with numbers of elements diferent than 2', function() {
    var func = function() {
			validation.validate([[1, 2], [1], [3, 2]]);
		};
    expect(func).to.throw(TypeError);
    expect(func).to.throw(/Attribute `value` must have 2 elements/);
  });

  it('should not throw a TypeError when reciving a valid attribute', function() {
    var func = function() {
			validation.validate(validData);
		};
    expect(func).not.to.throw(TypeError);
    expect(func).not.to.throw(Error);
  });
});
<commit_msg>Fixing 'indent'<commit_after>var validation = require('../lib/utils/validation');
var expect = require('chai').expect;

var validData = [
  [0.827106,	32699512],
  [1.275007,	50550730],
  [1.435098,	56214886],
  [1.597657,	63010730],
  [1.748174,	68767902],
  [1.904614,	75539606]
];

describe('data validation', function() {
  it('should throw a TypeError when attribute is null', function() {
    var func = function() {
      validation.validate(null);
    };
    expect(func).to.throw(TypeError);
    expect(func).to.throw(/Attribute `values` must be a array/);
  });

  it('should throw a TypeError when attribute is a string', function() {
    var func = function() {
      validation.validate("");
    };
    expect(func).to.throw(TypeError);
    expect(func).to.throw(/Attribute `values` must be a array/);
  });

  it('should throw a TypeError when attribute is not set', function() {
    var func = function() {
      validation.validate();
    };
    expect(func).to.throw(TypeError);
    expect(func).to.throw(/Attribute `values` must be a array/);
  });

  it('should throw a TypeError when reciving a object', function() {
    var func = function() {
      validation.validate({});
    };
    expect(func).to.throw(TypeError);
    expect(func).to.throw(/Attribute `values` must be a array/);
  });

  it('should throw a TypeError when reciving a empty array', function() {
    var func = function() {
      validation.validate([]);
    };
    expect(func).to.throw(TypeError);
    expect(func).to.throw(/Attribute `values` cannot be a empty/);
  });

  it('should throw a TypeError when reciving a non 2 dimensional array', function() {
    var func = function() {
      validation.validate([1, 1, 3]);
    };
    expect(func).to.throw(TypeError);
    expect(func).to.throw(/Attribute `value` must be a array/);
  });

  it('should throw a TypeError when reciving a non full 2 dimensional array', function() {
    var func = function() {
      validation.validate([[1, 2], 1, 3]);
    };
    expect(func).to.throw(TypeError);
    expect(func).to.throw(/Attribute `value` must be a array/);
  });

  it('should throw a TypeError when reciving full 2 dimensional array with numbers of elements diferent than 2', function() {
    var func = function() {
      validation.validate([[1, 2], [1], [3, 2]]);
    };
    expect(func).to.throw(TypeError);
    expect(func).to.throw(/Attribute `value` must have 2 elements/);
  });

  it('should not throw a TypeError when reciving a valid attribute', function() {
    var func = function() {
      validation.validate(validData);
    };
    expect(func).not.to.throw(TypeError);
    expect(func).not.to.throw(Error);
  });
});
<|endoftext|>


<commit_before>/*!
 * jqMVC.js (C) 2015 jqMVC.js Developers, MIT license http://github.com/r3wt/jqMVC.git
 * @author Garrett R Morris (https://github.com/r3wt)
 * @package jqMVC.js
 * @license MIT
 * @version 0.1.2
 * contains heavily modified code originally written by camilo tapia https://github.com/camme/jquery-router-plugin
 */

;!(function($){

    // you no touchy
    var app = {};

    // fix for browsers that dont have location.origin
    if (!window.location.origin) {
        window.location.origin = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: '');
    }

    // random shit, probably deprecated eventually
    var events = [];
    var services = {};
    var controller = null;

    //internal utilities

    function getPath()
    {
        if(window.hasOwnProperty('app_path')){
            return app_path.replace(/\/+/g, '/');//possibly unsafe.
        }
        return '/';
    }

    function bindStateEvents()
    {
        eventAdded = true;

        // default value telling router that we havent replaced the url from a hash. yet.
        router.fromHash = false;

        if (hasPushState) {
            if (location.hash.indexOf("#!/") === 0) {
                // replace the state
                var url = location.pathname + location.hash.replace(/^#!\//gi, "");
                history.replaceState({}, "", url);
                router.fromHash = true;
            }

            $(window).bind("popstate", handleRoutes);

        } else if (hasHashState) {
            $(window).bind("hashchange.router", handleRoutes);
        } else {
            // if no events are available we use a timer to check periodically for changes in the url
            setInterval(function(){
                if (location.href != currentUsedUrl) {
                    handleRoutes();
                    currentUsedUrl = location.href;
                }
            }, 500);
        }

    };

    function checkRoutes()
    {
        var currentUrl = parseUrl(location.pathname);
        // check if a route exists.
        var actionList = getParameters(currentUrl);
        console.log(actionList);
        if(actionList.length === 0){
            emit('notFound');
        }else{
            for(var i = 0; i < actionList.length; i++)
            {
                var route = actionList[i].route;
                var args = [];
                for(var prop in actionList[i].data){
                    args.push(actionList[i].data[prop]);
                }
                if(typeof route.callback !== 'function' && route.callback.isArray()){
                    for(var j=0;j<route.callback.length;j++){
                        if(j==route.callback.length - 1){
                            //last callback is always the route function
                            route.callback[j].apply(this,args);
                        }else{
                            //just a middleware.
                            var result = route.callback[j].apply(this);
							if(typeof result !== "undefined"){
								break;//halt execution
								// reasonable to assume that the middleware has
								// already continued executing some new path.

							}
                        }
                    }
                }else{
                    route.callback.apply(this,args);
                }
            }
        }
    };

    function parseUrl(url)
    {
        var currentUrl = url ? url : location.pathname;

        currentUrl = decodeURI(currentUrl);

        // if no pushstate is availabe we have to use the hash
        if (!hasPushState) {
            if (location.hash.indexOf("#!/") === 0) {
                currentUrl += location.hash.substring(3);
            } else {
            return '';
            }
        }

        // and if the last character is a slash, we just remove it

        if(currentUrl.slice(-1) == '/'){
            currentUrl = currentUrl.substring(0, currentUrl.length-1);
        }

        return currentUrl;
    };

    function getParameters(url)
    {
        var dataList = [];
        for (var i = 0; i < routeList.length; i++) {
            var route = routeList[i];
            if (route.type == "regexp") {
                var result = url.match(route.route);
                if (result) {
                    dataList.push({
                        route: route,
                        data: {matches: result}
                    });

                    // break after first hit
                    break;
                }
            } else {
                var currentUrlParts = url.split("/");
                var routeParts = route.route.split("/");
                if (routeParts.length == currentUrlParts.length) {
                    var data = {};
                    var matched = true;
                    var matchCounter = 0;

                    for(var j = 0; j < routeParts.length; j++) {
                        if (routeParts[j].indexOf(":") === 0) {
                            //its a parameter
                            data[routeParts[j].substring(1)] = decodeURI(currentUrlParts[j]);
                            matchCounter++;
                        } else {
                            //not a parameter, ensure the segments match.
                            if (routeParts[j] == currentUrlParts[j]) {
                                matchCounter++;
                            }
                        }
                    }

                    // we've an exact match. break
                    if (routeParts.length == matchCounter) {
                        dataList.push({
                            route: route,
                            data: data
                        });
                        router.currentParameters = data;
                        break;
                    }
                }
            }
        }

        return dataList;
    };

    function handleRoutes(e)
    {
        if (e != null && e.originalEvent && e.originalEvent.state !== undefined) {
            checkRoutes();
        }
        else if (hasHashState) {
            checkRoutes();
        }
        else if (!hasHashState && !hasPushState) {
            checkRoutes();
        }
    };

    function debugmsg(msg)
    {
        if (debug) {
            window.console && console.log('$.jqMVC :: ' + msg);
        }
    };

    function emit(event,eventData)
    {
        debugmsg('emit -> `'+event+'`');
        $(app).trigger(event,eventData);
    };

    function bind_href()
    {
        emit('on.bindhref');
        app.on('click','a[data-href]',function(e){
            e.preventDefault();
            emit('before.go');
            app.go( getPath().replace(/\/+$/, '')+$(this).data('href') ,'Loading');
            return false;
        });
    };

    //end internal utilities

    //router
    var hasPushState = (history && history.pushState);
    var hasHashState = !hasPushState && ("onhashchange" in window) && false;
    var routeList = [];
    var eventAdded = false;
    var currentUsedUrl = location.href;
    var firstRoute = true;
    var firstRun = false;
    var router = {};

    router.currentId = "";
    router.currentParameters = {};

    router.capabilities = {
        hash: hasHashState,
        pushState: hasPushState,
        timer: !hasHashState && !hasPushState
    };

    router.checkRoute = function(url)
    {
        return getParameters(parseUrl(url)).length > 0;
    };

    // get the current parameters for either a specified url or the current one if parameters is ommited
    router.parameters = function(url)
    {
        // parse the url so that we handle a unified url
        var currentUrl = parseUrl(url);

        // get the list of actions for the current url
        var list = getParameters(currentUrl);

        // if the list is empty, return an empty object
        if (list.length == 0) {
            router.currentParameters = {};
        } else {
            router.currentParameters = list[0].data;// if we got results, return the first one. at least for now
        }

        return router.currentParameters;
    };

    app.path = function()
    {
        var args = [];
        Array.prototype.push.apply( args, arguments );
        var route = args.shift(); //first arg is always the path
        var callback = args.pop(); //last arg is the callback
        var middleware = args; //safe to assume remaining arguments are middleware

        route = getPath().replace(/\/+$/, '') + route;
        var isRegExp = typeof route == "object";

        if (!isRegExp) {
            // remove the last slash to unifiy all routes
            if (route.lastIndexOf("/") == route.length - 1) {
            route = route.substring(0, route.length - 1);
            }
            // if the routes where created with an absolute url ,we have to remove the absolut part anyway, since we cant change that much
            route = route.replace(location.protocol + "//", "").replace(location.hostname, "");
        }

        var obj = {};

        if(middleware.length > 0){
            obj.callback = middleware;
            obj.callback.push(callback);
        }

        routeList.push({
            route: route,
            callback: callback,
            type: isRegExp ? "regexp" : "string",
        });
        return app;
    };
    //endrouter

    //middleware stack
    var stack = {};

    stack.items = [];

    stack.next = function(){
        stack.items.shift().call($,stack);
    };

    app.add = function(callable){
        stack.items.push(callable);
        return app;
    };

    app.run = function()
    {
        if(!firstRun){
            emit('firstRun');
            firstRun = true;
            app.add(function(){
                app.go(location.href);
            });
            stack.next();
        }
        app.run = function(){};
        return app;
    };
    //end middleware stack

    //event related

    app.trigger = function(event,eventData){
        emit(event,eventData);
        return app;
    };

    app.listen = function(event,callback)
    {
        $(app).bind(event,callback);
        return app;
    };

    app.on = function(event,target,callback)
    {
        if ($.isWindow(target)) {
            $(window).on(event,callback);
        } else {
            $(document).on(event,target,callback);
        }
        events.push({
            event:event,
            target:target,
            callback:callback
        });
        return app;
    };

    app.off = function(event,target,callback)
    {
        if ($.isWindow(target)) {
            $(window).unbind(event,callback);
        } else {
            $(document).off(event,target,callback);
        }
        return app;
    };

    //end event related

    app.loadModule = function(path){
        var path = window.location.origin + getPath() +  module_path + path;
        var s = document.createElement('script');
        s.setAttribute('src', path);
        s.className = 'jqMVCmodule';
        s.async = true;
        s.onload = function(){};
        document.body.appendChild( s );
        return app;
    };

    app.go = function(url)
    {
        if (hasPushState) {
            history.pushState({}, null, url);
            checkRoutes();
            if(!eventAdded){
                bindStateEvents();
            }
        } else {
            if(!eventAdded){
                bindStateEvents();
            }
            // remove part of url that we dont use
            url = url.replace(location.protocol + "//", "").replace(location.hostname, "");
            var hash = url.replace(location.pathname, "");
            if (hash.indexOf("!") < 0)
            {
                hash = "!/" + hash;
            }
            location.hash = hash;
        }
        return app;
    };

    app.data = function(args)
    {
        for(var prop in args){
            window[prop] = args[prop];
        }
        return app;
    };

    app.clean = function()
    {
        if (events.length > 0) {
            for (var i=0; i<events.length;i++) {
                app.off(events[i].event,events[i].target,events[i].callback);
            }
            events.length = 0;//truncate array.
        }

        if(controller !== null){
            if(controller.hasOwnProperty('destroy')){
                if(typeof controller.destroy === "function"){
                    controller.destroy();
                }
            }
            controller = null;
        }

        if($('script.jqMVCctrl').length > 0){
            $('script.jqMVCctrl').remove();
        }
        emit('on.clean');
        return app;
    };

    app.controller = function(path)
    {
        var path = window.location.origin + getPath() + ctrl_path + path;
        app.clean();
        var s = document.createElement('script');
        s.setAttribute('src', path);
        s.className = 'jqMVCctrl';
        var z = null;
        s.onload = function(){
            emit('on.controller');
            z = $ctrl;
            z.invoke();
        };
        controller = z;
        document.body.appendChild( s );
        return app;
    };

    app.addSvc = function(name,callback)
    {
        var obj = {};
        obj[name] = callback;
        $.extend(true,services,obj);
        return app;
    };

    app.svc = function(name)
    {
        if (typeof services[name] === "function") {
            var svc = Array.prototype.shift.apply(arguments);
            services[svc].apply(this, arguments);
            emit('on.service');
        }
        return app;
    };

    //merge infinite number of arrays.
    app.merge = function(){
        var args = [],
        result = [],
        unique = function(a,b) {
            var a = a.concat(b);
            for(var i=0; i<a.length; ++i) {
                for(var j=i+1; j<a.length; ++j) {
                    if(a[i] === a[j])
                        a.splice(j--, 1);
                }
            }
            return a;
        };
        Array.prototype.push.apply(args,arguments);
        for(k=0;k<args.length;k++){
            result = unique(result,args[k]);
        }
        return result;
    };

    //notificatins related
    var notify = {
        alert:function(message){
            alert(message);
        },
        confirm:function(message,callback){
            if(confirm(message)){
                callback.apply(this);
            }
        }
    };

    app.setNotifications = function(obj){
        notify = obj;
        return app;
    };

    app.alert = function()
    {
        notify.alert.apply(this,arguments);
        return app;
    };

    app.confirm = function()
    {
        notify.confirm.apply(this,arguments);
        return app;
    };

    //progress related
    var progress = {
        start: function(){

        },
        stop: function(){
        }
    };

    app.setProgress = function(obj){
        progress = obj;
        return app;
    }

    app.before = function()
    {
        progress.start();
        return app;
    };

    app.done = function()
    {
        emit('on.done');
        progress.stop();
        bind_href();
        return app;
    };

    //all view related functions here
    var view = {
        render: function(){
            app.done();
            throw 'You must implement a view with `setView()` before you can render templates';
        }
    };

    app.setView = function(obj){
        view = obj;
        return app;
    };

    app.render = function()
    {
        view.render.apply(this,arguments);
    };

    $.jqMVC = app;
}(jQuery));<commit_msg>formatting<commit_after>/*!
 * jqMVC.js (C) 2015 jqMVC.js Developers, MIT license http://github.com/r3wt/jqMVC.git
 * @author Garrett R Morris (https://github.com/r3wt)
 * @package jqMVC.js
 * @license MIT
 * @version 0.1.2
 * contains heavily modified code originally written by camilo tapia https://github.com/camme/jquery-router-plugin
 */

;!(function($){

    // you no touchy
    var app = {};

    // fix for browsers that dont have location.origin
    if (!window.location.origin) {
        window.location.origin = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: '');
    }

    // random shit, probably deprecated eventually
    var events = [];
    var services = {};
    var controller = null;

    //internal utilities

    function getPath()
    {
        if(window.hasOwnProperty('app_path')){
            return app_path.replace(/\/+/g, '/');//possibly unsafe.
        }
        return '/';
    }

    function bindStateEvents()
    {
        eventAdded = true;

        // default value telling router that we havent replaced the url from a hash. yet.
        router.fromHash = false;

        if (hasPushState) {
            if (location.hash.indexOf("#!/") === 0) {
                // replace the state
                var url = location.pathname + location.hash.replace(/^#!\//gi, "");
                history.replaceState({}, "", url);
                router.fromHash = true;
            }

            $(window).bind("popstate", handleRoutes);

        } else if (hasHashState) {
            $(window).bind("hashchange.router", handleRoutes);
        } else {
            // if no events are available we use a timer to check periodically for changes in the url
            setInterval(function(){
                if (location.href != currentUsedUrl) {
                    handleRoutes();
                    currentUsedUrl = location.href;
                }
            }, 500);
        }

    };

    function checkRoutes()
    {
        var currentUrl = parseUrl(location.pathname);
        // check if a route exists.
        var actionList = getParameters(currentUrl);
        console.log(actionList);
        if(actionList.length === 0){
            emit('notFound');
        }else{
            for(var i = 0; i < actionList.length; i++)
            {
                var route = actionList[i].route;
                var args = [];
                for(var prop in actionList[i].data){
                    args.push(actionList[i].data[prop]);
                }
                if(typeof route.callback !== 'function' && route.callback.isArray()){
                    for(var j=0;j<route.callback.length;j++){
                        if(j==route.callback.length - 1){
                            //last callback is always the route function
                            route.callback[j].apply(this,args);
                        }else{
                            //just a middleware.
                            var result = route.callback[j].apply(this);
                            if(typeof result !== "undefined"){
                                break;//halt execution
                                // reasonable to assume that the middleware has
                                // already continued executing some new path.

                            }
                        }
                    }
                }else{
                    route.callback.apply(this,args);
                }
            }
        }
    };

    function parseUrl(url)
    {
        var currentUrl = url ? url : location.pathname;

        currentUrl = decodeURI(currentUrl);

        // if no pushstate is availabe we have to use the hash
        if (!hasPushState) {
            if (location.hash.indexOf("#!/") === 0) {
                currentUrl += location.hash.substring(3);
            } else {
            return '';
            }
        }

        // and if the last character is a slash, we just remove it

        if(currentUrl.slice(-1) == '/'){
            currentUrl = currentUrl.substring(0, currentUrl.length-1);
        }

        return currentUrl;
    };

    function getParameters(url)
    {
        var dataList = [];
        for (var i = 0; i < routeList.length; i++) {
            var route = routeList[i];
            if (route.type == "regexp") {
                var result = url.match(route.route);
                if (result) {
                    dataList.push({
                        route: route,
                        data: {matches: result}
                    });

                    // break after first hit
                    break;
                }
            } else {
                var currentUrlParts = url.split("/");
                var routeParts = route.route.split("/");
                if (routeParts.length == currentUrlParts.length) {
                    var data = {};
                    var matched = true;
                    var matchCounter = 0;

                    for(var j = 0; j < routeParts.length; j++) {
                        if (routeParts[j].indexOf(":") === 0) {
                            //its a parameter
                            data[routeParts[j].substring(1)] = decodeURI(currentUrlParts[j]);
                            matchCounter++;
                        } else {
                            //not a parameter, ensure the segments match.
                            if (routeParts[j] == currentUrlParts[j]) {
                                matchCounter++;
                            }
                        }
                    }

                    // we've an exact match. break
                    if (routeParts.length == matchCounter) {
                        dataList.push({
                            route: route,
                            data: data
                        });
                        router.currentParameters = data;
                        break;
                    }
                }
            }
        }

        return dataList;
    };

    function handleRoutes(e)
    {
        if (e != null && e.originalEvent && e.originalEvent.state !== undefined) {
            checkRoutes();
        }
        else if (hasHashState) {
            checkRoutes();
        }
        else if (!hasHashState && !hasPushState) {
            checkRoutes();
        }
    };

    function debugmsg(msg)
    {
        if (debug) {
            window.console && console.log('$.jqMVC :: ' + msg);
        }
    };

    function emit(event,eventData)
    {
        debugmsg('emit -> `'+event+'`');
        $(app).trigger(event,eventData);
    };

    function bind_href()
    {
        emit('on.bindhref');
        app.on('click','a[data-href]',function(e){
            e.preventDefault();
            emit('before.go');
            app.go( getPath().replace(/\/+$/, '')+$(this).data('href') ,'Loading');
            return false;
        });
    };

    //end internal utilities

    //router
    var hasPushState = (history && history.pushState);
    var hasHashState = !hasPushState && ("onhashchange" in window) && false;
    var routeList = [];
    var eventAdded = false;
    var currentUsedUrl = location.href;
    var firstRoute = true;
    var firstRun = false;
    var router = {};

    router.currentId = "";
    router.currentParameters = {};

    router.capabilities = {
        hash: hasHashState,
        pushState: hasPushState,
        timer: !hasHashState && !hasPushState
    };

    router.checkRoute = function(url)
    {
        return getParameters(parseUrl(url)).length > 0;
    };

    // get the current parameters for either a specified url or the current one if parameters is ommited
    router.parameters = function(url)
    {
        // parse the url so that we handle a unified url
        var currentUrl = parseUrl(url);

        // get the list of actions for the current url
        var list = getParameters(currentUrl);

        // if the list is empty, return an empty object
        if (list.length == 0) {
            router.currentParameters = {};
        } else {
            router.currentParameters = list[0].data;// if we got results, return the first one. at least for now
        }

        return router.currentParameters;
    };

    app.path = function()
    {
        var args = [];
        Array.prototype.push.apply( args, arguments );
        var route = args.shift(); //first arg is always the path
        var callback = args.pop(); //last arg is the callback
        var middleware = args; //safe to assume remaining arguments are middleware

        route = getPath().replace(/\/+$/, '') + route;
        var isRegExp = typeof route == "object";

        if (!isRegExp) {
            // remove the last slash to unifiy all routes
            if (route.lastIndexOf("/") == route.length - 1) {
            route = route.substring(0, route.length - 1);
            }
            // if the routes where created with an absolute url ,we have to remove the absolut part anyway, since we cant change that much
            route = route.replace(location.protocol + "//", "").replace(location.hostname, "");
        }

        var obj = {};

        if(middleware.length > 0){
            obj.callback = middleware;
            obj.callback.push(callback);
        }

        routeList.push({
            route: route,
            callback: callback,
            type: isRegExp ? "regexp" : "string",
        });
        return app;
    };
    //endrouter

    //middleware stack
    var stack = {};

    stack.items = [];

    stack.next = function(){
        stack.items.shift().call($,stack);
    };

    app.add = function(callable){
        stack.items.push(callable);
        return app;
    };

    app.run = function()
    {
        if(!firstRun){
            emit('firstRun');
            firstRun = true;
            app.add(function(){
                app.go(location.href);
            });
            stack.next();
        }
        app.run = function(){};
        return app;
    };
    //end middleware stack

    //event related

    app.trigger = function(event,eventData){
        emit(event,eventData);
        return app;
    };

    app.listen = function(event,callback)
    {
        $(app).bind(event,callback);
        return app;
    };

    app.on = function(event,target,callback)
    {
        if ($.isWindow(target)) {
            $(window).on(event,callback);
        } else {
            $(document).on(event,target,callback);
        }
        events.push({
            event:event,
            target:target,
            callback:callback
        });
        return app;
    };

    app.off = function(event,target,callback)
    {
        if ($.isWindow(target)) {
            $(window).unbind(event,callback);
        } else {
            $(document).off(event,target,callback);
        }
        return app;
    };

    //end event related

    app.loadModule = function(path){
        var path = window.location.origin + getPath() +  module_path + path;
        var s = document.createElement('script');
        s.setAttribute('src', path);
        s.className = 'jqMVCmodule';
        s.async = true;
        s.onload = function(){};
        document.body.appendChild( s );
        return app;
    };

    app.go = function(url)
    {
        if (hasPushState) {
            history.pushState({}, null, url);
            checkRoutes();
            if(!eventAdded){
                bindStateEvents();
            }
        } else {
            if(!eventAdded){
                bindStateEvents();
            }
            // remove part of url that we dont use
            url = url.replace(location.protocol + "//", "").replace(location.hostname, "");
            var hash = url.replace(location.pathname, "");
            if (hash.indexOf("!") < 0)
            {
                hash = "!/" + hash;
            }
            location.hash = hash;
        }
        return app;
    };

    app.data = function(args)
    {
        for(var prop in args){
            window[prop] = args[prop];
        }
        return app;
    };

    app.clean = function()
    {
        if (events.length > 0) {
            for (var i=0; i<events.length;i++) {
                app.off(events[i].event,events[i].target,events[i].callback);
            }
            events.length = 0;//truncate array.
        }

        if(controller !== null){
            if(controller.hasOwnProperty('destroy')){
                if(typeof controller.destroy === "function"){
                    controller.destroy();
                }
            }
            controller = null;
        }

        if($('script.jqMVCctrl').length > 0){
            $('script.jqMVCctrl').remove();
        }
        emit('on.clean');
        return app;
    };

    app.controller = function(path)
    {
        var path = window.location.origin + getPath() + ctrl_path + path;
        app.clean();
        var s = document.createElement('script');
        s.setAttribute('src', path);
        s.className = 'jqMVCctrl';
        var z = null;
        s.onload = function(){
            emit('on.controller');
            z = $ctrl;
            z.invoke();
        };
        controller = z;
        document.body.appendChild( s );
        return app;
    };

    app.addSvc = function(name,callback)
    {
        var obj = {};
        obj[name] = callback;
        $.extend(true,services,obj);
        return app;
    };

    app.svc = function(name)
    {
        if (typeof services[name] === "function") {
            var svc = Array.prototype.shift.apply(arguments);
            services[svc].apply(this, arguments);
            emit('on.service');
        }
        return app;
    };

    //merge infinite number of arrays.
    app.merge = function(){
        var args = [],
        result = [],
        unique = function(a,b) {
            var a = a.concat(b);
            for(var i=0; i<a.length; ++i) {
                for(var j=i+1; j<a.length; ++j) {
                    if(a[i] === a[j])
                        a.splice(j--, 1);
                }
            }
            return a;
        };
        Array.prototype.push.apply(args,arguments);
        for(k=0;k<args.length;k++){
            result = unique(result,args[k]);
        }
        return result;
    };

    //notificatins related
    var notify = {
        alert:function(message){
            alert(message);
        },
        confirm:function(message,callback){
            if(confirm(message)){
                callback.apply(this);
            }
        }
    };

    app.setNotifications = function(obj){
        notify = obj;
        return app;
    };

    app.alert = function()
    {
        notify.alert.apply(this,arguments);
        return app;
    };

    app.confirm = function()
    {
        notify.confirm.apply(this,arguments);
        return app;
    };

    //progress related
    var progress = {
        start: function(){

        },
        stop: function(){
        }
    };

    app.setProgress = function(obj){
        progress = obj;
        return app;
    }

    app.before = function()
    {
        progress.start();
        return app;
    };

    app.done = function()
    {
        emit('on.done');
        progress.stop();
        bind_href();
        return app;
    };

    //all view related functions here
    var view = {
        render: function(){
            app.done();
            throw 'You must implement a view with `setView()` before you can render templates';
        }
    };

    app.setView = function(obj){
        view = obj;
        return app;
    };

    app.render = function()
    {
        view.render.apply(this,arguments);
    };

    $.jqMVC = app;
}(jQuery));<|endoftext|>