<commit_before>/**
 * This file is where we will write tests for the
 * functions in update-job/script.js using mocha and selenium.
 * Running `npm test` in the command line will run all
 * the mocha tests in the walk-in-interview directory.
 * Also note that the Dev App Server should be running
 * using `mvn package appengine:run` before running the tests.
 * The tests can also be run by cd-ing into this directory and
 * running `mocha script_test.js` in the command line.
 * To get test report formatted nicely, cd into this folder and
 * run `mocha script_test.js --reporter mochawesome --reporter-options
 * autoOpen=true`.
 */

/**
 * Unable to make imports from other files.
 * TODO(issue/38): import getRequirementsList() here.
 */
const REQUIREMENTS_LIST = {
  'O_LEVEL': 'O Level',
  'LANGUAGE_ENGLISH': 'English',
  'DRIVING_LICENSE_C': 'Category C Driving License',
};

/**
 * Note that if `https` is used instead in the url below then
 * we get the following error: ERR_SSL_PROTOCOL_ERROR
 */
const JOBPAGE_URL = 'http://localhost:3000/update-job/index.html';
const HOMEPAGE_URL = 'http://localhost:3000/job-details/index.html';

const assert = require('chai').assert;
const webdriver = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
const path = require('chromedriver').path;

const service = new chrome.ServiceBuilder(path).build();
chrome.setDefaultService(service);

const By = webdriver.By;
const until = webdriver.until;
const options = new chrome.Options();

/**
 * Note that these tests are headless.
 * To see the test browser pop up, remove this line.
 */
options.addArguments('headless');

/**
 * Set timeout to 50s so tests all have a chance to run.
 */
const TIMEOUT_MILLISECONDS = 50000;

let driver;

/** Note that this.timeout() will not work with arrow functions. */
describe('Update Job Tests', function() {
  this.timeout(TIMEOUT_MILLISECONDS);

  before(() => {
    driver = new webdriver.Builder().setChromeOptions(options)
        .withCapabilities(webdriver.Capabilities.chrome())
        .build();
    return driver.get(JOBPAGE_URL);
  });

  after(() => {
    return driver.quit();
  });

  describe('Page Rendering Tests', () => {
    describe('Page Title', () => {
      it('checks the title text', () => {
        return driver.findElement(By.id('page-title'))
            .getText()
            .then((title) => {
              assert.equal(title, 'Update Job Post');
            });
      });
    });

    describe('Cancel Button', () => {
      it('checks the value attribute', () => {
        return driver.findElement(By.id('cancel'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'Cancel');
            });
      });

      it('checks the type attribute', () => {
        return driver.findElement(By.id('cancel'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'reset');
            });
      });
    });

    describe('Submit Button', () => {
      it('checks the value attribute', () => {
        return driver.findElement(By.id('update'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'Update');
            });
      });

      it('checks the type attribute', () => {
        return driver.findElement(By.id('update'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'submit');
            });
      });
    });

    describe('Error Message', () => {
      it('checks initially empty', () => {
        return driver.findElement(By.id('error-message'))
            .getText()
            .then((msg) => {
              assert.isEmpty(msg);
            });
      });
    });

    describe('Job Title', () => {
      it('checks the type attribute', () => {
        return driver.findElement(By.id('title'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'text');
            });
      });

      it('checks the default value text', () => {
        return driver.findElement(By.id('title'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'Software Engineer');
            });
      });
    });

    describe('Job Description', () => {
      it('checks the type attribute', () => {
        return driver.findElement(By.id('description'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'textarea');
            });
      });

      it('checks the default value text', () => {
        return driver.findElement(By.id('description'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'Fight to defeat hair line receding');
            });
      });
    });

    describe('Job Address', () => {
      it('checks the type attribute', () => {
        return driver.findElement(By.id('address'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'text');
            });
      });

      it('checks the default value text', () => {
        return driver.findElement(By.id('address'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'Maple Tree');
            });
      });

      describe('Postal Code', () => {
        it('checks the type attribute', () => {
          return driver.findElement(By.id('postal-code'))
              .getAttribute('type')
              .then((value) => {
                assert.equal(value, 'text');
              });
        });

        it('checks the default value text', () => {
          return driver.findElement(By.id('postal-code'))
              .getAttribute('value')
              .then((value) => {
                assert.equal(value, '123');
              });
        });
      });
    });

    describe('Job Requirements', () => {
      it('checks the title text', () => {
        return driver.findElement(By.id('requirements-title'))
            .getText()
            .then((title) => {
              assert.equal(title, 'Requirements');
            });
      });

      it('checks correct requirement list rendered', () => {
        return driver.findElement(By.id('requirements-list'))
            .getText()
            .then((text) => {
              const list = REQUIREMENTS_LIST;
              for (const key in list) {
                if (list.hasOwnProperty(key)) {
                  if (!text.includes(list[key])) {
                    assert.fail(list[key] + ' requirement not included');
                  }
                }
              }
            });
      });

      it('checks the default option properly ticked', () => {
        return driver.findElement(By.id('requirements-list'))
            .getText()
            .then((text) => {
              for (const requirement in text) {
                if (requirement === 'O Level') {
                  requirement.getAttribute('checked')
                  .then((checked) => {
                    assert.isTrue(checked);
                  })
                }

                if (requirement === 'English') {
                  requirement.getAttribute('checked')
                  .then((checked) => {
                    assert.isTrue(checked);
                  })
                }
              }
            });
      });
    });

    describe('Job Pay', () => {
      it('checks the title text', () => {
        return driver.findElement(By.id('pay-title'))
            .getText()
            .then((title) => {
              assert.equal(title, 'Job Pay');
            });
      });

      it('checks correct select options rendered', () => {
        return driver.findElement(By.id('pay-frequency'))
            .getText()
            .then((text) => {
              const options = {
                'HOURLY': 'Hourly',
                'WEEKLY': 'Weekly',
                'MONTHLY': 'Monthly',
                'YEARLY': 'Yearly',
              };

              for (const key in options) {
                if (options.hasOwnProperty(key)) {
                  if (!text.includes(options[key])) {
                    assert.fail(options[key] + ' option not included');
                  }
                }
              }
            });
      });

      it('checks if default option properly selected', () => {
        return driver.findElement(By.id('pay-frequency'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'MONTHLY');
            });
      });

      it('min: checks the type attribute', () => {
        return driver.findElement(By.id('pay-min'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'number');
            });
      });

      it('min: checks the default value text', () => {
        return driver.findElement(By.id('pay-min'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, '1');
            });
      });

      it('max: checks the type attribute', () => {
        return driver.findElement(By.id('pay-max'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'number');
            });
      });

      it('max: checks the default value text', () => {
        return driver.findElement(By.id('pay-max'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, '4');
            });
      });
    });

    describe('Job Duration', () => {
      it('checks the title text', () => {
        return driver.findElement(By.id('duration-title'))
            .getText()
            .then((title) => {
              assert.equal(title, 'Job Duration');
            });
      });

      it('checks correct select options rendered', () => {
        return driver.findElement(By.id('duration'))
            .getText()
            .then((text) => {
              const options = {
                'ONE_WEEK': '1 Week',
                'TWO_WEEKS': '2 Weeks',
                'ONE_MONTH': '1 Month',
                'SIX_MONTHS': '6 Months',
                'ONE_YEAR': '1 Year',
                'OTHER': 'Other',
              };

              for (const key in options) {
                if (options.hasOwnProperty(key)) {
                  if (!text.includes(options[key])) {
                    assert.fail(options[key] + ' option not included');
                  }
                }
              }
            });
      });

      it('checks if default option properly selected', () => {
        return driver.findElement(By.id('duration'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'ONE_WEEK');
            });
      });
    });

    describe('Job Expiry', () => {
      it('checks the title text', () => {
        return driver.findElement(By.id('expiry-title'))
            .getText()
            .then((title) => {
              assert.equal(title, 'Job Expiry');
            });
      });

      it('checks the type attribute', () => {
        return driver.findElement(By.id('expiry'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'date');
            });
      });

      it('checks the min attribute', () => {
        return driver.findElement(By.id('expiry'))
            .getAttribute('min')
            .then((value) => {
              const expected = new Date().toISOString().substr(0, 10);

              assert.equal(value, expected);
            });
      });

      it('checks the max attribute', () => {
        return driver.findElement(By.id('expiry'))
            .getAttribute('max')
            .then((value) => {
              const date = new Date();
              date.setFullYear(date.getFullYear() + 1);
              const expected = date.toISOString().substr(0, 10);

              assert.equal(value, expected);
            });
      });

      it('checks if default option properly selected', () => {
        return driver.findElement(By.id('expiry'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, '2020-10-05');
            });
      });
    });
  });

  describe('Page Functionality Tests', () => {
    // TODO(issue/63): add more tests for invalid input.
    beforeEach(() => {
      /**
       * For the functionality tests, since we are testing the
       * cancel/submit buttons, the page url may have changed
       * in the test.
       * Always resets the current page to the job page
       * for the rest of the tests.
       */
        return driver.get(JOBPAGE_URL);
    });

    describe('Cancel Button', () => {
      /**
       * Clicking the cancel button should return the user to the homepage
       * and not make any POST request.
       */
      it('should return to homepage', () => {
        return clickCancel(driver)
            .then(() => driver.wait(until.urlIs(HOMEPAGE_URL)))
            .then(() => driver.getCurrentUrl())
            .then((currUrl) => assert.equal(currUrl, HOMEPAGE_URL));
      });
    });

    /**
     * If a field is not valid, then clicking submit will display an error
     * message with the invalid field, and no POST request will be made.
     */
    describe('Submit Button', () => {
      const date = new Date();
      const today = `${(date.getMonth() + 1)}-${date.getDate()}` +
        `-${date.getFullYear()}`;

      // Since all fields should be pre-filled with existing job post,
      // there is no need to add valid inputs to all fields again.

      it('no job title', () => {
        /**
         * The job title must be cleared here to test it.
         */
        return driver.findElement(By.id('title')).clear()
            .then(() => clickUpdate(driver))
            .then(() => driver.findElement(By.id('error-message')).getText())
            .then((text) => assert.equal(text,
                'There is an error in the following field: Job Title'));
      });

      it('should not be false positive', () => {
        /**
         * The job title must be cleared here to test it.
         * This test is making sure that the test is actually working
         * properly and not just passing for any string.
         * Note the use of assert.notEqual().
         */
        return driver.findElement(By.id('title')).clear()
            .then(() => clickUpdate(driver))
            .then(() => driver.findElement(By.id('error-message')).getText())
            .then((text) => assert.notEqual(text,
                'There is an error in the following field: '));
      });

      it('incorrect job address format', () => {
        // TODO(issue/13&33): add tests for address once places api implemented
      });

      it('incorrect postal code', () => {
        // TODO(issue/13&33): add tests for address once places api implemented
      });

      it('min greater than max', () => {
        /**
         * The pay-min and pay-max fields must be cleared before sending
         * another key.
         */
        return driver.findElement(By.id('pay-min')).clear()
            .then(() => driver.findElement(By.id('pay-max')).clear())
            .then(() => driver.findElement(By.id('pay-min')).sendKeys('7'))
            .then(() => driver.findElement(By.id('pay-max')).sendKeys('6'))
            .then(() => clickUpdate(driver))
            .then(() => driver.findElement(By.id('error-message')).getText())
            .then((text) => assert.equal(text,
                'There is an error in the following field: Job Pay'));
        });

      it('expiry date not chosen', () => {
        return driver.findElement(By.id('expiry')).clear()
            .then(() => clickUpdate(driver))
            .then(() => driver.findElement(By.id('error-message')).getText())
            .then((text) => assert.equal(text,
                'There is an error in the following field: Job Expiry'));
      });

      /**
       * If all the fields(including jobId) are valid, then a POST request should be made and the
       * user should be returned to the homepage.
       * However, since so far cloud firestore cannot be set up locally
       * and only a dummy jobId is passed, it can only catch an error.
       */
      it('not update successfully', () => {
        return clickUpdate(driver)
            .then(() => driver.findElement(By.id('error-message')).getText())
            .then((text) => assert.equal(text,
                'There was an error while storing the job post. Please try again'));
      });

      /**
       * TODO(issue/40): check that POST request has been made
       * & also check that the response is what was expected
       */
    });
  });
});

/**
 * This function just to submit the updated job with the
 * filled out fields.
 * @param {webdriver} driver the current driver being tested.
 * @return {webdriver} the altered driver.
 */
function clickUpdate(driver) {
  return driver.findElement(By.id('update')).click();
};

/**
 * This function just to click the cancel button and return
 * to the homepage.
 * @param {webdriver} driver the current driver being tested.
 * @return {webdriver} the altered driver.
 */
function clickCancel(driver) {
  return driver.findElement(By.id('cancel')).click();
};
<commit_msg>fix indentation<commit_after>/**
 * This file is where we will write tests for the
 * functions in update-job/script.js using mocha and selenium.
 * Running `npm test` in the command line will run all
 * the mocha tests in the walk-in-interview directory.
 * Also note that the Dev App Server should be running
 * using `mvn package appengine:run` before running the tests.
 * The tests can also be run by cd-ing into this directory and
 * running `mocha script_test.js` in the command line.
 * To get test report formatted nicely, cd into this folder and
 * run `mocha script_test.js --reporter mochawesome --reporter-options
 * autoOpen=true`.
 */

/**
 * Unable to make imports from other files.
 * TODO(issue/38): import getRequirementsList() here.
 */
const REQUIREMENTS_LIST = {
  'O_LEVEL': 'O Level',
  'LANGUAGE_ENGLISH': 'English',
  'DRIVING_LICENSE_C': 'Category C Driving License',
};

/**
 * Note that if `https` is used instead in the url below then
 * we get the following error: ERR_SSL_PROTOCOL_ERROR
 */
const JOBPAGE_URL = 'http://localhost:3000/update-job/index.html';
const HOMEPAGE_URL = 'http://localhost:3000/job-details/index.html';

const assert = require('chai').assert;
const webdriver = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
const path = require('chromedriver').path;

const service = new chrome.ServiceBuilder(path).build();
chrome.setDefaultService(service);

const By = webdriver.By;
const until = webdriver.until;
const options = new chrome.Options();

/**
 * Note that these tests are headless.
 * To see the test browser pop up, remove this line.
 */
options.addArguments('headless');

/**
 * Set timeout to 50s so tests all have a chance to run.
 */
const TIMEOUT_MILLISECONDS = 50000;

let driver;

/** Note that this.timeout() will not work with arrow functions. */
describe('Update Job Tests', function() {
  this.timeout(TIMEOUT_MILLISECONDS);

  before(() => {
    driver = new webdriver.Builder().setChromeOptions(options)
        .withCapabilities(webdriver.Capabilities.chrome())
        .build();
    return driver.get(JOBPAGE_URL);
  });

  after(() => {
    return driver.quit();
  });

  describe('Page Rendering Tests', () => {
    describe('Page Title', () => {
      it('checks the title text', () => {
        return driver.findElement(By.id('page-title'))
            .getText()
            .then((title) => {
              assert.equal(title, 'Update Job Post');
            });
      });
    });

    describe('Cancel Button', () => {
      it('checks the value attribute', () => {
        return driver.findElement(By.id('cancel'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'Cancel');
            });
      });

      it('checks the type attribute', () => {
        return driver.findElement(By.id('cancel'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'reset');
            });
      });
    });

    describe('Submit Button', () => {
      it('checks the value attribute', () => {
        return driver.findElement(By.id('update'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'Update');
            });
      });

      it('checks the type attribute', () => {
        return driver.findElement(By.id('update'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'submit');
            });
      });
    });

    describe('Error Message', () => {
      it('checks initially empty', () => {
        return driver.findElement(By.id('error-message'))
            .getText()
            .then((msg) => {
              assert.isEmpty(msg);
            });
      });
    });

    describe('Job Title', () => {
      it('checks the type attribute', () => {
        return driver.findElement(By.id('title'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'text');
            });
      });

      it('checks the default value text', () => {
        return driver.findElement(By.id('title'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'Software Engineer');
            });
      });
    });

    describe('Job Description', () => {
      it('checks the type attribute', () => {
        return driver.findElement(By.id('description'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'textarea');
            });
      });

      it('checks the default value text', () => {
        return driver.findElement(By.id('description'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'Fight to defeat hair line receding');
            });
      });
    });

    describe('Job Address', () => {
      it('checks the type attribute', () => {
        return driver.findElement(By.id('address'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'text');
            });
      });

      it('checks the default value text', () => {
        return driver.findElement(By.id('address'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'Maple Tree');
            });
      });

      describe('Postal Code', () => {
        it('checks the type attribute', () => {
          return driver.findElement(By.id('postal-code'))
              .getAttribute('type')
              .then((value) => {
                assert.equal(value, 'text');
              });
        });

        it('checks the default value text', () => {
          return driver.findElement(By.id('postal-code'))
              .getAttribute('value')
              .then((value) => {
                assert.equal(value, '123');
              });
        });
      });
    });

    describe('Job Requirements', () => {
      it('checks the title text', () => {
        return driver.findElement(By.id('requirements-title'))
            .getText()
            .then((title) => {
              assert.equal(title, 'Requirements');
            });
      });

      it('checks correct requirement list rendered', () => {
        return driver.findElement(By.id('requirements-list'))
            .getText()
            .then((text) => {
              const list = REQUIREMENTS_LIST;
              for (const key in list) {
                if (list.hasOwnProperty(key)) {
                  if (!text.includes(list[key])) {
                    assert.fail(list[key] + ' requirement not included');
                  }
                }
              }
            });
      });

      it('checks the default option properly ticked', () => {
        return driver.findElement(By.id('requirements-list'))
            .getText()
            .then((text) => {
              for (const requirement in text) {
                if (requirement === 'O Level') {
                  requirement.getAttribute('checked')
                      .then((checked) => {
                        assert.isTrue(checked);
                      });
                }

                if (requirement === 'English') {
                  requirement.getAttribute('checked')
                      .then((checked) => {
                        assert.isTrue(checked);
                      });
                }
              }
            });
      });
    });

    describe('Job Pay', () => {
      it('checks the title text', () => {
        return driver.findElement(By.id('pay-title'))
            .getText()
            .then((title) => {
              assert.equal(title, 'Job Pay');
            });
      });

      it('checks correct select options rendered', () => {
        return driver.findElement(By.id('pay-frequency'))
            .getText()
            .then((text) => {
              const options = {
                'HOURLY': 'Hourly',
                'WEEKLY': 'Weekly',
                'MONTHLY': 'Monthly',
                'YEARLY': 'Yearly',
              };

              for (const key in options) {
                if (options.hasOwnProperty(key)) {
                  if (!text.includes(options[key])) {
                    assert.fail(options[key] + ' option not included');
                  }
                }
              }
            });
      });

      it('checks if default option properly selected', () => {
        return driver.findElement(By.id('pay-frequency'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'MONTHLY');
            });
      });

      it('min: checks the type attribute', () => {
        return driver.findElement(By.id('pay-min'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'number');
            });
      });

      it('min: checks the default value text', () => {
        return driver.findElement(By.id('pay-min'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, '1');
            });
      });

      it('max: checks the type attribute', () => {
        return driver.findElement(By.id('pay-max'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'number');
            });
      });

      it('max: checks the default value text', () => {
        return driver.findElement(By.id('pay-max'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, '4');
            });
      });
    });

    describe('Job Duration', () => {
      it('checks the title text', () => {
        return driver.findElement(By.id('duration-title'))
            .getText()
            .then((title) => {
              assert.equal(title, 'Job Duration');
            });
      });

      it('checks correct select options rendered', () => {
        return driver.findElement(By.id('duration'))
            .getText()
            .then((text) => {
              const options = {
                'ONE_WEEK': '1 Week',
                'TWO_WEEKS': '2 Weeks',
                'ONE_MONTH': '1 Month',
                'SIX_MONTHS': '6 Months',
                'ONE_YEAR': '1 Year',
                'OTHER': 'Other',
              };

              for (const key in options) {
                if (options.hasOwnProperty(key)) {
                  if (!text.includes(options[key])) {
                    assert.fail(options[key] + ' option not included');
                  }
                }
              }
            });
      });

      it('checks if default option properly selected', () => {
        return driver.findElement(By.id('duration'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, 'ONE_WEEK');
            });
      });
    });

    describe('Job Expiry', () => {
      it('checks the title text', () => {
        return driver.findElement(By.id('expiry-title'))
            .getText()
            .then((title) => {
              assert.equal(title, 'Job Expiry');
            });
      });

      it('checks the type attribute', () => {
        return driver.findElement(By.id('expiry'))
            .getAttribute('type')
            .then((value) => {
              assert.equal(value, 'date');
            });
      });

      it('checks the min attribute', () => {
        return driver.findElement(By.id('expiry'))
            .getAttribute('min')
            .then((value) => {
              const expected = new Date().toISOString().substr(0, 10);

              assert.equal(value, expected);
            });
      });

      it('checks the max attribute', () => {
        return driver.findElement(By.id('expiry'))
            .getAttribute('max')
            .then((value) => {
              const date = new Date();
              date.setFullYear(date.getFullYear() + 1);
              const expected = date.toISOString().substr(0, 10);

              assert.equal(value, expected);
            });
      });

      it('checks if default option properly selected', () => {
        return driver.findElement(By.id('expiry'))
            .getAttribute('value')
            .then((value) => {
              assert.equal(value, '2020-10-05');
            });
      });
    });
  });

  describe('Page Functionality Tests', () => {
    // TODO(issue/63): add more tests for invalid input.
    beforeEach(() => {
      /**
       * For the functionality tests, since we are testing the
       * cancel/submit buttons, the page url may have changed
       * in the test.
       * Always resets the current page to the job page
       * for the rest of the tests.
       */
      return driver.get(JOBPAGE_URL);
    });

    describe('Cancel Button', () => {
      /**
       * Clicking the cancel button should return the user to the homepage
       * and not make any POST request.
       */
      it('should return to homepage', () => {
        return clickCancel(driver)
            .then(() => driver.wait(until.urlIs(HOMEPAGE_URL)))
            .then(() => driver.getCurrentUrl())
            .then((currUrl) => assert.equal(currUrl, HOMEPAGE_URL));
      });
    });

    /**
     * If a field is not valid, then clicking submit will display an error
     * message with the invalid field, and no POST request will be made.
     */
    describe('Submit Button', () => {
      // Since all fields should be pre-filled with existing job post,
      // there is no need to add valid inputs to all fields again.

      it('no job title', () => {
        /**
         * The job title must be cleared here to test it.
         */
        return driver.findElement(By.id('title')).clear()
            .then(() => clickUpdate(driver))
            .then(() => driver.findElement(By.id('error-message')).getText())
            .then((text) => assert.equal(text,
                'There is an error in the following field: Job Title'));
      });

      it('should not be false positive', () => {
        /**
         * The job title must be cleared here to test it.
         * This test is making sure that the test is actually working
         * properly and not just passing for any string.
         * Note the use of assert.notEqual().
         */
        return driver.findElement(By.id('title')).clear()
            .then(() => clickUpdate(driver))
            .then(() => driver.findElement(By.id('error-message')).getText())
            .then((text) => assert.notEqual(text,
                'There is an error in the following field: '));
      });

      it('incorrect job address format', () => {
        // TODO(issue/13&33): add tests for address once places api implemented
      });

      it('incorrect postal code', () => {
        // TODO(issue/13&33): add tests for address once places api implemented
      });

      it('min greater than max', () => {
        /**
         * The pay-min and pay-max fields must be cleared before sending
         * another key.
         */
        return driver.findElement(By.id('pay-min')).clear()
            .then(() => driver.findElement(By.id('pay-max')).clear())
            .then(() => driver.findElement(By.id('pay-min')).sendKeys('7'))
            .then(() => driver.findElement(By.id('pay-max')).sendKeys('6'))
            .then(() => clickUpdate(driver))
            .then(() => driver.findElement(By.id('error-message')).getText())
            .then((text) => assert.equal(text,
                'There is an error in the following field: Job Pay'));
      });

      it('expiry date not chosen', () => {
        return driver.findElement(By.id('expiry')).clear()
            .then(() => clickUpdate(driver))
            .then(() => driver.findElement(By.id('error-message')).getText())
            .then((text) => assert.equal(text,
                'There is an error in the following field: Job Expiry'));
      });

      /**
       * If all the fields(including jobId) are valid, then a POST request
       * should be made and the user should be returned to the homepage.
       * However, since so far cloud firestore cannot be set up locally
       * and only a dummy jobId is passed, it can only catch an error.
       */
      it('not update successfully', () => {
        return clickUpdate(driver)
            .then(() => driver.findElement(By.id('error-message')).getText())
            .then((text) => assert.equal(text,
                'There was an error while storing the job post. ' +
                  'Please try again'));
      });

      /**
       * TODO(issue/40): check that POST request has been made
       * & also check that the response is what was expected
       */
    });
  });
});

/**
 * This function just to submit the updated job with the
 * filled out fields.
 * @param {webdriver} driver the current driver being tested.
 * @return {webdriver} the altered driver.
 */
function clickUpdate(driver) {
  return driver.findElement(By.id('update')).click();
};

/**
 * This function just to click the cancel button and return
 * to the homepage.
 * @param {webdriver} driver the current driver being tested.
 * @return {webdriver} the altered driver.
 */
function clickCancel(driver) {
  return driver.findElement(By.id('cancel')).click();
};
<|endoftext|>


<commit_before>var isSeeking = false;
var playlistIndex = 0;
var fileData = null;

$(document).ready(function () {
	// get root browser contents
	browser_bootstrap();
	// hookup progress bar
	$('audio.player').on('timeupdate', function () {
		var currentTime = $('audio.player').get(0).currentTime;
		//var fileData = $('table.playlist tr.data').eq(playlistIndex).data('file');
		//var duration = fileData.duration;
		var duration = $('audio.player').get(0).duration;
		$('div.progress-bar').width(currentTime / duration * 100 + '%');
		// update time display
		$('div.progress-bar').html(stringifyTime(currentTime));
		$('.currentTime').html(fileData.format + '     ' + stringifyTime(currentTime) + ' / ' + stringifyTime(duration));
	});
	$('div.progress').click(function (e) {
		isSeeking = true;
		var duration = $('audio.player').get(0).duration;
		if (duration > 0) {
			var selectedX = e.pageX - $(this).offset().left;
			var maxX = $(this).width();
			var targetTimeFraction = selectedX / maxX;
			var targetTime = duration * targetTimeFraction;
			$('audio.player').get(0).currentTime = targetTime;
		}
		return false;
	});
	// automatic track change at end of current song
	$('audio.player').on('ended', function () {
		audioNext();
	});
	// automatic track change due to inaccurate duration calculation
	$('audio.player').on('play playing', function () {
		isSeeking = false;
	});
	$('audio.player').on('waiting', function () {
		var currentTime = $('audio.player').get(0).currentTime;
		if (currentTime > 0 && !isSeeking) {
			audioNext();
		}
	});
	// hookup audio player buttons
	$('button.previous').click(audioPrevious);
	$('button.play').click(audioPlay);
	$('button.pause').click(audioPause);
	$('button.stop').click(audioStop);
	$('button.next').click(audioNext);
	// keyboard shortcuts
	$(document).keypress(function (e) {
		var key = String.fromCharCode(e.charCode);
		if (key == 'z') {
			$('button.previous').click();
		} else if (key == 'x') {
			$('button.play').click();
		} else if (key == 'c') {
			$('button.pause').click();
		} else if (key == 'v') {
			$('button.stop').click();
		} else if (key == 'b') {
			$('button.next').click();
		}
	});
});

function stringifyTime(time) {
	var minutes = Math.floor(time / 60);
	var seconds = Math.floor(time % 60);
	if (seconds < 10) {
		seconds = '0' + seconds;
	}

	if (isNaN(minutes) || isNaN(seconds)) {
		return '0:00';
	} else {
		return minutes + ':' + seconds;
	}
}

function browser_bootstrap() {
	$.get('/dir?path=', function (data, status) {
		if (status == 'success') {
			handleDirContents('div.browser', data);
		}
	});
}

function handleDirContents(parent, dirEntries) {
	var dirs = [];
	var files = [];
	for (var dirEntry of dirEntries) {
		if (dirEntry.type == 'dir') {
			dirs.push(dirEntry);
		} else if (dirEntry.type == 'file') {
			files.push(dirEntry);
		}
	}
	handleDirs(parent, dirs);
	handleFiles(files);
}

function handleDirs(parent, dirs) {
	if (dirs.length > 0) {
		$(parent).append('

');
	}
	for (var dir of dirs) {
		$(parent + ' ul').append('
' + dir.name + '
');
		$(parent + ' ul li span').last().data('dirUrl', dir.dirUrl);
		$(parent + ' ul li span').last().click(function () {
			var element = this;
			if ($(element).siblings('ul').length > 0) {
				$(element).siblings('ul').remove();
			} else {
				// reset playlistIndex
				playlistIndex = 0;
				// clear playlist
				$('table.playlist tr.data').remove();
				// show loading message
				$('table.playlist').hide();
				$('.loading_message').show();
				// get dir contents
				var dirUrl = $(element).data('dirUrl');
				$.get(dirUrl, function (data, status) {
					if (status == 'success') {
						handleDirContents(OptimalSelect.select(element.parentNode), data);
					}
					// hide loading message
					$('.loading_message').hide();
					$('table.playlist').show();
				});
			}
			return false;
		});
	}
	$('li span').hover(
		function () {
			$(this).addClass('hover');
		}, function () {
			$(this).removeClass('hover');
		}
	);
}

function handleFiles(files) {
	for (var file of files) {
		for (var track of file.tracks) {
			//console.log(JSON.stringify(file, null, 4));
			$('table.playlist').append('

');
			$('table.playlist tr.data').last().append('' + track.track + '	');
			$('table.playlist tr.data').last().append('' + track.title + '	');
			$('table.playlist tr.data').last().append('' + track.artist + '	');
			$('table.playlist tr.data').last().append('' + track.album + '	');
			$('table.playlist tr.data').last().append('' + stringifyTime(track.duration) + '	');
			$('table.playlist tr.data').last().data('file', track);
		}
	}
	$('table.playlist tr.data').click(function () {
		// stop current song
		audioStop();
		// update playlistIndex
		playlistIndex = $(this).index() - 1;
		// play song
		audioPlay();
		return false;
	});
	$('table.playlist tr.data').hover(
		function () {
			$(this).addClass('hover');
		}, function () {
			$(this).removeClass('hover');
		}
	);
}

function audioStop() {
	$('audio.player').get(0).pause();
	$('audio.player').get(0).currentTime = 0;
	$('div.progress-bar').width('0%');
}

function audioPlay() {
	audioStop();
	fileData = $('table.playlist tr.data').eq(playlistIndex).data('file');
	// highlight in playlist
	$('table.playlist tr.data').removeClass('info');
	$('table.playlist tr.data').eq(playlistIndex).addClass('info');
	$('table.playlist tr.data').eq(playlistIndex).scrollintoview();
	// change current song label
	$('.currentSong').html('     ' + fileData.title);
	// load selected song
	$('audio.player').children().remove();
	$('audio.player').append('<source src="' + fileData.playUrl + '" type="audio/mpeg" />');
	$('audio.player').get(0).load();
	// start playback
	$('audio.player').get(0).play();
	$('div.progress-bar').addClass('active');
}

function audioPause() {
	if ($('audio.player').get(0).paused) {
		$('audio.player').get(0).play();
		$('div.progress-bar').addClass('active');
	} else {
		$('audio.player').get(0).pause();
		$('div.progress-bar').removeClass('active');
	}
}

function audioPrevious() {
	audioStop();
	if ($('table.playlist tr.data').length == 0) {
		playlistIndex = 0;
		return;
	}
	playlistIndex--;
	if (playlistIndex < 0) {
		playlistIndex = $('table.playlist tr.data').length - 1;
	}
	audioPlay();
}

function audioNext() {
	audioStop();
	if ($('table.playlist tr.data').length == 0) {
		playlistIndex = 0;
		return;
	}
	playlistIndex++;
	if (playlistIndex > ($('table.playlist tr.data').length - 1)) {
		playlistIndex = 0;
	}
	audioPlay();
}
<commit_msg>fix loading message<commit_after>var isSeeking = false;
var playlistIndex = 0;
var fileData = null;

$(document).ready(function () {
	// get root browser contents
	browser_bootstrap();
	// hookup progress bar
	$('audio.player').on('timeupdate', function () {
		var currentTime = $('audio.player').get(0).currentTime;
		//var fileData = $('table.playlist tr.data').eq(playlistIndex).data('file');
		//var duration = fileData.duration;
		var duration = $('audio.player').get(0).duration;
		$('div.progress-bar').width(currentTime / duration * 100 + '%');
		// update time display
		$('div.progress-bar').html(stringifyTime(currentTime));
		$('.currentTime').html(fileData.format + '     ' + stringifyTime(currentTime) + ' / ' + stringifyTime(duration));
	});
	$('div.progress').click(function (e) {
		isSeeking = true;
		var duration = $('audio.player').get(0).duration;
		if (duration > 0) {
			var selectedX = e.pageX - $(this).offset().left;
			var maxX = $(this).width();
			var targetTimeFraction = selectedX / maxX;
			var targetTime = duration * targetTimeFraction;
			$('audio.player').get(0).currentTime = targetTime;
		}
		return false;
	});
	// automatic track change at end of current song
	$('audio.player').on('ended', function () {
		audioNext();
	});
	// automatic track change due to inaccurate duration calculation
	$('audio.player').on('play playing', function () {
		isSeeking = false;
	});
	$('audio.player').on('waiting', function () {
		var currentTime = $('audio.player').get(0).currentTime;
		if (currentTime > 0 && !isSeeking) {
			audioNext();
		}
	});
	// hookup audio player buttons
	$('button.previous').click(audioPrevious);
	$('button.play').click(audioPlay);
	$('button.pause').click(audioPause);
	$('button.stop').click(audioStop);
	$('button.next').click(audioNext);
	// keyboard shortcuts
	$(document).keypress(function (e) {
		var key = String.fromCharCode(e.charCode);
		if (key == 'z') {
			$('button.previous').click();
		} else if (key == 'x') {
			$('button.play').click();
		} else if (key == 'c') {
			$('button.pause').click();
		} else if (key == 'v') {
			$('button.stop').click();
		} else if (key == 'b') {
			$('button.next').click();
		}
	});
});

function stringifyTime(time) {
	var minutes = Math.floor(time / 60);
	var seconds = Math.floor(time % 60);
	if (seconds < 10) {
		seconds = '0' + seconds;
	}

	if (isNaN(minutes) || isNaN(seconds)) {
		return '0:00';
	} else {
		return minutes + ':' + seconds;
	}
}

function browser_bootstrap() {
	$.get('/dir?path=', function (data, status) {
		if (status == 'success') {
			handleDirContents('div.browser', data);
		}
	});
}

function handleDirContents(parent, dirEntries) {
	var dirs = [];
	var files = [];
	for (var dirEntry of dirEntries) {
		if (dirEntry.type == 'dir') {
			dirs.push(dirEntry);
		} else if (dirEntry.type == 'file') {
			files.push(dirEntry);
		}
	}
	handleDirs(parent, dirs);
	handleFiles(files);
}

function handleDirs(parent, dirs) {
	if (dirs.length > 0) {
		$(parent).append('

');
	}
	for (var dir of dirs) {
		$(parent + ' ul').append('
' + dir.name + '
');
		$(parent + ' ul li span').last().data('dirUrl', dir.dirUrl);
		$(parent + ' ul li span').last().click(function () {
			var element = this;
			if ($(element).siblings('ul').length > 0) {
				$(element).siblings('ul').remove();
			} else {
				// reset playlistIndex
				playlistIndex = 0;
				// clear playlist
				$('table.playlist tr.data').remove();
				// show loading message
				$('.playlist_container').hide();
				$('.loading_message').show();
				// get dir contents
				var dirUrl = $(element).data('dirUrl');
				$.get(dirUrl, function (data, status) {
					if (status == 'success') {
						handleDirContents(OptimalSelect.select(element.parentNode), data);
					}
					// hide loading message
					$('.loading_message').hide();
					$('.playlist_container').show();
				});
			}
			return false;
		});
	}
	$('li span').hover(
		function () {
			$(this).addClass('hover');
		}, function () {
			$(this).removeClass('hover');
		}
	);
}

function handleFiles(files) {
	for (var file of files) {
		for (var track of file.tracks) {
			//console.log(JSON.stringify(file, null, 4));
			$('table.playlist').append('

');
			$('table.playlist tr.data').last().append('' + track.track + '	');
			$('table.playlist tr.data').last().append('' + track.title + '	');
			$('table.playlist tr.data').last().append('' + track.artist + '	');
			$('table.playlist tr.data').last().append('' + track.album + '	');
			$('table.playlist tr.data').last().append('' + stringifyTime(track.duration) + '	');
			$('table.playlist tr.data').last().data('file', track);
		}
	}
	$('table.playlist tr.data').click(function () {
		// stop current song
		audioStop();
		// update playlistIndex
		playlistIndex = $(this).index() - 1;
		// play song
		audioPlay();
		return false;
	});
	$('table.playlist tr.data').hover(
		function () {
			$(this).addClass('hover');
		}, function () {
			$(this).removeClass('hover');
		}
	);
}

function audioStop() {
	$('audio.player').get(0).pause();
	$('audio.player').get(0).currentTime = 0;
	$('div.progress-bar').width('0%');
}

function audioPlay() {
	audioStop();
	fileData = $('table.playlist tr.data').eq(playlistIndex).data('file');
	// highlight in playlist
	$('table.playlist tr.data').removeClass('info');
	$('table.playlist tr.data').eq(playlistIndex).addClass('info');
	$('table.playlist tr.data').eq(playlistIndex).scrollintoview();
	// change current song label
	$('.currentSong').html('     ' + fileData.title);
	// load selected song
	$('audio.player').children().remove();
	$('audio.player').append('<source src="' + fileData.playUrl + '" type="audio/mpeg" />');
	$('audio.player').get(0).load();
	// start playback
	$('audio.player').get(0).play();
	$('div.progress-bar').addClass('active');
}

function audioPause() {
	if ($('audio.player').get(0).paused) {
		$('audio.player').get(0).play();
		$('div.progress-bar').addClass('active');
	} else {
		$('audio.player').get(0).pause();
		$('div.progress-bar').removeClass('active');
	}
}

function audioPrevious() {
	audioStop();
	if ($('table.playlist tr.data').length == 0) {
		playlistIndex = 0;
		return;
	}
	playlistIndex--;
	if (playlistIndex < 0) {
		playlistIndex = $('table.playlist tr.data').length - 1;
	}
	audioPlay();
}

function audioNext() {
	audioStop();
	if ($('table.playlist tr.data').length == 0) {
		playlistIndex = 0;
		return;
	}
	playlistIndex++;
	if (playlistIndex > ($('table.playlist tr.data').length - 1)) {
		playlistIndex = 0;
	}
	audioPlay();
}
<|endoftext|>


<commit_before>// View model for the courses page
function pageViewModel(gvm) {
    // projecttitle
    gvm.projecttitle = ko.observable("");
    gvm.numberOfCompetencesToAdd = 0;

    // Page specific i18n bindings
    gvm.title = ko.computed(function (){i18n.setLocale(gvm.lang()); return gvm.app() + ' - ' + i18n.__("ProjectTitle") + ": " + gvm.projecttitle();}, gvm);
    gvm.pageHeader = ko.observable("Project");
    gvm.projectname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectName");}, gvm);

    gvm.getProjectInfo = function() {
        $.getJSON('/api/project/' + $("#projectHeader").data('value'), function(data) {
            gvm.pageHeader(data[0].code + ' - ' + data[0].name);
        });
    };
}

function addCompetence() {
    alert('hallo');
    var competencePanelWrapper = document.createElement('div');
    var competencePanel = document.createElement('div');
    var competencePanelHeading = document.createElement('div');
    var competencePanelBody = document.createElement('div');
    var competencePanelFooter = document.createElement('div');
    var competenceCode = document.createElement('input');
    var competenceName = document.createElement('input');
    var subcompetenceButton = document.createElement('button');
    var removeCompetenceButton = document.createElement('button');

    competenceCode.type = 'text';
    competenceCode.placeholder = "Competence-Code";
    $(competenceCode).addClass("form-control");

    competenceName.type = 'text';
    competenceName.placeholder = "Name of the competence";
    $(competenceName).addClass("form-control");

    $(subcompetenceButton).addClass("btn");
    $(subcompetenceButton).text("Add a subcompetence");
    $(subcompetenceButton).val(viewModel.numberOfCompetencesToAdd);
    $(subcompetenceButton).on('click', function() {
        addSubCompetence("comp-" + $(subcompetenceButton).val())
    });

    $(removeCompetenceButton).addClass("btn pull-right");
    $(removeCompetenceButton).text("Remove this competence");
    $(removeCompetenceButton).val(viewModel.numberOfCompetencesToAdd);
    $(removeCompetenceButton).on('click', function() {
        removeCompetence($(removeCompetenceButton).val());
    });

    $(competencePanelWrapper).addClass("col-md-9 compPanel-" + $(subcompetenceButton).val());
    $(competencePanel).addClass("panel panel-default");
    $(competencePanelHeading).addClass("panel-heading comp-" + $(subcompetenceButton).val());
    $(competencePanelBody).addClass("panel-body");
    $(competencePanelFooter).addClass("panel-footer");

    if(viewModel.numberOfCompetencesToAdd == 0) {
        $("#top-col").after(competencePanelWrapper);
    } else {
        $(".compPanel:last").after(competencePanelWrapper);
    }
    ++viewModel.numberOfCompetencesToAdd;
    $(competencePanelWrapper).append(competencePanel);
    $(competencePanel).append(competencePanelHeading);
    $(competencePanelHeading).append(competenceName);
    $(competenceName).after(competenceCode);
    $(competencePanelHeading).after(competencePanelBody);
    $(competencePanelBody).after(competencePanelFooter);
    $(competencePanelFooter).append(subcompetenceButton);
    $(subcompetenceButton).after(removeCompetenceButton);
}

function addSubCompetence(competence) {
    alert(competence);
}

function removeCompetence(competence) {
    $(".compPanel-" + competence).remove();
    --viewModel.numberOfCompetencesToAdd;
}

function addIndicator() {

}

function initPage() {
    viewModel.getProjectInfo();
    $("#addCompetenceBtn").click(function() {
        addCompetence();
    })
}<commit_msg>projectpage<commit_after>// View model for the courses page
function pageViewModel(gvm) {
    // projecttitle
    gvm.projecttitle = ko.observable("");
    gvm.numberOfCompetencesToAdd = 0;

    // Page specific i18n bindings
    gvm.title = ko.computed(function (){i18n.setLocale(gvm.lang()); return gvm.app() + ' - ' + i18n.__("ProjectTitle") + ": " + gvm.projecttitle();}, gvm);
    gvm.pageHeader = ko.observable("Project");
    gvm.projectname = ko.computed(function(){i18n.setLocale(gvm.lang()); return i18n.__("ProjectName");}, gvm);

    gvm.getProjectInfo = function() {
        $.getJSON('/api/project/' + $("#projectHeader").data('value'), function(data) {
            gvm.pageHeader(data[0].code + ' - ' + data[0].name);
        });
    };
}

function addCompetence() {
    var competencePanelWrapper = document.createElement('div');
    var competencePanel = document.createElement('div');
    var competencePanelHeading = document.createElement('div');
    var competencePanelBody = document.createElement('div');
    var competencePanelFooter = document.createElement('div');
    var competenceCode = document.createElement('input');
    var competenceName = document.createElement('input');
    var subcompetenceButton = document.createElement('button');
    var removeCompetenceButton = document.createElement('button');

    competenceCode.type = 'text';
    competenceCode.placeholder = "Competence-Code";
    $(competenceCode).addClass("form-control");

    competenceName.type = 'text';
    competenceName.placeholder = "Name of the competence";
    $(competenceName).addClass("form-control");

    $(subcompetenceButton).addClass("btn");
    $(subcompetenceButton).text("Add a subcompetence");
    $(subcompetenceButton).val(viewModel.numberOfCompetencesToAdd);
    $(subcompetenceButton).on('click', function() {
        addSubCompetence("comp-" + $(subcompetenceButton).val())
    });

    $(removeCompetenceButton).addClass("btn pull-right");
    $(removeCompetenceButton).text("Remove this competence");
    $(removeCompetenceButton).val(viewModel.numberOfCompetencesToAdd);
    $(removeCompetenceButton).on('click', function() {
        removeCompetence($(removeCompetenceButton).val());
    });

    $(competencePanelWrapper).addClass("col-md-9 compPanel panel-" + $(subcompetenceButton).val());
    $(competencePanel).addClass("panel panel-default");
    $(competencePanelHeading).addClass("panel-heading comp-" + $(subcompetenceButton).val());
    $(competencePanelBody).addClass("panel-body");
    $(competencePanelFooter).addClass("panel-footer");

    if(viewModel.numberOfCompetencesToAdd == 0) {
        $("#top-col").after(competencePanelWrapper);
    } else {
        $(".compPanel:last").after(competencePanelWrapper);
    }
    ++viewModel.numberOfCompetencesToAdd;
    $(competencePanelWrapper).append(competencePanel);
    $(competencePanel).append(competencePanelHeading);
    $(competencePanelHeading).append(competenceName);
    $(competenceName).after(competenceCode);
    $(competencePanelHeading).after(competencePanelBody);
    $(competencePanelBody).after(competencePanelFooter);
    $(competencePanelFooter).append(subcompetenceButton);
    $(subcompetenceButton).after(removeCompetenceButton);
}

function addSubCompetence(competence) {
    alert(competence);
}

function removeCompetence(competence) {
    $(".panel-" + competence).remove();
    --viewModel.numberOfCompetencesToAdd;
}

function addIndicator() {

}

function initPage() {
    viewModel.getProjectInfo();
    $("#addCompetenceBtn").click(function() {
        addCompetence();
    })
}<|endoftext|>


<commit_before>(function($, kendo, breeze){

    var exports = kendo.data.breeze = {};
    var Predicate = breeze.Predicate;
    var Operators = breeze.FilterQueryOp;

    function BreezeTransport(options) {
        if (!options.manager) {
            throw new Error("Please specify a Breeze EntityManager via `manager` option");
        }
        if (!options.query) {
            throw new Error("Please specify a Breeze EntityQuery via `query` option");
        }
        this.manager = options.manager;
        this.query = options.query;
    }

    function makeOperator(op) {
        return {
            eq         : Operators.Equals,
            neq        : Operators.NotEquals,
            lt         : Operators.LessThan,
            lte        : Operators.LessThanOrEqual,
            gt         : Operators.GreaterThan,
            gte        : Operators.GreaterThanOrEqual,
            startswith : Operators.StartsWith,
            endswith   : Operators.EndsWith,
            contains   : Operators.Contains,
        }[op];
    }

    function makeFilters(args) {
        var filters = args.filters.map(function(f){
            var field = f.field;
            var operator = makeOperator(f.operator);
            var value = f.value;
            return Predicate.create(field, operator, value);
        });
        if (args.logic == "and") return Predicate.and(filters);
        if (args.logic == "or") return Predicate.or(filters);
        throw new Error("Unsupported predicate logic " + args.logic);
    }

    $.extend(BreezeTransport.prototype, {
        read: function(options) {
            var self = this;
            //console.log("READ", options);
            var query = self.query;
            var args = options.data;
            if (args.filter) {
                query = query.where(makeFilters(args.filter));
            }
            if (args.sort && args.sort.length > 0) {
                query = query.orderBy(args.sort.map(function(col){
                    return col.field + (col.dir == "desc" ? " desc" : "");
                }).join(", "));
            }
            if (args.page) {
                query = query
                    .skip(args.skip)
                    .take(args.take)
                    .inlineCount();
            }
            try {
                self.manager.executeQuery(query).then(function(data){
                    options.success(self._makeResults(data));
                }).fail(function(err){
                    options.error(err);
                });
            } catch(ex) {
                console.error(ex);
            }
        },
        create: function(options) {
            //console.log("CREATE", options);
            this._saveChanges();
        },
        update: function(options) {
            //console.log("UPDATE", options);
            this._saveChanges();
        },
        destroy: function(options) {
            //console.log("DESTROY", options);
            this._saveChanges();
        },

        _saveChanges: (function(){
            // throttle, since we will get multiple calls even in
            // "batch" mode.
            var timer = null;
            return function() {
                var self = this;
                clearTimeout(timer);
                setTimeout(function(){
                    self.manager.saveChanges();
                }, 10);
            };
        })(),

        _makeResults: function(data) {
            var manager = this.manager;

            try {
                var meta = manager.metadataStore;
                var typeName = meta.getEntityTypeNameForResourceName(this.query.resourceName);
                var typeObj = meta.getEntityType(typeName);
            } catch(ex) {
                // without metadata Breeze returns plain JS objects
                // so we can just return the original array.
                data.results.total = data.inlineCount;
                return data.results;
            }

            // with the metadata, some complex objects are returned on
            // which we can't call ObservableArray/Object (would
            // overrun the stack).

            var props = typeObj.dataProperties;
            var a = data.results.map(function(rec){
                var obj = {};
                props.forEach(function(prop){
                    obj[prop.name] = rec[prop.name];
                });
                obj = new kendo.data.Model(obj);
                syncItems(obj, rec);
                return obj;
            });

            a = new kendo.data.ObservableArray(a);
            a.bind("change", function(ev){
                switch (ev.action) {
                  case "remove":
                    ev.items.forEach(function(item){
                        item.__breezeEntity.entityAspect.setDeleted();
                    });
                    break;
                  case "add":
                    ev.items.forEach(function(item){
                        var entity = manager.createEntity(typeName, item);
                        manager.addEntity(entity);
                        syncItems(item, entity);
                    });
                    break;
                }
            });
            a.total = data.inlineCount;
            return a;
        },

        _makeSchema: function() {
            var schema = {
                total: function(data) {
                    return data.total;
                }
            };
            try {
                var meta = this.manager.metadataStore;
                var typeName = meta.getEntityTypeNameForResourceName(this.query.resourceName);
                var typeObj = meta.getEntityType(typeName);
            } catch(ex) {
                return schema;
            }
            var model = { fields: {} };
            if (typeObj.keyProperties) {
                if (typeObj.keyProperties.length == 1) {
                    model.id = typeObj.keyProperties[0].name;
                } else if (typeObj.keyProperties.length > 1) {
                    console.error("Multiple-key ID not supported");
                }
            }
            typeObj.dataProperties.forEach(function(prop){
                var type = "string";
                if (prop.dataType.isNumeric) {
                    type = "number";
                }
                else if (prop.dataType.isDate) {
                    type = "date";
                }
                else if (prop.dataType.name == "Boolean") {
                    type = "boolean";
                }
                model.fields[prop.name] = {
                    type         : type,
                    defaultValue : prop.defaultValue,
                    nullable     : prop.isNullable,
                };
            });
            schema.model = model;
            return schema;
        }
    });

    exports.Source = kendo.data.DataSource.extend({
        init: function(options) {
            var transport = new BreezeTransport(options);
            options = $.extend({}, {
                transport : transport,
                schema    : transport._makeSchema(),
                batch     : true,
            }, options);
            kendo.data.DataSource.prototype.init.call(this, options);
        }
    });

    function syncItems(observable, entity) {
        var protect = Mutex();
        observable.bind({
            "change": protect(function(ev){
                if (ev.field) {
                    entity[ev.field] = observable[ev.field];
                } else {
                    console.error("Unhandled ObservableObject->Breeze change event", ev);
                }
            })
        });
        entity.entityAspect.propertyChanged.subscribe(protect(function(ev){
            observable.set(ev.propertyName, ev.newValue);
        }));
        observable.__breezeEntity = entity;
    }

    function Mutex() {
        var locked = false;
        return function(f) {
            return function() {
                if (!locked) {
                    locked = true;
                    try { f.apply(this, arguments) }
                    finally { locked = false }
                }
            };
        };
    }

})(jQuery, kendo, breeze);
<commit_msg>Fixed undefined is not a function error when using breeze-kendo and angular.js<commit_after>(function ($, kendo, breeze) {

    var exports = kendo.data.breeze = {};
    var Predicate = breeze.Predicate;
    var Operators = breeze.FilterQueryOp;

    function BreezeTransport(options) {
        if (!options.manager) {
            throw new Error("Please specify a Breeze EntityManager via `manager` option");
        }
        if (!options.query) {
            throw new Error("Please specify a Breeze EntityQuery via `query` option");
        }
        this.manager = options.manager;
        this.query = options.query;
    }

    function makeOperator(op) {
        return {
            eq: Operators.Equals,
            neq: Operators.NotEquals,
            lt: Operators.LessThan,
            lte: Operators.LessThanOrEqual,
            gt: Operators.GreaterThan,
            gte: Operators.GreaterThanOrEqual,
            startswith: Operators.StartsWith,
            endswith: Operators.EndsWith,
            contains: Operators.Contains,
        }[op];
    }

    function makeFilters(args) {
        var filters = args.filters.map(function (f) {
            var field = f.field;
            var operator = makeOperator(f.operator);
            var value = f.value;
            return Predicate.create(field, operator, value);
        });
        if (args.logic == "and") return Predicate.and(filters);
        if (args.logic == "or") return Predicate.or(filters);
        throw new Error("Unsupported predicate logic " + args.logic);
    }

    $.extend(BreezeTransport.prototype, {
        read: function (options) {
            var self = this;
            //console.log("READ", options);
            var query = self.query;
            var args = options.data;
            if (args.filter) {
                query = query.where(makeFilters(args.filter));
            }
            if (args.sort && args.sort.length > 0) {
                query = query.orderBy(args.sort.map(function (col) {
                    return col.field + (col.dir == "desc" ? " desc" : "");
                }).join(", "));
            }
            if (args.page) {
                query = query
                    .skip(args.skip)
                    .take(args.take)
                    .inlineCount();
            }
            try {
                self.manager.executeQuery(query,
                    function (data) {
                        options.success(self._makeResults(data));
                    },
                    function (err) {
                        options.error(err);
                    }
                );
            } catch (ex) {
                console.error(ex);
            }
        },
        create: function (options) {
            //console.log("CREATE", options);
            this._saveChanges();
        },
        update: function (options) {
            //console.log("UPDATE", options);
            this._saveChanges();
        },
        destroy: function (options) {
            //console.log("DESTROY", options);
            this._saveChanges();
        },

        _saveChanges: (function () {
            // throttle, since we will get multiple calls even in
            // "batch" mode.
            var timer = null;
            return function () {
                var self = this;
                clearTimeout(timer);
                setTimeout(function () {
                    self.manager.saveChanges();
                }, 10);
            };
        })(),

        _makeResults: function (data) {
            var manager = this.manager;

            try {
                var meta = manager.metadataStore;
                var typeName = meta.getEntityTypeNameForResourceName(this.query.resourceName);
                var typeObj = meta.getEntityType(typeName);
            } catch (ex) {
                // without metadata Breeze returns plain JS objects
                // so we can just return the original array.
                data.results.total = data.inlineCount;
                return data.results;
            }

            // with the metadata, some complex objects are returned on
            // which we can't call ObservableArray/Object (would
            // overrun the stack).

            var props = typeObj.dataProperties;
            var a = data.results.map(function (rec) {
                var obj = {};
                props.forEach(function (prop) {
                    obj[prop.name] = rec[prop.name];
                });
                obj = new kendo.data.Model(obj);
                syncItems(obj, rec);
                return obj;
            });

            a = new kendo.data.ObservableArray(a);
            a.bind("change", function (ev) {
                switch (ev.action) {
                    case "remove":
                        ev.items.forEach(function (item) {
                            item.__breezeEntity.entityAspect.setDeleted();
                        });
                        break;
                    case "add":
                        ev.items.forEach(function (item) {
                            var entity = manager.createEntity(typeName, item);
                            manager.addEntity(entity);
                            syncItems(item, entity);
                        });
                        break;
                }
            });
            a.total = data.inlineCount;
            return a;
        },

        _makeSchema: function () {
            var schema = {
                total: function (data) {
                    return data.total;
                }
            };
            try {
                var meta = this.manager.metadataStore;
                var typeName = meta.getEntityTypeNameForResourceName(this.query.resourceName);
                var typeObj = meta.getEntityType(typeName);
            } catch (ex) {
                return schema;
            }
            var model = { fields: {} };
            if (typeObj.keyProperties) {
                if (typeObj.keyProperties.length == 1) {
                    model.id = typeObj.keyProperties[0].name;
                } else if (typeObj.keyProperties.length > 1) {
                    console.error("Multiple-key ID not supported");
                }
            }
            typeObj.dataProperties.forEach(function (prop) {
                var type = "string";
                if (prop.dataType.isNumeric) {
                    type = "number";
                }
                else if (prop.dataType.isDate) {
                    type = "date";
                }
                else if (prop.dataType.name == "Boolean") {
                    type = "boolean";
                }
                model.fields[prop.name] = {
                    type: type,
                    defaultValue: prop.defaultValue,
                    nullable: prop.isNullable,
                };
            });
            schema.model = model;
            return schema;
        }
    });

    exports.Source = kendo.data.DataSource.extend({
        init: function (options) {
            var transport = new BreezeTransport(options);
            options = $.extend({}, {
                transport: transport,
                schema: transport._makeSchema(),
                batch: true,
            }, options);
            kendo.data.DataSource.prototype.init.call(this, options);
        }
    });

    function syncItems(observable, entity) {
        var protect = Mutex();
        observable.bind({
            "change": protect(function (ev) {
                if (ev.field) {
                    entity[ev.field] = observable[ev.field];
                } else {
                    console.error("Unhandled ObservableObject->Breeze change event", ev);
                }
            })
        });
        entity.entityAspect.propertyChanged.subscribe(protect(function (ev) {
            observable.set(ev.propertyName, ev.newValue);
        }));
        observable.__breezeEntity = entity;
    }

    function Mutex() {
        var locked = false;
        return function (f) {
            return function () {
                if (!locked) {
                    locked = true;
                    try { f.apply(this, arguments) }
                    finally { locked = false }
                }
            };
        };
    }

})(jQuery, kendo, breeze);
<|endoftext|>


<commit_before>import uniqueId from 'lodash/uniqueId'
import { extendObservable, observable, computed, action, autorun, autorunAsync } from 'mobx'
import EditorTabState, { TabGroup } from 'components/Editor/state'

const state = observable({
  panes: observable.map({}),
  activePaneId: null,
  rootPaneId: null,
  autoCloseEmptyPane: true,
  get rootPane () {
    const rootPane = this.panes.get(this.rootPaneId)
    return rootPane || this.panes.values()[0]
  },
  get activePane () {
    const activePane = this.panes.get(this.activePaneId)
    return activePane || this.rootPane
  },
})

class BasePane {
  constructor (paneConfig) {
    const defaults = {
      id: uniqueId('pane_view_'),
      flexDirection: 'row',
      size: 100,
      parentId: '',
      index: 0,
    }

    paneConfig = Object.assign({}, defaults, paneConfig)
    extendObservable(this, paneConfig)
    state.panes.set(this.id, this)
  }

  @computed
  get parent () {
    return state.panes.get(this.parentId)
  }

  @computed
  get views () {
    return state.panes.values()
      .filter(pane => pane.parentId === this.id)
      .sort((a, b) => a.index - b.index)
  }

  @computed
  get siblings () {
    return this.parent.views
  }

  @computed
  get prev () {
    return this.siblings[this.index - 1]
  }

  @computed
  get next () {
    return this.siblings[this.index + 1]
  }
}

class Pane extends BasePane {
  constructor (paneConfig) {
    super(paneConfig)
    this.contentType = 'tabGroup'
    const tabGroup = this.tabGroup || new TabGroup()
    this.contentId = tabGroup.id
  }

  @observable contentId = ''

  @computed
  get tabGroup () {
    if (this.views.length) return null
    return EditorTabState.tabGroups.get(this.contentId)
  }

  @computed
  get leafChildren () {
    if (!this.views.length) return [this]
    return this.views.reduce((acc, pane) => {
      return acc.concat(pane.leafChildren)
    }, [])
  }

  @action
  destroy () {
    if (this.isRoot) return
    const parent = this.parent
    if (!parent) return

    if (this.views.length) {
      this.views.forEach(pane => pane.destroy())
    }

    let inherior = this.prev || this.next
    if (!inherior) {
      parent.contentId = this.contentId
    } else {
      if (!inherior.tabGroup) {
        const candidates = inherior.leafChildren
        inherior = this.prev ? candidates[candidates.length - 1] : candidates[0]
      }
      inherior.tabGroup.merge(this.tabGroup)
    }

    state.panes.delete(this.id)
  }
}

const rootPane = new Pane({
  id: 'pane_view_1',
  flexDirection: 'row',
  size: 100,
  isRoot: true,
})

state.panes.set(rootPane.id, rootPane)
state.rootPaneId = rootPane.id

autorun(() => {
  state.panes.forEach(parentPane =>
    parentPane.views.forEach((pane, index) => {
      if (pane.index !== index) pane.index = index
    })
  )
})

autorunAsync(() => {
  state.panes.forEach(pane => {
    if (!pane || pane.isRoot) return
    if (pane.views.length === 1) {
      pane.contentId = pane.views[0].contentId
      state.panes.delete(pane.views[0].id)
    }
  })
})

autorun('auto delete pane without tabs', () => {
  if (state.autoCloseEmptyPane) {
    state.panes.forEach(pane => {
      if (!pane) return
      if (!pane.views.length && pane.tabGroup && pane.tabGroup.tabs.length === 0) {
        pane.destroy()
      }
    })
  }
})

export default state
export { Pane }
<commit_msg>:bug: address Coding/WebIDE#180<commit_after>import uniqueId from 'lodash/uniqueId'
import { extendObservable, observable, computed, action, autorun, autorunAsync } from 'mobx'
import EditorTabState, { TabGroup } from 'components/Editor/state'

const state = observable({
  panes: observable.map({}),
  activePaneId: null,
  autoCloseEmptyPane: true,
  get rootPane () {
    const rootPane = this.panes.values().find(pane => pane.isRoot)
    return rootPane || this.panes.values()[0]
  },
  get activePane () {
    const activePane = this.panes.get(this.activePaneId)
    return activePane || this.rootPane
  },
})

class BasePane {
  constructor (paneConfig) {
    const defaults = {
      id: uniqueId('pane_view_'),
      flexDirection: 'row',
      size: 100,
      parentId: '',
      index: 0,
    }

    paneConfig = Object.assign({}, defaults, paneConfig)
    extendObservable(this, paneConfig)
    state.panes.set(this.id, this)
  }

  @computed
  get isRoot () {
    return !Boolean(this.parentId)
  }

  @computed
  get parent () {
    return state.panes.get(this.parentId)
  }

  @computed
  get views () {
    return state.panes.values()
      .filter(pane => pane.parentId === this.id)
      .sort((a, b) => a.index - b.index)
  }

  @computed
  get siblings () {
    return this.parent.views
  }

  @computed
  get prev () {
    return this.siblings[this.index - 1]
  }

  @computed
  get next () {
    return this.siblings[this.index + 1]
  }
}

class Pane extends BasePane {
  constructor (paneConfig) {
    super(paneConfig)
    this.contentType = 'tabGroup'
    const tabGroup = this.tabGroup || new TabGroup()
    this.contentId = tabGroup.id
  }

  @observable contentId = ''

  @computed
  get tabGroup () {
    if (this.views.length) return null
    return EditorTabState.tabGroups.get(this.contentId)
  }

  @computed
  get leafChildren () {
    if (!this.views.length) return [this]
    return this.views.reduce((acc, pane) => {
      return acc.concat(pane.leafChildren)
    }, [])
  }

  @action
  destroy () {
    if (this.isRoot) return
    const parent = this.parent
    if (!parent) return

    if (this.views.length) {
      this.views.forEach(pane => pane.destroy())
    }

    let inherior = this.prev || this.next
    if (!inherior) {
      parent.contentId = this.contentId
    } else {
      if (!inherior.tabGroup) {
        const candidates = inherior.leafChildren
        inherior = this.prev ? candidates[candidates.length - 1] : candidates[0]
      }
      inherior.tabGroup.merge(this.tabGroup)
    }

    state.panes.delete(this.id)
  }
}

const rootPane = new Pane({
  id: 'pane_view_1',
  flexDirection: 'row',
  size: 100,
})

state.panes.set(rootPane.id, rootPane)

autorun('normalize pane indexes', () => {
  state.panes.forEach(parentPane =>
    parentPane.views.forEach((pane, index) => {
      if (pane.index !== index) pane.index = index
    })
  )
})

autorunAsync('short-circuit unnecessary internal pane node', () => {
  // pane.parent -> pane -> lonelyChild
  // => pane.panret -> lonelyChild
  //    delete pane
  state.panes.forEach(pane => {
    if (!pane) return
    if (pane.views.length === 1) {
      const lonelyChild = pane.views[0]
      lonelyChild.parentId = pane.parentId
      state.panes.delete(pane.id)
    }
  })
})

autorun('auto delete pane without tabs', () => {
  if (state.autoCloseEmptyPane) {
    state.panes.forEach(pane => {
      if (!pane) return
      if (!pane.views.length && pane.tabGroup && pane.tabGroup.tabs.length === 0) {
        pane.destroy()
      }
    })
  }
})

export default state
export { Pane }
<|endoftext|>


<commit_before>(function(Browser) {
		Browser.Items = Browser.Items || {};
		Browser.Items.Collections = Browser.Items.Collections || {};
		Browser.Items.Collections.Views =  Browser.Items.Collections.Views || {};

	Browser.Items.Collections.Views.Results = Backbone.View.extend({

		el : $('#zeega-items-list'),

		initialize : function()
		{
			this.collection = new Browser.Items.Collection();
			this.collection.on( 'reset', this.reset, this);
			this._childViews = [];
			this._collectionChildViews = [];
			$('#spinner').spin('large');

			jda.app.isLoading = true;

			this.collection.bind('remove', this.remove, this);

		},
		remove : function(model){
			var deleteIdx = -1;
			for (var i=0;i<this._childViews.length;i++){
				var itemView = this._childViews[i];
				if (itemView.model.id == model.id){
					deleteIdx = i;
					break;
				}
			}
			if (deleteIdx >= 0){
				var removed = this._childViews.splice(deleteIdx,1);
				$(this.el).find(removed[0].el).remove();

				this.updateResultsCounts();
				this.updated = true;
			}

		},
		updateResultsCounts : function(){
			var collectionsCount = this.collection.collectionsCount;
			var itemsCount = this.collection.count;

			$('.jda-results-collections-count').text( jda.app.addCommas(collectionsCount));
			$('.jda-results-items-count').text( jda.app.addCommas(itemsCount));
		},
		render : function()
		{
			var _this = this;

			_this._isRendered = true;
			if(jda.app.currentView == 'thumb'){
				this.el = '#zeega-items-thumbnails';
			} else {
				this.el = '#zeega-items-list';
			}

			//Display collections and items separately if this is not null
			if (this.collection.collectionsCollection != null){

				$("#zeega-results-count").hide();

				this.updateResultsCounts();

				if(jda.app.currentView == 'thumb'){
					$('.collections-thumbnails').empty();
				}else if(jda.app.currentView == 'list'){
					$('#zeega-collections-list').empty();

				}

				//this is getting ridiculous!!
				_.each( _.toArray(this.collection.collectionsCollection), function(item){
					var itemView;
					if(jda.app.currentView == 'thumb'){
						itemView = new Browser.Items.Views.Thumb({model:item});
						$('.collections-thumbnails').append( itemView.render().el );

					} else{

						itemView = new Browser.Items.Views.List({model:item});
						$('#zeega-collections-list').append( itemView.render().el );
					}

					_this._collectionChildViews.push( itemView );

				})

				$('.jda-separate-collections-and-items').show();
				if (this.collection.collectionsCollection.length ==0){
					$('.jda-separate-collections-and-items').find('.jda-results-collections-text').hide();
				}

			} else {
				$('#zeega-results-count-number').text(this.collection.count);
				$("#zeega-results-count").fadeTo(100,1);
			}

			//Display regular old items
			_.each( _.toArray(this.collection), function(item){
				var itemView;
				if(jda.app.currentView == 'thumb'){
					itemView = new Browser.Items.Views.Thumb({model:item});
				} else{

					itemView = new Browser.Items.Views.List({model:item});
				}

				_this._childViews.push( itemView );
				$(_this.el).append( itemView.render().el );
			})

			$('#spinner').spin(false);

			//this is kind of a hack - give all thumbnails same height
			//to fix floaty issues
			if(jda.app.currentView == 'thumb'){
				$(this.el).find('li').css('height','170px');
			}

			$(this.el).show();
			jda.app.isLoading = false;

			//Display related Tags

			if (!_.isUndefined(this.collection.tags) && this.collection.tags.length > 0 && jda.app.currentView != 'event'){

				$("#jda-related-tags button").remove();
				_.each( _.toArray(this.collection.tags), function(tag){

					var tagHTML ='<button class="btn btn-mini btn-danger">'+tag.name+'</button> ';

					$("#jda-related-tags").append(tagHTML);
					$("#jda-related-tags button").filter(":last").click(function(){

						//clear all current search filters
						jda.app.clearSearchFilters();

						//add only tag filter
						VisualSearch.searchBox.addFacet('tag', tag.name, 0);

						jda.app.search({ page:1,});
						return false;
					});
				})

				$("#jda-related-tags-title").fadeTo(100,1);
			}
			else{
				$("#jda-related-tags-title").fadeTo(1000,0);
			}

			return this;
		},

		renderTags : function()
		{
		},

		reset : function()
		{
			if ( this._isRendered )
			{
				$(this.el).empty();
				this._childViews = [];
				//this.render();
			}
		},

		search : function(obj,reset)
		{
			console.log("jda.app.resultsView.search",obj);
			var _this = this;

			this.updated = true;

			$("#zeega-results-count").fadeTo(1000,0.5);

			$("#related-tags-title:visible").fadeTo(1000,0.5);
			//$(this.el).fadeTo(1000,0.5);
			jda.app.isLoading = true;
			if (obj.page == 1) $(this.el).hide();

			$('#spinner').spin('large');

			this.collection.setSearch(obj,reset);
			this.setURLHash();

			// fetch search collection for the list/thumb view
			this.collection.fetch({
				add : (obj.page) > 1 ? true : false,
				success : function(model, response)
				{

					//deselect/unfocus last tag - temp fix till figure out why tag is popping up autocomplete
					VisualSearch.searchBox.disableFacets();

					$('#zeega-results-count-number').html( jda.app.addCommas(response["items_count"]));

					_this.renderTags(response.tags);
					_this.render();

					//If this was a collection search then load the collection view which
					//appears above search results
					if (!_.isUndefined(_this.collectionFilter) && _this.collectionFilter != null){

						_this.collectionFilter.render();
					}
					//If this was a user search then load the user view which
					//appears above search results
					if (!_.isUndefined(_this.userFilter) && _this.userFilter != null){

						_this.userFilter.render();
					}

					if(_this.collection.length<parseInt(response["items_count"])) jda.app.killScroll = false; //to activate infinite scroll again
					else jda.app.killScroll = true;

					jda.app.isLoading = false;	//to activate infinite scroll again
				},
				error : function(model, response){
					console.log('Search failed - model is ' + model);
				}
			});

		},

		setMapBounds : function(bounds)
		{
			this.collection.search.mapBounds = bounds;
			this.setURLHash();
		},

		setView : function(view)
		{
			this.collection.search.view_type = view;
			this.setURLHash();
		},

		setContent : function(content)
		{
			this.collection.search.content = content;
			this.setURLHash();
		},
		clearTags : function(){
			var currentQ = 	this.collection.search.q;
			if (currentQ.indexOf("tag:") >= 0){
				var newQ = currentQ.substring(0,currentQ.indexOf("tag:"));
				this.collection.search.q = newQ;
				this.setURLHash();
			}
		},
		setURLHash : function()
		{
			var obj = this.collection.search;
			console.log('app.resultsCollection.setURLHash',obj);

		 	var hash = '';
		 	if( !_.isUndefined(obj.view_type)) hash += 'view_type=' + obj.view_type + '&';
		 	if( !_.isUndefined(obj.q) && obj.q.length > 0) hash += 'q=' + obj.q + '&';
		 	if( !_.isUndefined(obj.collection) && obj.collection > 0) hash += 'collection=' + obj.collection + '&';
		 	if( !_.isUndefined(obj.user) && obj.user > 0) hash += 'user=' + obj.user + '&';
		 	if( !_.isUndefined(obj.content) )  hash += 'content='+ obj.content + '&';
		 	if( !_.isUndefined(obj.mapBounds) )  hash += 'map_bounds='+ encodeURIComponent(obj.mapBounds) + '&';
		 	if( !_.isUndefined(obj.username) && !_.isNull(obj.username) &&  obj.username.length > 0)  hash += 'username='+ encodeURIComponent(obj.username) + '&';
		 	if( !_.isUndefined(obj.times) )
			{
		 		if( !_.isUndefined(obj.times.start) ) hash += 'min_date='+ obj.times.start + '&';
		 		if( !_.isUndefined(obj.times.end) ) hash += 'max_date='+ obj.times.end + '&';
			}

	 		jda.app.router.navigate(hash,{trigger:false});

		},

		setStartAndEndTimes : function(startDate, endDate)
		{
			var search = this.collection.search;
			search.times = {};
			search.times.start = startDate;
			search.times.end = endDate;
			this.setURLHash()
		},

		getCQLSearchString : function()
		{

			var search = this.collection.search;

			var cqlFilters = [];
			if( !_.isUndefined(search.times) )
			{
				if( !_.isUndefined(search.times.start) )
				{
					startDate = new Date(search.times.start*1000);
					startString = startDate.format('yyyy-mm-dd HH:MM:ss');
					cqlFilters.push("media_date_created >= '" + startString +"'");
				}
				if( !_.isUndefined(search.times.start) )
				{
					endDate = new Date(search.times.end*1000);
					endString = endDate.format('yyyy-mm-dd HH:MM:ss');
					cqlFilters.push("media_date_created <= '" + endString +"'");
				}
			}

			//Tags and Texts are stored in the q property
			if( !_.isUndefined(search.q) )
			{
				var text = search.q;
				if(text)
				{
					if(cqlFilters.length > 0)
					{
						var newCqlFilters = [];
						var prevCqlFiltersString = cqlFilters.join(" AND ");
						newCqlFilters.push(prevCqlFiltersString + " AND (title LIKE '%"+text+"%' OR " + prevCqlFiltersString + " AND media_creator_username LIKE '%"+text+"%' OR " + prevCqlFiltersString + " AND description LIKE '%"+text+"%')");

						cqlFilters = newCqlFilters;
					}
					else
					{
						console.log("map search");
						cqlFilters.push("(title LIKE '%"+text+"%' OR media_creator_username LIKE '%"+text+"%' OR description LIKE '%"+text+"%')");
					}
				}
			}
			if( !_.isUndefined(search.content)&&search.content!="all" )
			{
				var capitalizedContent =  search.content.charAt(0).toUpperCase() + search.content.slice(1);
				cqlFilters.push("media_type='" + capitalizedContent + "'");
			}
			if (cqlFilters.length>0)
			{
				cqlFilterString = cqlFilters.join(" AND ");
			}
			else
			{
				cqlFilterString = null;
			}
			//console.log("CQL filter string " + cqlFilterString);
			return cqlFilterString;
		},

		getSearch : function(){ return this.collection.search },

		//Formats returned results number
		addCommas : function(nStr)
		{
			nStr += '';
			x = nStr.split('.');
			x1 = x[0];
			x2 = x.length > 1 ? '.' + x[1] : '';
			var rgx = /(\d+)(\d{3})/;
			while (rgx.test(x1))
			{
				x1 = x1.replace(rgx, '$1' + ',' + '$2');
			}
			return x1 + x2;
		}

	});

})(jda.module("browser"));<commit_msg>fix tag search link<commit_after>(function(Browser) {
		Browser.Items = Browser.Items || {};
		Browser.Items.Collections = Browser.Items.Collections || {};
		Browser.Items.Collections.Views =  Browser.Items.Collections.Views || {};

	Browser.Items.Collections.Views.Results = Backbone.View.extend({

		el : $('#zeega-items-list'),

		initialize : function()
		{
			this.collection = new Browser.Items.Collection();
			this.collection.on( 'reset', this.reset, this);
			this._childViews = [];
			this._collectionChildViews = [];
			$('#spinner').spin('large');

			jda.app.isLoading = true;

			this.collection.bind('remove', this.remove, this);

		},
		remove : function(model){
			var deleteIdx = -1;
			for (var i=0;i<this._childViews.length;i++){
				var itemView = this._childViews[i];
				if (itemView.model.id == model.id){
					deleteIdx = i;
					break;
				}
			}
			if (deleteIdx >= 0){
				var removed = this._childViews.splice(deleteIdx,1);
				$(this.el).find(removed[0].el).remove();

				this.updateResultsCounts();
				this.updated = true;
			}

		},
		updateResultsCounts : function(){
			var collectionsCount = this.collection.collectionsCount;
			var itemsCount = this.collection.count;

			$('.jda-results-collections-count').text( jda.app.addCommas(collectionsCount));
			$('.jda-results-items-count').text( jda.app.addCommas(itemsCount));
		},
		render : function()
		{
			var _this = this;

			_this._isRendered = true;
			if(jda.app.currentView == 'thumb'){
				this.el = '#zeega-items-thumbnails';
			} else {
				this.el = '#zeega-items-list';
			}

			//Display collections and items separately if this is not null
			if (this.collection.collectionsCollection != null){

				$("#zeega-results-count").hide();

				this.updateResultsCounts();

				if(jda.app.currentView == 'thumb'){
					$('.collections-thumbnails').empty();
				}else if(jda.app.currentView == 'list'){
					$('#zeega-collections-list').empty();

				}

				//this is getting ridiculous!!
				_.each( _.toArray(this.collection.collectionsCollection), function(item){
					var itemView;
					if(jda.app.currentView == 'thumb'){
						itemView = new Browser.Items.Views.Thumb({model:item});
						$('.collections-thumbnails').append( itemView.render().el );

					} else{

						itemView = new Browser.Items.Views.List({model:item});
						$('#zeega-collections-list').append( itemView.render().el );
					}

					_this._collectionChildViews.push( itemView );

				})

				$('.jda-separate-collections-and-items').show();
				if (this.collection.collectionsCollection.length ==0){
					$('.jda-separate-collections-and-items').find('.jda-results-collections-text').hide();
				}

			} else {
				$('#zeega-results-count-number').text(this.collection.count);
				$("#zeega-results-count").fadeTo(100,1);
			}

			//Display regular old items
			_.each( _.toArray(this.collection), function(item){
				var itemView;
				if(jda.app.currentView == 'thumb'){
					itemView = new Browser.Items.Views.Thumb({model:item});
				} else{

					itemView = new Browser.Items.Views.List({model:item});
				}

				_this._childViews.push( itemView );
				$(_this.el).append( itemView.render().el );
			})

			$('#spinner').spin(false);

			//this is kind of a hack - give all thumbnails same height
			//to fix floaty issues
			if(jda.app.currentView == 'thumb'){
				$(this.el).find('li').css('height','170px');
			}

			$(this.el).show();
			jda.app.isLoading = false;

			//Display related Tags

			if (!_.isUndefined(this.collection.tags) && this.collection.tags.length > 0 && jda.app.currentView != 'event'){

				$("#jda-related-tags button").remove();
				_.each( _.toArray(this.collection.tags), function(tag){

					var tagHTML ='<button class="btn btn-mini btn-danger">'+tag.name+'</button> ';

					$("#jda-related-tags").append(tagHTML);
					$("#jda-related-tags button").filter(":last").click(function(){

						//clear all current search filters
						jda.app.clearSearchFilters(false);

						//add only tag filter
						VisualSearch.searchBox.addFacet('tag', tag.name, 0);

						jda.app.search({ page:1,});
						return false;
					});
				})

				$("#jda-related-tags-title").fadeTo(100,1);
			}
			else{
				$("#jda-related-tags-title").fadeTo(1000,0);
			}

			return this;
		},

		renderTags : function()
		{
		},

		reset : function()
		{
			if ( this._isRendered )
			{
				$(this.el).empty();
				this._childViews = [];
				//this.render();
			}
		},

		search : function(obj,reset)
		{
			console.log("jda.app.resultsView.search",obj);
			var _this = this;

			this.updated = true;

			$("#zeega-results-count").fadeTo(1000,0.5);

			$("#related-tags-title:visible").fadeTo(1000,0.5);
			//$(this.el).fadeTo(1000,0.5);
			jda.app.isLoading = true;
			if (obj.page == 1) $(this.el).hide();

			$('#spinner').spin('large');

			this.collection.setSearch(obj,reset);
			this.setURLHash();

			// fetch search collection for the list/thumb view
			this.collection.fetch({
				add : (obj.page) > 1 ? true : false,
				success : function(model, response)
				{

					//deselect/unfocus last tag - temp fix till figure out why tag is popping up autocomplete
					VisualSearch.searchBox.disableFacets();

					$('#zeega-results-count-number').html( jda.app.addCommas(response["items_count"]));

					_this.renderTags(response.tags);
					_this.render();

					//If this was a collection search then load the collection view which
					//appears above search results
					if (!_.isUndefined(_this.collectionFilter) && _this.collectionFilter != null){

						_this.collectionFilter.render();
					}
					//If this was a user search then load the user view which
					//appears above search results
					if (!_.isUndefined(_this.userFilter) && _this.userFilter != null){

						_this.userFilter.render();
					}

					if(_this.collection.length<parseInt(response["items_count"])) jda.app.killScroll = false; //to activate infinite scroll again
					else jda.app.killScroll = true;

					jda.app.isLoading = false;	//to activate infinite scroll again
				},
				error : function(model, response){
					console.log('Search failed - model is ' + model);
				}
			});

		},

		setMapBounds : function(bounds)
		{
			this.collection.search.mapBounds = bounds;
			this.setURLHash();
		},

		setView : function(view)
		{
			this.collection.search.view_type = view;
			this.setURLHash();
		},

		setContent : function(content)
		{
			this.collection.search.content = content;
			this.setURLHash();
		},
		clearTags : function(){
			var currentQ = 	this.collection.search.q;
			if (currentQ.indexOf("tag:") >= 0){
				var newQ = currentQ.substring(0,currentQ.indexOf("tag:"));
				this.collection.search.q = newQ;
				this.setURLHash();
			}
		},
		setURLHash : function()
		{
			var obj = this.collection.search;
			console.log('app.resultsCollection.setURLHash',obj);

		 	var hash = '';
		 	if( !_.isUndefined(obj.view_type)) hash += 'view_type=' + obj.view_type + '&';
		 	if( !_.isUndefined(obj.q) && obj.q.length > 0) hash += 'q=' + obj.q + '&';
		 	if( !_.isUndefined(obj.collection) && obj.collection > 0) hash += 'collection=' + obj.collection + '&';
		 	if( !_.isUndefined(obj.user) && obj.user > 0) hash += 'user=' + obj.user + '&';
		 	if( !_.isUndefined(obj.content) )  hash += 'content='+ obj.content + '&';
		 	if( !_.isUndefined(obj.mapBounds) )  hash += 'map_bounds='+ encodeURIComponent(obj.mapBounds) + '&';
		 	if( !_.isUndefined(obj.username) && !_.isNull(obj.username) &&  obj.username.length > 0)  hash += 'username='+ encodeURIComponent(obj.username) + '&';
		 	if( !_.isUndefined(obj.times) )
			{
		 		if( !_.isUndefined(obj.times.start) ) hash += 'min_date='+ obj.times.start + '&';
		 		if( !_.isUndefined(obj.times.end) ) hash += 'max_date='+ obj.times.end + '&';
			}

	 		jda.app.router.navigate(hash,{trigger:false});

		},

		setStartAndEndTimes : function(startDate, endDate)
		{
			var search = this.collection.search;
			search.times = {};
			search.times.start = startDate;
			search.times.end = endDate;
			this.setURLHash()
		},

		getCQLSearchString : function()
		{

			var search = this.collection.search;

			var cqlFilters = [];
			if( !_.isUndefined(search.times) )
			{
				if( !_.isUndefined(search.times.start) )
				{
					startDate = new Date(search.times.start*1000);
					startString = startDate.format('yyyy-mm-dd HH:MM:ss');
					cqlFilters.push("media_date_created >= '" + startString +"'");
				}
				if( !_.isUndefined(search.times.start) )
				{
					endDate = new Date(search.times.end*1000);
					endString = endDate.format('yyyy-mm-dd HH:MM:ss');
					cqlFilters.push("media_date_created <= '" + endString +"'");
				}
			}

			//Tags and Texts are stored in the q property
			if( !_.isUndefined(search.q) )
			{
				var text = search.q;
				if(text)
				{
					if(cqlFilters.length > 0)
					{
						var newCqlFilters = [];
						var prevCqlFiltersString = cqlFilters.join(" AND ");
						newCqlFilters.push(prevCqlFiltersString + " AND (title LIKE '%"+text+"%' OR " + prevCqlFiltersString + " AND media_creator_username LIKE '%"+text+"%' OR " + prevCqlFiltersString + " AND description LIKE '%"+text+"%')");

						cqlFilters = newCqlFilters;
					}
					else
					{
						console.log("map search");
						cqlFilters.push("(title LIKE '%"+text+"%' OR media_creator_username LIKE '%"+text+"%' OR description LIKE '%"+text+"%')");
					}
				}
			}
			if( !_.isUndefined(search.content)&&search.content!="all" )
			{
				var capitalizedContent =  search.content.charAt(0).toUpperCase() + search.content.slice(1);
				cqlFilters.push("media_type='" + capitalizedContent + "'");
			}
			if (cqlFilters.length>0)
			{
				cqlFilterString = cqlFilters.join(" AND ");
			}
			else
			{
				cqlFilterString = null;
			}
			//console.log("CQL filter string " + cqlFilterString);
			return cqlFilterString;
		},

		getSearch : function(){ return this.collection.search },

		//Formats returned results number
		addCommas : function(nStr)
		{
			nStr += '';
			x = nStr.split('.');
			x1 = x[0];
			x2 = x.length > 1 ? '.' + x[1] : '';
			var rgx = /(\d+)(\d{3})/;
			while (rgx.test(x1))
			{
				x1 = x1.replace(rgx, '$1' + ',' + '$2');
			}
			return x1 + x2;
		}

	});

})(jda.module("browser"));<|endoftext|>