<commit_before>const formats = [
  'bold',
  'font',
  'italic',
  'size',
  'strike',
  'indent',
  'list',
  'direction',
  'align'
];
const UI_LANG = browser.i18n.getUILanguage();
const RTL_LANGS = ['ar', 'fa', 'he'];
const LANG_DIR = RTL_LANGS.includes(UI_LANG) ? 'rtl' : 'ltr';
const TEXT_ALIGN_DIR = LANG_DIR === 'rtl' ? 'right' : 'left';
const SURVEY_PATH = 'https://qsurvey.mozilla.com/s3/notes?ref=sidebar';

// Additional keyboard shortcuts for non-default toolbar buttons
const bindings = {
  strike: {           // ⌘/Ctrl+Shift+X
    key: 'X',
    shiftKey: true,
    shortKey: true,
    handler: function(range) {
      if (this.quill.getFormat(range).strike === true)
        this.quill.formatText(range, 'strike', false);
      else
        this.quill.formatText(range, 'strike', true);
    }
  }, ordered: {       // ⌘/Ctrl+Shift+1
    key: '1',
    shiftKey: true,
    shortKey: true,
    handler: function(range) {
      if (this.quill.getFormat(range).list === 'ordered')
        this.quill.formatLine(range, {'list': false}, true);
      else
        this.quill.formatLine(range, {'list': 'ordered'}, true);
    }
  }, bullet: {        // ⌘/Ctrl+Shift+2
    key: '2',
    shiftKey: true,
    shortKey: true,
    handler: function(range) {
      if (this.quill.getFormat(range).list === 'bullet')
        this.quill.formatLine(range, {'list': false}, true);
      else
        this.quill.formatLine(range, {'list': 'bullet'}, true);
    }
  }
};

const Block = Quill.import('blots/block');
Block.tagName = 'DIV';
Quill.register(Block, true);

const fontSizeStyle = Quill.import('attributors/style/size');
fontSizeStyle.whitelist = ['12px', '14px', '16px', '18px', '20px'];
Quill.register(fontSizeStyle, true);

// add text direction icon to toolbar if RTL
const qlDirection = document.getElementById('ql-direction');
if (LANG_DIR === 'rtl') {
  qlDirection.innerHTML = '<button class="ql-direction" value="rtl"></button>';
}

const quill = new Quill('#editor', {
  theme: 'snow',
  placeholder: browser.i18n.getMessage('emptyPlaceHolder'),
  modules: {
    keyboard: {
      bindings: bindings
    },
    toolbar: '#toolbar'
  },
  formats: formats // enabled formats, see https://github.com/quilljs/quill/issues/1108
});

let userOSKey;

if (navigator.appVersion.indexOf('Mac') !== -1)
  userOSKey = '⌘';
else
  userOSKey = 'Ctrl';

const size = document.getElementsByClassName('ql-size')[0],
      bold = document.getElementsByClassName('ql-bold')[0],
      italic = document.getElementsByClassName('ql-italic')[0],
      strike = document.getElementsByClassName('ql-strike')[0],
      ordered = document.getElementsByClassName('ql-list')[0],
      bullet = document.getElementsByClassName('ql-list')[1];

// Setting button titles in place of tooltips
size.title = browser.i18n.getMessage('fontSizeTitle');
bold.title = browser.i18n.getMessage('boldTitle') + ' (' + userOSKey + '+B)';
italic.title = browser.i18n.getMessage('italicTitle') + ' (' + userOSKey + '+I)';
strike.title = browser.i18n.getMessage('strikethroughTitle') + ' (' + userOSKey + '+Shift+' + bindings.strike.key + ')';
ordered.title = browser.i18n.getMessage('numberedListTitle') + ' (' + userOSKey + '+Shift+' + bindings.ordered.key + ')';
bullet.title = browser.i18n.getMessage('bulletedListTitle') + ' (' + userOSKey + '+Shift+' + bindings.bullet.key + ')';
qlDirection.title = browser.i18n.getMessage('textDirectionTitle');

function handleLocalContent(data) {
  if (!data.hasOwnProperty('notes')) {
    quill.setContents({
      ops: [
        { attributes: { size: 'large', bold: true }, insert: browser.i18n.getMessage('welcomeTitle2') },
        { insert: '\n\n', attributes: { direction: LANG_DIR, align: TEXT_ALIGN_DIR }},
        {
          attributes: { size: 'large' },
          insert:
            browser.i18n.getMessage('welcomeText2')
        },
        { insert: '\n\n', attributes: { direction: LANG_DIR, align: TEXT_ALIGN_DIR }}
      ]
    });
  } else {
    if (JSON.stringify(quill.getContents()) !== JSON.stringify(data.notes)) {
      console.log('Write in Quill');
      quill.setContents(data.notes);
    }
  }
}

function loadContent() {
  return new Promise((resolve) => {
    browser.storage.local.get(['notes'], data => {
      // We load the local content
      handleLocalContent(data);
      // In the meantime we try to load the kinto content
      chrome.runtime.sendMessage({
        action: 'kinto-load'
      });
      resolve();
    });
  });
}

loadContent()
  .then(() => {
    document.getElementById('loading').style.display = 'none';
  });

let ignoreNextLoadEvent = false;
quill.on('text-change', () => {
  const content = quill.getContents();
  browser.storage.local.get("notes")
    .then((data) => {
      if (data.notes != content) {
        console.log('text-change, contentWasSynced: false');
        browser.storage.local.set({ notes: content, contentWasSynced: false })
          .then(() => {
            // Notify other sidebars
            if (!ignoreNextLoadEvent) {
              chrome.runtime.sendMessage('notes@mozilla.com', {
                action: 'text-change'
              });
              // Debounce this second event
              chrome.runtime.sendMessage({
                action: 'metrics-changed',
                context: getPadStats()
              });
              // Debounce this second event
              chrome.runtime.sendMessage({
                action: 'kinto-save',
                content
              });
            } else {
              ignoreNextLoadEvent = false;
            }
          });
      }
    });
});

const enableSync = document.getElementById('enable-sync');
const giveFeedback = document.getElementById('give-feedback');
const noteDiv = document.getElementById('sync-note');
const syncNoteBody = document.getElementById('sync-note-dialog');
const closeButton = document.getElementById('close-button');
enableSync.textContent = browser.i18n.getMessage('syncNotes');
syncNoteBody.textContent = browser.i18n.getMessage('syncNotReady2');
giveFeedback.setAttribute('title', browser.i18n.getMessage('giveFeedback'));
giveFeedback.setAttribute('href', SURVEY_PATH);

closeButton.addEventListener('click', () => {
  noteDiv.classList.toggle('visible');
});

giveFeedback.addEventListener('click', (event) => {
  event.preventDefault();
  enableSync.textContent = 'Disconnected';
  browser.runtime.sendMessage({
    action: 'disconnected'
  });
});

enableSync.onclick = () => {
  browser.runtime.sendMessage({
    action: 'authenticate',
    context: getPadStats()
  });
};

// gets the user-selected theme from local storage and applies respective CSS
// file to the document
function getThemeFromStorage() {
  const getting = browser.storage.local.get(['theme']);
  getting.then(function applyTheme(data) {
    if (data.theme === 'dark') {
      if (! document.getElementById('dark-styles')) {
        const darkSS = document.createElement('link');
        darkSS.id = 'dark-styles';
        darkSS.type = 'text/css';
        darkSS.rel = 'stylesheet';
        darkSS.href = 'styles-dark.css';
        document.getElementsByTagName('head')[0].appendChild(darkSS);
      } else
        return;
    } else if (data.theme === 'default' || data.theme === undefined) {
      if (document.getElementById('dark-styles')) {
        const darkSS = document.getElementById('dark-styles');
        darkSS.parentElement.removeChild(darkSS);
      } else
        return;
    }
  });
}
document.addEventListener('DOMContentLoaded', getThemeFromStorage);

chrome.runtime.onMessage.addListener(eventData => {
  var time = new Date().toLocaleTimeString();
  switch (eventData.action) {
    case 'sync-authenticated':
      chrome.runtime.sendMessage({
          action: 'kinto-load'
        });
      break;
    case 'kinto-loaded':
      if (eventData.data !== null) {
        let remote = eventData.data
        const local = quill.getContents();
        console.log("Merge content");
        let newContent = JSON.parse(JSON.stringify(remote));
        newContent.ops.push({ insert: '\n==========\n\n' });
        content = newContent.ops.concat(local.ops);

        setTimeout(() => {
          console.log("Content is", content);
          quill.setContents(content);
        }, 10);
      }
      break;
    case 'text-change':
      ignoreNextLoadEvent = true;
      loadContent();
      break;
    case 'text-editing':
      enableSync.textContent = 'Editing';
      break;
    case 'text-synced':
      enableSync.textContent = 'Synced at ' + time;
      break;
    case 'text-saved':
      enableSync.textContent = 'Saved at ' + time;
      break;
    case 'theme-changed':
      getThemeFromStorage();
  }
});

// disable drop of links and images into notes
const qlEditor = document.querySelectorAll('.ql-editor');

document.addEventListener('dragover', () => {
  qlEditor[0].classList.add('forbid-cursor');
  browser.runtime.sendMessage({
    action: 'metrics-drag-n-drop',
    context: getPadStats()
  });
  return true;
});

document.addEventListener('dragleave', () => {
  qlEditor[0].classList.remove('forbid-cursor');
  return true;
});

document.addEventListener('drop', (e) => {
  e.preventDefault();
  qlEditor[0].classList.remove('forbid-cursor');
  return false;
});

function getPadStats() {
  const content = quill.getContents();
  const text = quill.getText();
  const styles = {
    size: false,
    bold: false,

    italic: false,
    strike: false,
    list: false
  };

  content.forEach(node => {
    if (node.hasOwnProperty('attributes')) {
      Object.keys(node.attributes).forEach(key => {
        if (styles.hasOwnProperty(key)) {
          styles[key] = true;
        }
      });
    }
  });

  return {
    syncEnabled: false,
    characters: text.length,
    lineBreaks: (text.match(/\n/g) || []).length,
    usesSize: styles.size,
    usesBold: styles.bold,
    usesItalics: styles.italic,
    usesStrikethrough: styles.strike,
    usesList: styles.list
  };
}

// Create a connection with the background script to handle open and
// close events.
browser.runtime.connect();

chrome.runtime.onMessage.addListener(eventData => {
  switch (eventData.action) {
    case 'authenticated':
      if (eventData.err) {
        // TODO: Localize this
        enableSync.textContent = 'Login Failed…';
      } else if (eventData.bearer) {
        enableSync.textContent = 'Synced';
        enableSync.disabled = true;
      }

      break;
  }
});
<commit_msg>Use the Kinto timestamp in the toolbar.<commit_after>const formats = [
  'bold',
  'font',
  'italic',
  'size',
  'strike',
  'indent',
  'list',
  'direction',
  'align'
];
const UI_LANG = browser.i18n.getUILanguage();
const RTL_LANGS = ['ar', 'fa', 'he'];
const LANG_DIR = RTL_LANGS.includes(UI_LANG) ? 'rtl' : 'ltr';
const TEXT_ALIGN_DIR = LANG_DIR === 'rtl' ? 'right' : 'left';
const SURVEY_PATH = 'https://qsurvey.mozilla.com/s3/notes?ref=sidebar';

// Additional keyboard shortcuts for non-default toolbar buttons
const bindings = {
  strike: {           // ⌘/Ctrl+Shift+X
    key: 'X',
    shiftKey: true,
    shortKey: true,
    handler: function(range) {
      if (this.quill.getFormat(range).strike === true)
        this.quill.formatText(range, 'strike', false);
      else
        this.quill.formatText(range, 'strike', true);
    }
  }, ordered: {       // ⌘/Ctrl+Shift+1
    key: '1',
    shiftKey: true,
    shortKey: true,
    handler: function(range) {
      if (this.quill.getFormat(range).list === 'ordered')
        this.quill.formatLine(range, {'list': false}, true);
      else
        this.quill.formatLine(range, {'list': 'ordered'}, true);
    }
  }, bullet: {        // ⌘/Ctrl+Shift+2
    key: '2',
    shiftKey: true,
    shortKey: true,
    handler: function(range) {
      if (this.quill.getFormat(range).list === 'bullet')
        this.quill.formatLine(range, {'list': false}, true);
      else
        this.quill.formatLine(range, {'list': 'bullet'}, true);
    }
  }
};

const Block = Quill.import('blots/block');
Block.tagName = 'DIV';
Quill.register(Block, true);

const fontSizeStyle = Quill.import('attributors/style/size');
fontSizeStyle.whitelist = ['12px', '14px', '16px', '18px', '20px'];
Quill.register(fontSizeStyle, true);

// add text direction icon to toolbar if RTL
const qlDirection = document.getElementById('ql-direction');
if (LANG_DIR === 'rtl') {
  qlDirection.innerHTML = '<button class="ql-direction" value="rtl"></button>';
}

const quill = new Quill('#editor', {
  theme: 'snow',
  placeholder: browser.i18n.getMessage('emptyPlaceHolder'),
  modules: {
    keyboard: {
      bindings: bindings
    },
    toolbar: '#toolbar'
  },
  formats: formats // enabled formats, see https://github.com/quilljs/quill/issues/1108
});

let userOSKey;

if (navigator.appVersion.indexOf('Mac') !== -1)
  userOSKey = '⌘';
else
  userOSKey = 'Ctrl';

const size = document.getElementsByClassName('ql-size')[0],
      bold = document.getElementsByClassName('ql-bold')[0],
      italic = document.getElementsByClassName('ql-italic')[0],
      strike = document.getElementsByClassName('ql-strike')[0],
      ordered = document.getElementsByClassName('ql-list')[0],
      bullet = document.getElementsByClassName('ql-list')[1];

// Setting button titles in place of tooltips
size.title = browser.i18n.getMessage('fontSizeTitle');
bold.title = browser.i18n.getMessage('boldTitle') + ' (' + userOSKey + '+B)';
italic.title = browser.i18n.getMessage('italicTitle') + ' (' + userOSKey + '+I)';
strike.title = browser.i18n.getMessage('strikethroughTitle') + ' (' + userOSKey + '+Shift+' + bindings.strike.key + ')';
ordered.title = browser.i18n.getMessage('numberedListTitle') + ' (' + userOSKey + '+Shift+' + bindings.ordered.key + ')';
bullet.title = browser.i18n.getMessage('bulletedListTitle') + ' (' + userOSKey + '+Shift+' + bindings.bullet.key + ')';
qlDirection.title = browser.i18n.getMessage('textDirectionTitle');

function handleLocalContent(data) {
  if (!data.hasOwnProperty('notes')) {
    quill.setContents({
      ops: [
        { attributes: { size: 'large', bold: true }, insert: browser.i18n.getMessage('welcomeTitle2') },
        { insert: '\n\n', attributes: { direction: LANG_DIR, align: TEXT_ALIGN_DIR }},
        {
          attributes: { size: 'large' },
          insert:
            browser.i18n.getMessage('welcomeText2')
        },
        { insert: '\n\n', attributes: { direction: LANG_DIR, align: TEXT_ALIGN_DIR }}
      ]
    });
  } else {
    if (JSON.stringify(quill.getContents()) !== JSON.stringify(data.notes)) {
      console.log('Write in Quill');
      quill.setContents(data.notes);
    }
  }
}

function loadContent() {
  return new Promise((resolve) => {
    browser.storage.local.get(['notes'], data => {
      // We load the local content
      handleLocalContent(data);
      // In the meantime we try to load the kinto content
      chrome.runtime.sendMessage({
        action: 'kinto-load'
      });
      resolve();
    });
  });
}

loadContent()
  .then(() => {
    document.getElementById('loading').style.display = 'none';
  });

let ignoreNextLoadEvent = false;
quill.on('text-change', () => {
  const content = quill.getContents();
  browser.storage.local.get("notes")
    .then((data) => {
      if (data.notes != content) {
        console.log('text-change, contentWasSynced: false');
        browser.storage.local.set({ notes: content, contentWasSynced: false })
          .then(() => {
            // Notify other sidebars
            if (!ignoreNextLoadEvent) {
              chrome.runtime.sendMessage('notes@mozilla.com', {
                action: 'text-change'
              });
              // Debounce this second event
              chrome.runtime.sendMessage({
                action: 'metrics-changed',
                context: getPadStats()
              });
              // Debounce this second event
              chrome.runtime.sendMessage({
                action: 'kinto-save',
                content
              });
            } else {
              ignoreNextLoadEvent = false;
            }
          });
      }
    });
});

const enableSync = document.getElementById('enable-sync');
const giveFeedback = document.getElementById('give-feedback');
const noteDiv = document.getElementById('sync-note');
const syncNoteBody = document.getElementById('sync-note-dialog');
const closeButton = document.getElementById('close-button');
enableSync.textContent = browser.i18n.getMessage('syncNotes');
syncNoteBody.textContent = browser.i18n.getMessage('syncNotReady2');
giveFeedback.setAttribute('title', browser.i18n.getMessage('giveFeedback'));
giveFeedback.setAttribute('href', SURVEY_PATH);

closeButton.addEventListener('click', () => {
  noteDiv.classList.toggle('visible');
});

giveFeedback.addEventListener('click', (event) => {
  event.preventDefault();
  enableSync.textContent = 'Disconnected';
  browser.runtime.sendMessage({
    action: 'disconnected'
  });
});

enableSync.onclick = () => {
  browser.runtime.sendMessage({
    action: 'authenticate',
    context: getPadStats()
  });
};

// gets the user-selected theme from local storage and applies respective CSS
// file to the document
function getThemeFromStorage() {
  const getting = browser.storage.local.get(['theme']);
  getting.then(function applyTheme(data) {
    if (data.theme === 'dark') {
      if (! document.getElementById('dark-styles')) {
        const darkSS = document.createElement('link');
        darkSS.id = 'dark-styles';
        darkSS.type = 'text/css';
        darkSS.rel = 'stylesheet';
        darkSS.href = 'styles-dark.css';
        document.getElementsByTagName('head')[0].appendChild(darkSS);
      } else
        return;
    } else if (data.theme === 'default' || data.theme === undefined) {
      if (document.getElementById('dark-styles')) {
        const darkSS = document.getElementById('dark-styles');
        darkSS.parentElement.removeChild(darkSS);
      } else
        return;
    }
  });
}
document.addEventListener('DOMContentLoaded', getThemeFromStorage);

chrome.runtime.onMessage.addListener(eventData => {
  let time;
  switch (eventData.action) {
    case 'sync-authenticated':
      chrome.runtime.sendMessage({
          action: 'kinto-load'
        });
      break;
    case 'kinto-loaded':
      if (eventData.data !== null) {
        let remote = eventData.data
        const local = quill.getContents();
        console.log("Merge content");
        let newContent = JSON.parse(JSON.stringify(remote));
        newContent.ops.push({ insert: '\n==========\n\n' });
        content = newContent.ops.concat(local.ops);

        setTimeout(() => {
          console.log("Content is", content);
          quill.setContents(content);
        }, 10);
      }
      break;
    case 'text-change':
      ignoreNextLoadEvent = true;
      loadContent();
      break;
    case 'text-editing':
      enableSync.textContent = 'Editing';
      break;
    case 'text-synced':
      time = new Date(eventData.last_modified).toLocaleTimeString();
      enableSync.textContent = 'Synced at ' + time;
      break;
    case 'text-saved':
      time = new Date().toLocaleTimeString();
      enableSync.textContent = 'Saved at ' + time;
      break;
    case 'theme-changed':
      getThemeFromStorage();
  }
});

// disable drop of links and images into notes
const qlEditor = document.querySelectorAll('.ql-editor');

document.addEventListener('dragover', () => {
  qlEditor[0].classList.add('forbid-cursor');
  browser.runtime.sendMessage({
    action: 'metrics-drag-n-drop',
    context: getPadStats()
  });
  return true;
});

document.addEventListener('dragleave', () => {
  qlEditor[0].classList.remove('forbid-cursor');
  return true;
});

document.addEventListener('drop', (e) => {
  e.preventDefault();
  qlEditor[0].classList.remove('forbid-cursor');
  return false;
});

function getPadStats() {
  const content = quill.getContents();
  const text = quill.getText();
  const styles = {
    size: false,
    bold: false,

    italic: false,
    strike: false,
    list: false
  };

  content.forEach(node => {
    if (node.hasOwnProperty('attributes')) {
      Object.keys(node.attributes).forEach(key => {
        if (styles.hasOwnProperty(key)) {
          styles[key] = true;
        }
      });
    }
  });

  return {
    syncEnabled: false,
    characters: text.length,
    lineBreaks: (text.match(/\n/g) || []).length,
    usesSize: styles.size,
    usesBold: styles.bold,
    usesItalics: styles.italic,
    usesStrikethrough: styles.strike,
    usesList: styles.list
  };
}

// Create a connection with the background script to handle open and
// close events.
browser.runtime.connect();

chrome.runtime.onMessage.addListener(eventData => {
  switch (eventData.action) {
    case 'authenticated':
      if (eventData.err) {
        // TODO: Localize this
        enableSync.textContent = 'Login Failed…';
      } else if (eventData.bearer) {
        enableSync.textContent = 'Synced';
        enableSync.disabled = true;
      }

      break;
  }
});
<|endoftext|>


<commit_before>/*eslint-env node, mocha */

var chai = require('chai');
var assert = chai.assert;

var XMLUtils = require('../src/xmlutils');

describe('XMLUtils#toXML/fromXML mixed inline and definition', function () {
    it('sample should convert to XML that looks the same as sample_xml', function () {
        var sample_definition = {
            "root$attributes": {"xmlns:myns": "http://tempuri.org"},
            "root": {
                "list$namespace": "myns",
                "list$attributes": {"outer": "hello"},
                "list": {
                    "item$namespace": "myns"
                }
            }
        };

        var sample_obj = {
            "root": {
                "$xmlns:xs": "http://www.w3.org/2001/XMLSchema", // Define a new namespace inline
                "list": {
                    "xs:item": [
                        {
                            "$inline": "first",
                            "$": "value1"
                        },
                        {
                            "namespace$": "myns",
                            "$inline": "second",
                            "$": "value2"
                        },
                        "value3"
                    ],
                    "item": "after"
                }
            }
        };

        var sample_xml = [
            '<root xmlns:myns="http://tempuri.org" xmlns:xs="http://www.w3.org/2001/XMLSchema">',
            '  <myns:list outer="hello">',
            '    <xs:item inline="first">value1</xs:item>',
            '    <myns:item inline="second">value2</myns:item>',
            '    <xs:item>value3</xs:item>',
            '    <myns:item>after</myns:item>',
            '  </myns:list>',
            '</root>'
        ].join("\n");

        var xmlutils = new XMLUtils(sample_definition);
        var generated_xml = xmlutils.toXML(sample_obj, "root");
        assert.strictEqual(generated_xml, sample_xml);
    });
});

describe('XMLUtils#toXML/fromXML simple', function () {
  var sample_obj = {
    "root": {
      "first": {
        "firstNested": "",
        "secondNested": ""
      },
      "second": "",
      "last": {
        "stuff": "",
      }
    }
  };

  var sample_xml = [
    "<root>",
    "  <first>",
    "    <firstNested></firstNested>",
    "    <secondNested></secondNested>",
    "  </first>",
    "  <second></second>",
    "  <last>",
    "    <stuff></stuff>",
    "  </last>",
    "</root>"
  ].join("\n");

  var xmlutils = new XMLUtils(null); // TODO: Order is not guarantied with null

  it('sample should convert to XML that looks the same as sample_xml', function () {
    var generated_xml = xmlutils.toXML(sample_obj, "root");
    assert.strictEqual(sample_xml, generated_xml);
  });

  it('should return the order of the elements and convert back to the same sample_xml', function () {
    var generated_obj = xmlutils.fromXML(sample_xml);
    assert.deepEqual(generated_obj["root$order"], ["first", "second", "last"]);
    assert.deepEqual(generated_obj["root"]["first$order"], ["firstNested", "secondNested"]);

    var generated_xml = xmlutils.toXML(generated_obj, "root");
    assert.strictEqual(generated_xml, sample_xml);
  });

});

describe('XMLUtils#toXML/fromXML complex object', function () {
  var sample_obj = {
    "Envelope": {
      "Header": {
        "$myObjectAttib": "aValue1",
        "arrays": {
          "array": [
            {
              "$mySingleArrayAttrib": "test",
              "key": "value1"
            },
            {
              "key": "value2"
            },
            {
              "key": "value3"
            }
          ]
        }
      },
      "Body": {
        "value": "stuff",
        "values": {
          "value": ["a", "b", "c"]
        },
        "Fault": ""
      },
      "soap:SameName": "name1",
      "soap2:SameName": "name2",
      "isArray": ["Stuff"],
    }
  };

  var sample_definition = {
    "Envelope$namespace": "soap",
    "Envelope$attributes": {
      "xmlns:myns1": "http://myns1",
      "xmlns:myns2": "http://myns1",
      "xmlns:soap": "http://www.w3.org/2003/05/soap-envelope/",
      "xmlns:soap2": "http://www.w3.org/2003/05/soap-envelope/",
      "soap:encodingStyle": "http://www.w3.org/2003/05/soap-encoding",
    },
    "Envelope$order": ["Header", "Body"],
    "Envelope": {
      "Header$namespace": "soap",
      "Header": {
        "arrays": {
          "array$type": [],
          "array$attributes": {
            "SameOnAll": "same"
          },
        }
      },
      "Body$namespace": "soap",
      "Body": {
        "value": "string",
        "values$attributes": {
          "xmlns:stuff": "http://www.w3.org/2003/05/soap-encoding"
        },
        "values": {
          "value$namespace": "stuff",
          "value$type": [],
        },
      },
      "isArray$type": [],
      "myns1:overlaps$type": [], // TODO: implement
      "myns2:overlaps$type": []
    }
  };

  var xmlutils = new XMLUtils(sample_definition);

  //console.log(JSON.stringify(result, null, 2));
  it('should convert to XML and back to js again', function () {
    var xml = xmlutils.toXML(sample_obj, "Envelope");
    //console.log(xml);
    assert.isTrue(xml.startsWith("<soap:Envelope"));
    var obj = xmlutils.fromXML(xml);
    //console.log(JSON.stringify(obj, null, 2));
    assert.deepEqual(obj.Envelope.Body.values.value, sample_obj.Envelope.Body.values.value);
    //assert.deepEqual(obj, merge(sample_definition, sample_obj));
  });
});

describe('XMLUtils#toXML/fromXML simple', function () {
    var sample_obj = {
        "root": {
            "first": {
                "firstNested": "",
                "secondNested": ""
            },
            "second": "",
            "last": {
                "stuff": "test",
            }
        }
    };

    var sample_xml = [
        "<root>",
        "  <first>",
        "    <firstNested />",
        "    <secondNested />",
        "  </first>",
        "  <second />",
        "  <last>",
        "    <stuff>test</stuff>",
        "  </last>",
        "</root>"
    ].join("\n");

    var xmlutils = new XMLUtils(null);

    it('sample should convert to XML that looks the same as sample_xml', function () {
        var generated_xml = xmlutils.toXML(sample_obj, "root", 2, true);
        assert.strictEqual(sample_xml, generated_xml);
    });
});

describe('Binary encoding', function () {
    var definition = {
        complexAll: {
            tickerSymbola$type: "base64Binary",
            tickerSymbolb$type: "hexBinary",
        }
    };

    var obj = {
        complexAll: {
            tickerSymbola: Buffer.from("ÆØÅ"),
            tickerSymbolb: Buffer.from("ÅØÆ")
        },
        complexAll$order: [
            "tickerSymbola",
            "tickerSymbolb"
        ]
    };

    var xml = [
        "<complexAll>",
        "  <tickerSymbola>w4bDmMOF</tickerSymbola>",
        "  <tickerSymbolb>c385c398c386</tickerSymbolb>",
        "</complexAll>",
    ].join("\n");

    it('Base64/Hex Encode', function () {
        var xmlutils = new XMLUtils(definition);
        var generatedXml = xmlutils.toXML(obj, "complexAll");
        assert.strictEqual(generatedXml, xml);
    });
    it('Base64/Hex Decode', function () {
        var xmlutils = new XMLUtils(definition);
        var generatedObj = xmlutils.fromXML(xml);
        assert.deepEqual(generatedObj, obj);
    });
});

describe('Types', function () {
    var definition = {
        complexAll: {
            boolean1$type: "boolean",
            boolean2$type: "boolean",
            float$type: "float",
            int$type: "int",
        },
    };
    var obj = {
        complexAll: {
            boolean1: true,
            boolean2: false,
            float: 1.1,
            int: 1,
        },
        complexAll$order: [
            "boolean1",
            "boolean2",
            "float",
            "int"
        ]
    };

    var xml = [
        "<complexAll>",
        "  <boolean1>true</boolean1>",
        "  <boolean2>false</boolean2>",
        "  <float>1.1</float>",
        "  <int>1</int>",
        "</complexAll>",
    ].join("\n");

    it('to', function () {
        var xmlutils = new XMLUtils(definition);
        var generatedXml = xmlutils.toXML(obj, "complexAll");
        assert.strictEqual(generatedXml, xml);
    });

    it('from', function () {
        var xmlutils = new XMLUtils(definition);
        var generatedObj = xmlutils.fromXML(xml, "complexAll");
        assert.deepEqual(generatedObj, obj);
    });
});

describe('Escaping', function () {
    var obj = {
        complexAll: {
            nestedXml: "<xml>...</xml>",
            charsXml: "%<>\"'",
            charsRaw: "><&",
            cdata: "<![CDATA['><]]>",
            comment: "<!-- my comment -->",
        }
    };
    var objParsed = {
        complexAll: {
            nestedXml: "<xml>...</xml>",
            charsXml: "%<>\"'",
            charsRaw: "><&",
            cdata: "'><",
            comment: '<!-- my comment -->',
        },
        complexAll$order: [
            "nestedXml",
            "charsXml",
            "charsRaw",
            "cdata",
            "comment"
        ]
    };

    var xml = [
        "<complexAll>",
        "  <nestedXml><xml>...</xml></nestedXml>",
        "  <charsXml>%<>"'</charsXml>",
        "  <charsRaw>><&</charsRaw>",
        "  <cdata><![CDATA['><]]></cdata>",
        "  <comment><!-- my comment --></comment>",
        "</complexAll>",
    ].join("\n");

    it('to', function () {
        var xmlutils = new XMLUtils();
        var generatedXml = xmlutils.toXML(obj, "complexAll");
        assert.strictEqual(generatedXml, xml);
    });

    it('from', function () {
        var xmlutils = new XMLUtils();
        var generatedObj = xmlutils.fromXML(xml, "complexAll");
        assert.deepEqual(generatedObj, objParsed);
    });
});

describe('Sample generation', function () {
    it('Simple', function () {
        var definition = {
            complexAll$attributes: {"xmlns:myns": "http://tempuri.org", "xmlns:xs": "http://www.w3.org/2001/XMLSchema"},
            complexAll$namespace: "myns",
            complexAll: {
                tickerSymbola$type: "string",
                tickerSymbola$namespace: "myns",
                tickerSymbolb$type: "string",
                tickerSymbolb$namespace: "myns",
            },
        };

        var expected = {
            complexAll: {
                tickerSymbola: " ",
                tickerSymbolb: " "
            }
        };

        var xmlutils = new XMLUtils(definition);
        var sample = xmlutils.generateSample("complexAll");
        assert.deepEqual(sample, expected);
    });

    it('Length and Array', function () {
        var definition = {
            complexAllLength$attributes: {
                "xmlns:myns": "http://tempuri.org",
                "xmlns:xs": "http://www.w3.org/2001/XMLSchema"
            },
            complexAllLength$namespace: "myns",
            complexAllLength: {
                tickerSymbola$type: "string",
                tickerSymbola$length: [10, 10],
                tickerSymbola$namespace: "myns",
                tickerSymbolb$type: ["string", 2, 2],
                tickerSymbolb$length: [1, 1],
                tickerSymbolb$namespace: "myns",
            }
        };

        var expected = {
            complexAllLength: {
                tickerSymbola: "          ",
                tickerSymbolb: [" ", " "]
            }
        };

        var xmlutils = new XMLUtils(definition);
        var sample = xmlutils.generateSample("complexAllLength");
        assert.deepEqual(sample, expected);
    });
});

describe('Test soap envelope', function () {
    const soapDefinition = {
        Envelope$namespace: "soap",
        Envelope$attributes: {
            "xmlns:soap": "http://www.w3.org/2003/05/soap-envelope/",
            "soap:encodingStyle": "http://www.w3.org/2003/05/soap-encoding"
        },
        Envelope$order: ["Header", "Body"],
        Envelope: {
            Header$namespace: "soap",
            Body$namespace: "soap",
            Body: {
                complexAll: {
                    boolean1$type: "boolean",
                },
            }
        }
    };

    var expectedSoapObj = {
        Envelope: {
            Header: "",
            Body: {
                complexAll: {
                    boolean1: true,
                }
            }
        }
    };

    var expectedSoapXml = [
        '<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope/" soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding">',
        '  <soap:Header />',
        '  <soap:Body>',
        '    <complexAll>',
        '      <boolean1>true</boolean1>',
        '    </complexAll>',
        '  </soap:Body>',
        '</soap:Envelope>',
    ].join("\n");

    var soapXmlUtils = new XMLUtils(soapDefinition);

    it('to', function () {
        var generatedSoapXml = soapXmlUtils.toXML(expectedSoapObj, "Envelope", 2, true);
        assert.strictEqual(generatedSoapXml, expectedSoapXml);
    });

    it('from', function () {
        var generatedSoapObj = soapXmlUtils.fromXML(expectedSoapXml);
        assert.deepEqual(generatedSoapObj, expectedSoapObj);
    });
});

describe('Test soap envelope', function () {
    const definition = {
        complexAll: {
            xmlBlob$type: "xml",
        }
    };

    var expectedXml = [
        '<complexAll>',
        '  <xmlBlob>',
        '    <myxml>stuff</myxml>',
        '  </xmlBlob>',
        '</complexAll>',
    ].join("\n");

    var obj = {
        complexAll: {
            xmlBlob: "<myxml>stuff</myxml>"
        }
    };

    it('to', function () {
        var xmlUtils = new XMLUtils(definition);
        var generatedXml = xmlUtils.toXML(obj, "complexAll");
        assert.strictEqual(generatedXml, expectedXml);
    });
});
<commit_msg>Add documentation for length and type information<commit_after>/*eslint-env node, mocha */

var chai = require('chai');
var assert = chai.assert;

var XMLUtils = require('../src/xmlutils');

describe('XMLUtils#toXML/fromXML mixed inline and definition', function () {
    it('sample should convert to XML that looks the same as sample_xml', function () {
        var sample_definition = {
            "root$attributes": {"xmlns:myns": "http://tempuri.org"},
            "root": {
                "list$namespace": "myns",
                "list$attributes": {"outer": "hello"},
                "list": {
                    "item$namespace": "myns"
                }
            }
        };

        var sample_obj = {
            "root": {
                "$xmlns:xs": "http://www.w3.org/2001/XMLSchema", // Define a new namespace inline
                "list": {
                    "xs:item": [
                        {
                            "$inline": "first",
                            "$": "value1"
                        },
                        {
                            "namespace$": "myns",
                            "$inline": "second",
                            "$": "value2"
                        },
                        "value3"
                    ],
                    "item": "after"
                }
            }
        };

        var sample_xml = [
            '<root xmlns:myns="http://tempuri.org" xmlns:xs="http://www.w3.org/2001/XMLSchema">',
            '  <myns:list outer="hello">',
            '    <xs:item inline="first">value1</xs:item>',
            '    <myns:item inline="second">value2</myns:item>',
            '    <xs:item>value3</xs:item>',
            '    <myns:item>after</myns:item>',
            '  </myns:list>',
            '</root>'
        ].join("\n");

        var xmlutils = new XMLUtils(sample_definition);
        var generated_xml = xmlutils.toXML(sample_obj, "root");
        assert.strictEqual(generated_xml, sample_xml);
    });
});

describe('XMLUtils#toXML/fromXML simple', function () {
  var sample_obj = {
    "root": {
      "first": {
        "firstNested": "",
        "secondNested": ""
      },
      "second": "",
      "last": {
        "stuff": "",
      }
    }
  };

  var sample_xml = [
    "<root>",
    "  <first>",
    "    <firstNested></firstNested>",
    "    <secondNested></secondNested>",
    "  </first>",
    "  <second></second>",
    "  <last>",
    "    <stuff></stuff>",
    "  </last>",
    "</root>"
  ].join("\n");

  var xmlutils = new XMLUtils(null); // TODO: Order is not guarantied with null

  it('sample should convert to XML that looks the same as sample_xml', function () {
    var generated_xml = xmlutils.toXML(sample_obj, "root");
    assert.strictEqual(sample_xml, generated_xml);
  });

  it('should return the order of the elements and convert back to the same sample_xml', function () {
    var generated_obj = xmlutils.fromXML(sample_xml);
    assert.deepEqual(generated_obj["root$order"], ["first", "second", "last"]);
    assert.deepEqual(generated_obj["root"]["first$order"], ["firstNested", "secondNested"]);

    var generated_xml = xmlutils.toXML(generated_obj, "root");
    assert.strictEqual(generated_xml, sample_xml);
  });

});

describe('XMLUtils#toXML/fromXML complex object', function () {
  var sample_obj = {
    "Envelope": {
      "Header": {
        "$myObjectAttib": "aValue1",
        "arrays": {
          "array": [
            {
              "$mySingleArrayAttrib": "test",
              "key": "value1"
            },
            {
              "key": "value2"
            },
            {
              "key": "value3"
            }
          ]
        }
      },
      "Body": {
        "value": "stuff",
        "values": {
          "value": ["a", "b", "c"]
        },
        "Fault": ""
      },
      "soap:SameName": "name1",
      "soap2:SameName": "name2",
      "isArray": ["Stuff"],
    }
  };

  var sample_definition = {
    "Envelope$namespace": "soap",
    "Envelope$attributes": {
      "xmlns:myns1": "http://myns1",
      "xmlns:myns2": "http://myns1",
      "xmlns:soap": "http://www.w3.org/2003/05/soap-envelope/",
      "xmlns:soap2": "http://www.w3.org/2003/05/soap-envelope/",
      "soap:encodingStyle": "http://www.w3.org/2003/05/soap-encoding",
    },
    "Envelope$order": ["Header", "Body"],
    "Envelope": {
      "Header$namespace": "soap",
      "Header": {
        "arrays": {
          "array$type": [],
          "array$attributes": {
            "SameOnAll": "same"
          },
        }
      },
      "Body$namespace": "soap",
      "Body": {
        "value": "string",
        "values$attributes": {
          "xmlns:stuff": "http://www.w3.org/2003/05/soap-encoding"
        },
        "values": {
          "value$namespace": "stuff",
          "value$type": [],
        },
      },
      "isArray$type": [],
      "myns1:overlaps$type": [], // TODO: implement
      "myns2:overlaps$type": []
    }
  };

  var xmlutils = new XMLUtils(sample_definition);

  //console.log(JSON.stringify(result, null, 2));
  it('should convert to XML and back to js again', function () {
    var xml = xmlutils.toXML(sample_obj, "Envelope");
    //console.log(xml);
    assert.isTrue(xml.startsWith("<soap:Envelope"));
    var obj = xmlutils.fromXML(xml);
    //console.log(JSON.stringify(obj, null, 2));
    assert.deepEqual(obj.Envelope.Body.values.value, sample_obj.Envelope.Body.values.value);
    //assert.deepEqual(obj, merge(sample_definition, sample_obj));
  });
});

describe('XMLUtils#toXML/fromXML simple', function () {
    var sample_obj = {
        "root": {
            "first": {
                "firstNested": "",
                "secondNested": ""
            },
            "second": "",
            "last": {
                "stuff": "test",
            }
        }
    };

    var sample_xml = [
        "<root>",
        "  <first>",
        "    <firstNested />",
        "    <secondNested />",
        "  </first>",
        "  <second />",
        "  <last>",
        "    <stuff>test</stuff>",
        "  </last>",
        "</root>"
    ].join("\n");

    var xmlutils = new XMLUtils(null);

    it('sample should convert to XML that looks the same as sample_xml', function () {
        var generated_xml = xmlutils.toXML(sample_obj, "root", 2, true);
        assert.strictEqual(sample_xml, generated_xml);
    });
});

describe('Binary encoding', function () {
    var definition = {
        complexAll: {
            tickerSymbola$type: "base64Binary",
            tickerSymbolb$type: "hexBinary",
        }
    };

    var obj = {
        complexAll: {
            tickerSymbola: Buffer.from("ÆØÅ"),
            tickerSymbolb: Buffer.from("ÅØÆ")
        },
        complexAll$order: [
            "tickerSymbola",
            "tickerSymbolb"
        ]
    };

    var xml = [
        "<complexAll>",
        "  <tickerSymbola>w4bDmMOF</tickerSymbola>",
        "  <tickerSymbolb>c385c398c386</tickerSymbolb>",
        "</complexAll>",
    ].join("\n");

    it('Base64/Hex Encode', function () {
        var xmlutils = new XMLUtils(definition);
        var generatedXml = xmlutils.toXML(obj, "complexAll");
        assert.strictEqual(generatedXml, xml);
    });
    it('Base64/Hex Decode', function () {
        var xmlutils = new XMLUtils(definition);
        var generatedObj = xmlutils.fromXML(xml);
        assert.deepEqual(generatedObj, obj);
    });
});

describe('Types', function () {
    var definition = {
        complexAll: {
            boolean1$type: "boolean",
            boolean2$type: "boolean",
            float$type: "float",
            int$type: "int",
        },
    };
    var obj = {
        complexAll: {
            boolean1: true,
            boolean2: false,
            float: 1.1,
            int: 1,
        },
        complexAll$order: [
            "boolean1",
            "boolean2",
            "float",
            "int"
        ]
    };

    var xml = [
        "<complexAll>",
        "  <boolean1>true</boolean1>",
        "  <boolean2>false</boolean2>",
        "  <float>1.1</float>",
        "  <int>1</int>",
        "</complexAll>",
    ].join("\n");

    it('to', function () {
        var xmlutils = new XMLUtils(definition);
        var generatedXml = xmlutils.toXML(obj, "complexAll");
        assert.strictEqual(generatedXml, xml);
    });

    it('from', function () {
        var xmlutils = new XMLUtils(definition);
        var generatedObj = xmlutils.fromXML(xml, "complexAll");
        assert.deepEqual(generatedObj, obj);
    });
});

describe('Escaping', function () {
    var obj = {
        complexAll: {
            nestedXml: "<xml>...</xml>",
            charsXml: "%<>\"'",
            charsRaw: "><&",
            cdata: "<![CDATA['><]]>",
            comment: "<!-- my comment -->",
        }
    };
    var objParsed = {
        complexAll: {
            nestedXml: "<xml>...</xml>",
            charsXml: "%<>\"'",
            charsRaw: "><&",
            cdata: "'><",
            comment: '<!-- my comment -->',
        },
        complexAll$order: [
            "nestedXml",
            "charsXml",
            "charsRaw",
            "cdata",
            "comment"
        ]
    };

    var xml = [
        "<complexAll>",
        "  <nestedXml><xml>...</xml></nestedXml>",
        "  <charsXml>%<>"'</charsXml>",
        "  <charsRaw>><&</charsRaw>",
        "  <cdata><![CDATA['><]]></cdata>",
        "  <comment><!-- my comment --></comment>",
        "</complexAll>",
    ].join("\n");

    it('to', function () {
        var xmlutils = new XMLUtils();
        var generatedXml = xmlutils.toXML(obj, "complexAll");
        assert.strictEqual(generatedXml, xml);
    });

    it('from', function () {
        var xmlutils = new XMLUtils();
        var generatedObj = xmlutils.fromXML(xml, "complexAll");
        assert.deepEqual(generatedObj, objParsed);
    });
});

describe('Sample generation', function () {
    it('Simple', function () {
        var definition = {
            complexAll$attributes: {"xmlns:myns": "http://tempuri.org", "xmlns:xs": "http://www.w3.org/2001/XMLSchema"},
            complexAll$namespace: "myns",
            complexAll: {
                tickerSymbola$type: "string",
                tickerSymbola$namespace: "myns",
                tickerSymbolb$type: "string",
                tickerSymbolb$namespace: "myns",
            },
        };

        var expected = {
            complexAll: {
                tickerSymbola: " ",
                tickerSymbolb: " "
            }
        };

        var xmlutils = new XMLUtils(definition);
        var sample = xmlutils.generateSample("complexAll");
        assert.deepEqual(sample, expected);
    });

    it('Length and Array', function () {
        var definition = {
            complexAllLength$attributes: {
                "xmlns:myns": "http://tempuri.org",
                "xmlns:xs": "http://www.w3.org/2001/XMLSchema"
            },
            complexAllLength$namespace: "myns",
            complexAllLength: {
                tickerSymbola$type: "string",
                tickerSymbola$length: [10, 10], // String of length 10
                tickerSymbola$namespace: "myns",
                tickerSymbolb$type: ["string", 2, 2], // String array with length of 2
                tickerSymbolb$length: [1, 1], // Item length of 1
                tickerSymbolb$namespace: "myns",
            }
        };

        var expected = {
            complexAllLength: {
                tickerSymbola: "          ",
                tickerSymbolb: [" ", " "]
            }
        };

        var xmlutils = new XMLUtils(definition);
        var sample = xmlutils.generateSample("complexAllLength");
        assert.deepEqual(sample, expected);
    });
});

describe('Test soap envelope', function () {
    const soapDefinition = {
        Envelope$namespace: "soap",
        Envelope$attributes: {
            "xmlns:soap": "http://www.w3.org/2003/05/soap-envelope/",
            "soap:encodingStyle": "http://www.w3.org/2003/05/soap-encoding"
        },
        Envelope$order: ["Header", "Body"],
        Envelope: {
            Header$namespace: "soap",
            Body$namespace: "soap",
            Body: {
                complexAll: {
                    boolean1$type: "boolean",
                },
            }
        }
    };

    var expectedSoapObj = {
        Envelope: {
            Header: "",
            Body: {
                complexAll: {
                    boolean1: true,
                }
            }
        }
    };

    var expectedSoapXml = [
        '<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope/" soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding">',
        '  <soap:Header />',
        '  <soap:Body>',
        '    <complexAll>',
        '      <boolean1>true</boolean1>',
        '    </complexAll>',
        '  </soap:Body>',
        '</soap:Envelope>',
    ].join("\n");

    var soapXmlUtils = new XMLUtils(soapDefinition);

    it('to', function () {
        var generatedSoapXml = soapXmlUtils.toXML(expectedSoapObj, "Envelope", 2, true);
        assert.strictEqual(generatedSoapXml, expectedSoapXml);
    });

    it('from', function () {
        var generatedSoapObj = soapXmlUtils.fromXML(expectedSoapXml);
        assert.deepEqual(generatedSoapObj, expectedSoapObj);
    });
});

describe('Test soap envelope', function () {
    const definition = {
        complexAll: {
            xmlBlob$type: "xml",
        }
    };

    var expectedXml = [
        '<complexAll>',
        '  <xmlBlob>',
        '    <myxml>stuff</myxml>',
        '  </xmlBlob>',
        '</complexAll>',
    ].join("\n");

    var obj = {
        complexAll: {
            xmlBlob: "<myxml>stuff</myxml>"
        }
    };

    it('to', function () {
        var xmlUtils = new XMLUtils(definition);
        var generatedXml = xmlUtils.toXML(obj, "complexAll");
        assert.strictEqual(generatedXml, expectedXml);
    });
});
<|endoftext|>


<commit_before>'use strict';

/*
- Clean all generated dirs
- Copy error pages
- Styles
- Scripts
- Images

Dev:
- Sourcemaps
- BrowserSync
- Watch

Build:
- Image compression

Test:
- Continuous
- Spec reporter
*/

var src = './assets/',
    dest = src + 'public/',
    distDir = src + 'dist/',
    snapshotDir = dest + '999-SNAPSHOT/',
    govuk = {
      elements: src + 'govuk_elements',
      template: src + 'govuk_elements/govuk',
      images: src + 'govuk_elements/govuk/public/images/'
    };

module.exports = {
  dest: dest,
  distDir: distDir,

  dev: {
    dest: snapshotDir,
  },
  prod: {
    dest: distDir,
  },

  production: {
    jsSrc: distDir + 'javascripts/*.js',
    jsDest: distDir + 'javascripts/',
    imagesDir: distDir + 'images',
    cssSrc: distDir + 'stylesheets',
    dest: distDir
  },

  scripts: {
    src: src + 'javascripts/modules/**/*.js',
    dest: snapshotDir + 'javascripts',
    entryPoint: src + 'javascripts/application.js',
    jshintExclude: '!' + src + 'javascripts/**/{base64v1_0,details.polyfill,mdtpdf}.js',
    jscsSrc: '.jscsrc',
    gulpTasks: 'gulpfile.js/**/*.js',
    encryptionSrc: src + 'javascripts/encryption/**/*.js',
    encryptionDest: {
      dev: snapshotDir + 'javascripts/',
      prod: distDir + 'javascripts/'
    },
    vendorDest: {
      dev: snapshotDir + 'javascripts/vendor/',
      prod: distDir + 'javascripts/vendor/'
    },

    modernizr: {
      options: [
        'setClasses',
        'html5printshiv',
        'testProp'
      ],
      tests: [
        'touchevents'
      ],
      excludeTests: [
        'flash',
        'hidden'
      ],
      files : {
        src: [
          src + '{javascripts,scss,govuk_*}/**/*.{js,scss}',
          '!**[^node_modules]/**/modernizr.js'
        ]
      }
    }
  },

  images: {
    govuk: govuk.images + '**/*',
    dev: {
      src: src + 'images/**/*',
      dest: snapshotDir + 'images'
    },
    prod: {
      dest: distDir + 'images'
    }
  },

  sass: {
    src: src + 'scss/**/*.scss',
    govukSrc: govuk.template + '/public/sass/**/*.scss',
    govukElementsSrc: govuk.elements + '/public/sass/**/*.scss',
    dev: {
      dest: snapshotDir + 'stylesheets/',
      settings: {
        sourceComments: true,
        includePaths: [govuk.template + '/public/sass'],
        outputStyle: 'expanded'
      },
      sourceMapsDir: './maps'
    },
    prod: {
      dest: distDir + 'stylesheets/',
      settings: {
        includePaths: [govuk.template + '/public/sass'],
        outputStyle: 'compressed'
      }
    }
  },

  browserify: {
    bundleConfigs: [{
      entries: [
        src + 'javascripts/application.js'
      ],
      dev: {
        dest: snapshotDir + 'javascripts'
      },
      prod: {
        dest: distDir + 'javascripts'
      },
      outputName: 'application.js'
    },{
      entries: [
        src + 'javascripts/export/fingerprint.js'
      ],
      dev: {
        dest: snapshotDir + 'javascripts'
      },
      prod: {
        dest: distDir + 'javascripts'
      },
      outputName: 'mdtpdf.js'
    }]
  },

  test: {
    specsScr: src + 'test/specs/unit/**/*.js',
    fixturesScr: src + 'test/specs/fixtures/*.html',
    karmaConfig: src + 'test/config/karma.conf.js'
  },

  browserSync: {
    ui: false,
    port: 9032,
    open: false,
    server: {
      routes: {
        '/assets': dest,
        '/component-library': './component-library'
      }
    }
  }
};
<commit_msg>add the baseDir back as assets are not served without it<commit_after>'use strict';

/*
- Clean all generated dirs
- Copy error pages
- Styles
- Scripts
- Images

Dev:
- Sourcemaps
- BrowserSync
- Watch

Build:
- Image compression

Test:
- Continuous
- Spec reporter
*/

var src = './assets/',
    dest = src + 'public/',
    distDir = src + 'dist/',
    snapshotDir = dest + '999-SNAPSHOT/',
    govuk = {
      elements: src + 'govuk_elements',
      template: src + 'govuk_elements/govuk',
      images: src + 'govuk_elements/govuk/public/images/'
    };

module.exports = {
  dest: dest,
  distDir: distDir,

  dev: {
    dest: snapshotDir,
  },
  prod: {
    dest: distDir,
  },

  production: {
    jsSrc: distDir + 'javascripts/*.js',
    jsDest: distDir + 'javascripts/',
    imagesDir: distDir + 'images',
    cssSrc: distDir + 'stylesheets',
    dest: distDir
  },

  scripts: {
    src: src + 'javascripts/modules/**/*.js',
    dest: snapshotDir + 'javascripts',
    entryPoint: src + 'javascripts/application.js',
    jshintExclude: '!' + src + 'javascripts/**/{base64v1_0,details.polyfill,mdtpdf}.js',
    jscsSrc: '.jscsrc',
    gulpTasks: 'gulpfile.js/**/*.js',
    encryptionSrc: src + 'javascripts/encryption/**/*.js',
    encryptionDest: {
      dev: snapshotDir + 'javascripts/',
      prod: distDir + 'javascripts/'
    },
    vendorDest: {
      dev: snapshotDir + 'javascripts/vendor/',
      prod: distDir + 'javascripts/vendor/'
    },

    modernizr: {
      options: [
        'setClasses',
        'html5printshiv',
        'testProp'
      ],
      tests: [
        'touchevents'
      ],
      excludeTests: [
        'flash',
        'hidden'
      ],
      files : {
        src: [
          src + '{javascripts,scss,govuk_*}/**/*.{js,scss}',
          '!**[^node_modules]/**/modernizr.js'
        ]
      }
    }
  },

  images: {
    govuk: govuk.images + '**/*',
    dev: {
      src: src + 'images/**/*',
      dest: snapshotDir + 'images'
    },
    prod: {
      dest: distDir + 'images'
    }
  },

  sass: {
    src: src + 'scss/**/*.scss',
    govukSrc: govuk.template + '/public/sass/**/*.scss',
    govukElementsSrc: govuk.elements + '/public/sass/**/*.scss',
    dev: {
      dest: snapshotDir + 'stylesheets/',
      settings: {
        sourceComments: true,
        includePaths: [govuk.template + '/public/sass'],
        outputStyle: 'expanded'
      },
      sourceMapsDir: './maps'
    },
    prod: {
      dest: distDir + 'stylesheets/',
      settings: {
        includePaths: [govuk.template + '/public/sass'],
        outputStyle: 'compressed'
      }
    }
  },

  browserify: {
    bundleConfigs: [{
      entries: [
        src + 'javascripts/application.js'
      ],
      dev: {
        dest: snapshotDir + 'javascripts'
      },
      prod: {
        dest: distDir + 'javascripts'
      },
      outputName: 'application.js'
    },{
      entries: [
        src + 'javascripts/export/fingerprint.js'
      ],
      dev: {
        dest: snapshotDir + 'javascripts'
      },
      prod: {
        dest: distDir + 'javascripts'
      },
      outputName: 'mdtpdf.js'
    }]
  },

  test: {
    specsScr: src + 'test/specs/unit/**/*.js',
    fixturesScr: src + 'test/specs/fixtures/*.html',
    karmaConfig: src + 'test/config/karma.conf.js'
  },

  browserSync: {
    ui: false,
    port: 9032,
    open: false,
    server: {
      baseDir: '.',
      routes: {
        '/assets': dest,
        '/component-library': './component-library'
      }
    }
  }
};
<|endoftext|>


<commit_before>#!/usr/bin/env node

var fs = require('fs'),
    request = require("request"),
    mkdirp = require('mkdirp'),
    async = require('async'),
    xml2js = require('xml2js'),
    colors = require('colors'),
    appInfo = require('./package.json');

var Downloader = {
    /**
	* Config options
	*/
    options: {
        site: process.argv[2],
        num: 50,
        start: 0,
        concurrency: 8,
        retry_time: 2,
        retried_time: 0,
        total_page: 0,
        dir: process.argv[3] ? process.argv[3] + '/' + process.argv[2] : './tumblr/' + process.argv[2]
    },
    wrapTask: function (task, context, exArgs) {
		var self = this;
		return function () {
			var args = [].slice.call(arguments);
			args = exArgs ? exArgs.concat(args) : args;
			task.apply(context || self, args);
		};
	},
    start: function (retry) {
        var self = this;

        self.mkdirp();

        console.log(colors.green('Downloading photos from ' + self.options.site + ', concurrency=' + self.options.concurrency + ' ...'));

        self.run(retry);
    },
    run: function(retry){
        var self = this;
        async.waterfall([
            function(callback){
                var url = 'http://' + self.options.site + '/api/read?type=photo&num=' + self.options.num + '&start=' + self.options.start;
                callback(null, retry, url)
            },
            self.wrapTask(self.crawlPosts),
            self.wrapTask(self.getPosts),
            self.wrapTask(self.parsePosts),
            self.wrapTask(self.downImages)
        ], function (err, result) {
            if (err){
                console.log('Code: \'%s\', Message: \'%s\''.red, err.code, err.message);
                self.options.retried_time += 1;
            }
            if (err && self.options.retried_time <= self.options.retry_time) {
                if (self.options.retry_time - self.options.retried_time + 1) {
                    console.log(colors.bgWhite.black('Retry in 3s...(will retry ' + (self.options.retry_time - self.options.retried_time + 1) + ' times)'));
                } else {
                    console.log('Retry in 3s...'.bgWhite.black);
                }
                setTimeout(function () {
                    self.run(true);
                }, 3000);
            } else {
                console.log('Starting grab new posts...'.bgGreen.black);
                self.options.retried_time = 0;
                self.options.total_page += 1;
                self.options.start += self.options.num;
                self.run();
            }
        });
    },
    crawlPosts: function(retry,url,callback){
        var self = this;
        if(retry && self.options.retried_time && self.options.retried_time <= self.options.retry_time){
            console.log(self.options.retried_time + ' time: retrying crawl posts from ' + self.options.start + ' to ' + (self.options.start + self.options.num) + '...');
        }else{
            console.log('Crawling posts from ' + self.options.start + ' to ' + (self.options.start + self.options.num) + '...');
        }
        request(url, function (err, response, body) {
            if(!err && response.statusCode == 200) {
                console.log('✔'.green + ' Crawled posts from ' + self.options.start + ' to ' + (self.options.start + self.options.num));
            }
            callback(err, body);
        });
    },
    getPosts: function(body, callback){
        var self = this;
        xml2js.parseString(body, { explicitArray: false }, function(err, data){
            if(!data){
                console.log('Unknown error, please try it again later.'.red);
                return false;
            }
            console.log('Total Posts: ' + data.tumblr.posts.$.total);
            var posts = data.tumblr.posts.post;
            if(posts && posts instanceof Array == false) posts = [posts];
            callback(err, posts);
        });
    },
    parsePosts: function(posts, callback){
        var self = this;
        if(posts){
            var images = [];
            posts.forEach(function (element) {
                if (element['photoset']) {
                    var photoset = element['photoset']['photo'];
                    photoset.forEach(function (photo) {
                        images.push(photo['photo-url'][0]._);
                    });
                } else if (element['photo-url']) {
                    images.push(element['photo-url'][0]._);
                }
            }, this);
            console.log(images.length + ' images found (' + self.options.start + ' to ' + (self.options.start + self.options.num) + ')');
            callback(null, images);
        }else{
            console.log("Our work here is done!".rainbow);
            console.log('Total pages: ' + self.options.total_page);
            self.exit();
        }
    },
    downImages: function(images, callback){
        var self = this;
        async.eachLimit(
            images,
            self.options.concurrency,
            self.wrapTask(self.downImage),
            function (err) {
                callback(err);
            });
    },
    downImage: function(item, callback){
        var self = this;
        var filename = item.match(/[^/\\\\]+$/ig)[0];
        request.head(item, function (err, res, body) {
            if (!err) {
                if (self.isExist(filename, res)) {
                    console.log('Already have %s'.gray, item);
                    callback();
                } else {
                    self.download(item, self.options.dir, filename, function (err) {
                        callback(err);
                    });
                }
            } else {
                console.log('Requesting header info: %s'.yellow, item);
                callback(err);
            }
        });
    },
    isExist: function(filename, res){
        var self = this;
        return fs.existsSync(self.options.dir + "/" + filename) &&
               res.headers['content-length'] == fs.statSync(self.options.dir + "/" + filename).size;
    },
    download: function (url, dir, filename, callback) {
        var toPath = dir + "/" + filename;
        request(url)
            .pipe(fs.createWriteStream(toPath))
            .on('close', function () {
                console.log('✔'.green + ' %s', url);
                callback();
            })
            .on('error', function(err) {
                callback(err);
            });
    },
    mkdirp: function(){
        var self = this;
        mkdirp(self.options.dir, function (err) {
            if (err) {
                console.log(err.red);
            }
        })
    },
    exit: function(){
        process.exit();
    }
};

switch(process.argv[2]){
    case '-v':
    case '--version':
        console.log('version is ' + appInfo.version);
        break;
    case '-h':
    case '--help':
        console.log('Useage: tumblr-dl <url> [<directory>]');
        console.log('Options:');
        console.log('  -v --version output the version number');
        console.log('  -h --help    output usage information');
        break;
    default:
        if(!process.argv[2]){
            console.log('Usage: tumblr-dl <url> [<directory>]');
            console.log('eg. tumblr-dl xxx.tumblr.com');
            console.log('eg. tumblr-dl xxx.tumblr.com ~/pictures/xxx.tumblr/');
            process.exit();
        }else{
            Downloader.start();
        }
}<commit_msg>bug fix when getting images from reblog posts<commit_after>#!/usr/bin/env node

var fs = require('fs'),
    request = require("request"),
    mkdirp = require('mkdirp'),
    async = require('async'),
    xml2js = require('xml2js'),
    colors = require('colors'),
    appInfo = require('./package.json');

var Downloader = {
    /**
	* Config options
	*/
    options: {
        site: process.argv[2],
        num: 50,
        start: 0,
        concurrency: 8,
        retry_time: 2,
        retried_time: 0,
        total_page: 0,
        dir: process.argv[3] ? process.argv[3] + '/' + process.argv[2] : './tumblr/' + process.argv[2]
    },
    wrapTask: function (task, context, exArgs) {
		var self = this;
		return function () {
			var args = [].slice.call(arguments);
			args = exArgs ? exArgs.concat(args) : args;
			task.apply(context || self, args);
		};
	},
    start: function (retry) {
        var self = this;

        self.mkdirp();

        console.log(colors.green('Downloading photos from ' + self.options.site + ', concurrency=' + self.options.concurrency + ' ...'));

        self.run(retry);
    },
    run: function(retry){
        var self = this;
        async.waterfall([
            function(callback){
                var url = 'http://' + self.options.site + '/api/read?type=photo&num=' + self.options.num + '&start=' + self.options.start;
                callback(null, retry, url)
            },
            self.wrapTask(self.crawlPosts),
            self.wrapTask(self.getPosts),
            self.wrapTask(self.parsePosts),
            self.wrapTask(self.downImages)
        ], function (err, result) {
            if (err){
                console.log('Code: \'%s\', Message: \'%s\''.red, err.code, err.message);
                self.options.retried_time += 1;
            }
            if (err && self.options.retried_time <= self.options.retry_time) {
                if (self.options.retry_time - self.options.retried_time + 1) {
                    console.log(colors.bgWhite.black('Retry in 3s...(will retry ' + (self.options.retry_time - self.options.retried_time + 1) + ' times)'));
                } else {
                    console.log('Retry in 3s...'.bgWhite.black);
                }
                setTimeout(function () {
                    self.run(true);
                }, 3000);
            } else {
                console.log('Starting grab new posts...'.bgGreen.black);
                self.options.retried_time = 0;
                self.options.total_page += 1;
                self.options.start += self.options.num;
                self.run();
            }
        });
    },
    crawlPosts: function(retry,url,callback){
        var self = this;
        if(retry && self.options.retried_time && self.options.retried_time <= self.options.retry_time){
            console.log(self.options.retried_time + ' time: retrying crawl posts from ' + self.options.start + ' to ' + (self.options.start + self.options.num) + '...');
        }else{
            console.log('Crawling posts from ' + self.options.start + ' to ' + (self.options.start + self.options.num) + '...');
        }
        request(url, function (err, response, body) {
            if(!err && response.statusCode == 200) {
                console.log('✔'.green + ' Crawled posts from ' + self.options.start + ' to ' + (self.options.start + self.options.num));
            }
            callback(err, body);
        });
    },
    getPosts: function(body, callback){
        var self = this;
        xml2js.parseString(body, { explicitArray: false }, function(err, data){
            if(!data){
                console.log('Unknown error, please try it again later.'.red);
                return false;
            }
            console.log('Total Posts: ' + data.tumblr.posts.$.total);
            var posts = data.tumblr.posts.post;
            if(posts && posts instanceof Array == false) posts = [posts];
            callback(err, posts);
        });
    },
    parsePosts: function(posts, callback){
        var self = this;
        if(posts){
            var images = [];
            posts.forEach(function (element) {
                if (element['photoset']) {
                    var photoset = element['photoset']['photo'];
                    photoset.forEach(function (photo) {
                        images.push(photo['photo-url'][0]._);
                    });
                } else if (element['photo-url']) {
                    images.push(element['photo-url'][0]._);
                } else if (element['regular-body']) {
                    //匹配图片（g表示匹配所有结果i表示区分大小写）
                    var imgReg = /<img.*?(?:>|\/>)/gi;
                    //匹配src属性
                    var srcReg = /src=[\'\"]?([^\'\"]*)[\'\"]?/i;
                    var arr = element['regular-body'].match(imgReg);
                    for (var i = 0; i < arr.length; i++) {
                        var src = arr[i].match(srcReg);
                        //获取图片地址
                        if (src[1]) {
                            images.push(src[1]);
                        }
                    }
                }
            }, this);
            console.log(images.length + ' images found (' + self.options.start + ' to ' + (self.options.start + self.options.num) + ')');
            callback(null, images);
        }else{
            console.log("Our work here is done!".rainbow);
            console.log('Total pages: ' + self.options.total_page);
            self.exit();
        }
    },
    downImages: function(images, callback){
        var self = this;
        async.eachLimit(
            images,
            self.options.concurrency,
            self.wrapTask(self.downImage),
            function (err) {
                callback(err);
            });
    },
    downImage: function(item, callback){
        var self = this;
        var filename = item.match(/[^/\\\\]+$/ig)[0];
        request.head(item, function (err, res, body) {
            if (!err) {
                if (self.isExist(filename, res)) {
                    console.log('Already have %s'.gray, item);
                    callback();
                } else {
                    self.download(item, self.options.dir, filename, function (err) {
                        callback(err);
                    });
                }
            } else {
                console.log('Requesting header info: %s'.yellow, item);
                callback(err);
            }
        });
    },
    isExist: function(filename, res){
        var self = this;
        return fs.existsSync(self.options.dir + "/" + filename) &&
               res.headers['content-length'] == fs.statSync(self.options.dir + "/" + filename).size;
    },
    download: function (url, dir, filename, callback) {
        var toPath = dir + "/" + filename;
        request(url)
            .pipe(fs.createWriteStream(toPath))
            .on('close', function () {
                console.log('✔'.green + ' %s', url);
                callback();
            })
            .on('error', function(err) {
                callback(err);
            });
    },
    mkdirp: function(){
        var self = this;
        mkdirp(self.options.dir, function (err) {
            if (err) {
                console.log(err.red);
            }
        })
    },
    exit: function(){
        process.exit();
    }
};

switch(process.argv[2]){
    case '-v':
    case '--version':
        console.log('version is ' + appInfo.version);
        break;
    case '-h':
    case '--help':
        console.log('Useage: tumblr-dl <url> [<directory>]');
        console.log('Options:');
        console.log('  -v --version output the version number');
        console.log('  -h --help    output usage information');
        break;
    default:
        if(!process.argv[2]){
            console.log('Usage: tumblr-dl <url> [<directory>]');
            console.log('eg. tumblr-dl xxx.tumblr.com');
            console.log('eg. tumblr-dl xxx.tumblr.com ~/pictures/xxx.tumblr/');
            process.exit();
        }else{
            Downloader.start();
        }
}<|endoftext|>


<commit_before>var fs = require('fs'),
  prettyjson = require('./prettyjson');
fs.readdir('services/', function(err, files) {
  if(err) {
    console.log(err, 'readdir');
    process.exit(1);
  } else {
    for(var i=0; i<files.length; i++) {
      if(files[i] != 'README.md') {
        doFile(files[i]);
      }
    }
  }
});
function doFile(fileName) {
  fs.readFile('services/'+fileName, function(err, data) {
    try {
      if(err) {
        console.log(err, fileName);
        process.exit(1);
      } else {
        var obj, changed = false;
        try {
          obj = JSON.parse(data);
        } catch(e) {
          console.log(e, fileName);
          process.exit(1);
        }
        if(typeof(obj.id) != 'string') {
          console.log('id wrong', fileName);
          if(!obj.id) {
            obj.id = fileName.substring(0, fileName.length-5);
            changed = true;
         }
        }
        if(typeof(obj.name) != 'string') {
          console.log('name wrong', fileName);
          if(!obj.name) {
            obj.name = fileName.substring(0, fileName.length-5);
            changed = true;
          }
        }
        if(obj.type != 'service' && obj.type != 'software') {
          console.log('type wrong', fileName);
          if(!obj.type) {
            obj.type = 'service';
            changed = true;
          }
        }
        if(!Array.isArray(obj.urls)) {
          console.log('urls wrong', fileName);
          if(obj.url) {
            obj.urls=[obj.url];
            changed=true;
          } else if(!obj.urls) {
            obj.urls=[];
            changed=true;
          }
        }
        if(typeof(obj.fulltos) != 'object' || Array.isArray(obj.fulltos)) {
          console.log('fulltos wrong', fileName);
          if(!obj.fulltos) {
            obj.fulltos={};
            changed=true;
          }
        }
        for(var i in obj.fulltos) {
          if(['privacy', 'terms', 'faq', 'security', 'help', 'legal', 'trademark', 'copyright', 'dcma', 'guidelines'].indexOf(i)==-1) {
            console.log('wrong fulltos entry name', i, fileName);
          }
          if(typeof(obj.fulltos[i]) != 'object') {
            console.log('entry type wrong', i, fileName);
            if(typeof(obj.fulltos[i])=='string') {
              obj.fulltos[i]={url: obj.fulltos[i]};
              changed = true;
            }
          }
          if(typeof(obj.service) != 'string') {
            if(typeof(obj.name) != 'string') {
              console.log('not a service-pointer entry, and no name', i, fileName);
            }
            if(typeof(obj.url) != 'string') {
              console.log('not a service-pointer entry, and no url', i, fileName);
            }
          }
        }
        if(typeof(obj.tos) != 'undefined') {
          console.log('obj.tos exists next to obj.fulltos', fileName, obj.tos, obj.fulltos);
          obj.fulltos = obj.tos;
          delete obj.tos;
          changed = true;
        }
        if(typeof(obj.tosdr) != 'object' || Array.isArray(obj.tosdr)) {
          console.log('wrong type tosdr', fileName);
          if(!obj.tosdr) {
            obj.tosdr = {};
            changed = true;
          }
        }
        if([false, 'A', 'B', 'C', 'D', 'E'].indexOf(obj.tosdr.rated)==-1) {
          console.log('wrong obj.tosdr.rated', fileName);
          if(!obj.tosdr.rated) {
            obj.tosdr.rated = false;
            changed = true;
          }
        }
        if(!Array.isArray(obj.tosdr.keywords)) {
          console.log('wrong obj.tosdr.keywords', fileName);
          if(!obj.tosdr.keywords) {
            obj.tosdr.keywords = [];
            changed = true;
          }
        }
        if(!Array.isArray(obj.tosdr.related)) {
          console.log('wrong obj.tosdr.related', fileName);
          if(!obj.tosdr.related) {
            obj.tosdr.related = [];
            changed = true;
          }
        }
        if(typeof(obj.alexa) != 'number') {
          console.log('wrong obj.alexa', fileName);
          if(!obj.alexa) {
            obj.alexa = 1000000;
            changed = true;
          }
        }
        if(typeof(obj.freesoftware) != 'boolean') {
          console.log('wrong obj.freesoftware', fileName);
          obj.freesoftware = false;
          changed = true;
        }
        if(obj.type == 'software' && typeof(obj.license) != 'string') {
          console.log('wrong obj.license', fileName);
          obj.license = '(proprietary)';
          changed = true;
        }
        if(changed) {
          fs.writeFile('services/'+fileName, prettyjson(obj), function(err) {
            if(err) {
              console.log(e, filename);
              process.exit(1);
            } else {
              console.log('fixed '+fileName);
            }
          });
        }
      }
    } catch(e) {
      console.log(e, fileName);
      process.exit(1);
    }
  });
}
<commit_msg>make fixservices check fulltos objects<commit_after>var fs = require('fs'),
  prettyjson = require('./prettyjson');
fs.readdir('services/', function(err, files) {
  if(err) {
    console.log(err, 'readdir');
    process.exit(1);
  } else {
    for(var i=0; i<files.length; i++) {
      if(files[i] != 'README.md') {
        doFile(files[i]);
      }
    }
  }
});
function doFile(fileName) {
  fs.readFile('services/'+fileName, function(err, data) {
    try {
      if(err) {
        console.log(err, fileName);
        process.exit(1);
      } else {
        var obj, changed = false;
        try {
          obj = JSON.parse(data);
        } catch(e) {
          console.log(e, fileName);
          process.exit(1);
        }
        if(typeof(obj.id) != 'string') {
          console.log('id wrong', fileName);
          if(!obj.id) {
            obj.id = fileName.substring(0, fileName.length-5);
            changed = true;
         }
        }
        if(typeof(obj.name) != 'string') {
          console.log('name wrong', fileName);
          if(!obj.name) {
            obj.name = fileName.substring(0, fileName.length-5);
            changed = true;
          }
        }
        if(obj.type != 'service' && obj.type != 'software') {
          console.log('type wrong', fileName);
          if(!obj.type) {
            obj.type = 'service';
            changed = true;
          }
        }
        if(!Array.isArray(obj.urls)) {
          console.log('urls wrong', fileName);
          if(obj.url) {
            obj.urls=[obj.url];
            changed=true;
          } else if(!obj.urls) {
            obj.urls=[];
            changed=true;
          }
        }
        if(typeof(obj.fulltos) != 'object' || Array.isArray(obj.fulltos)) {
          console.log('fulltos wrong', fileName);
          if(!obj.fulltos) {
            obj.fulltos={};
            changed=true;
          }
        }
        for(var i in obj.fulltos) {
          if(['privacy', 'terms', 'faq', 'security', 'help', 'legal', 'trademark', 'copyright', 'dcma', 'guidelines'].indexOf(i)==-1) {
            console.log('wrong fulltos entry name', i, fileName);
          }
          if(typeof(obj.fulltos[i]) != 'object') {
            console.log('entry type wrong', i, fileName);
            if(typeof(obj.fulltos[i])=='string') {
              obj.fulltos[i]={url: obj.fulltos[i]};
              changed = true;
            }
          }
          if(typeof(obj.fulltos[i].service) != 'string') {
            if(typeof(obj.fulltos[i].name) != 'string') {
              console.log('not a service-pointer entry, and no name', i, fileName);
            }
            if(typeof(obj.fulltos[i].url) != 'string') {
              console.log('not a service-pointer entry, and no url', i, fileName);
            }
          }
        }
        if(typeof(obj.tos) != 'undefined') {
          console.log('obj.tos exists next to obj.fulltos', fileName, obj.tos, obj.fulltos);
          obj.fulltos = obj.tos;
          delete obj.tos;
          changed = true;
        }
        if(typeof(obj.tosdr) != 'object' || Array.isArray(obj.tosdr)) {
          console.log('wrong type tosdr', fileName);
          if(!obj.tosdr) {
            obj.tosdr = {};
            changed = true;
          }
        }
        if([false, 'A', 'B', 'C', 'D', 'E'].indexOf(obj.tosdr.rated)==-1) {
          console.log('wrong obj.tosdr.rated', fileName);
          if(!obj.tosdr.rated) {
            obj.tosdr.rated = false;
            changed = true;
          }
        }
        if(!Array.isArray(obj.tosdr.keywords)) {
          console.log('wrong obj.tosdr.keywords', fileName);
          if(!obj.tosdr.keywords) {
            obj.tosdr.keywords = [];
            changed = true;
          }
        }
        if(!Array.isArray(obj.tosdr.related)) {
          console.log('wrong obj.tosdr.related', fileName);
          if(!obj.tosdr.related) {
            obj.tosdr.related = [];
            changed = true;
          }
        }
        if(typeof(obj.alexa) != 'number') {
          console.log('wrong obj.alexa', fileName);
          if(!obj.alexa) {
            obj.alexa = 1000000;
            changed = true;
          }
        }
        if(typeof(obj.freesoftware) != 'boolean') {
          console.log('wrong obj.freesoftware', fileName);
          obj.freesoftware = false;
          changed = true;
        }
        if(obj.type == 'software' && typeof(obj.license) != 'string') {
          console.log('wrong obj.license', fileName);
          obj.license = '(proprietary)';
          changed = true;
        }
        if(changed) {
          fs.writeFile('services/'+fileName, prettyjson(obj), function(err) {
            if(err) {
              console.log(e, filename);
              process.exit(1);
            } else {
              console.log('fixed '+fileName);
            }
          });
        }
      }
    } catch(e) {
      console.log(e, fileName);
      process.exit(1);
    }
  });
}
<|endoftext|>


<commit_before>$(document).ready(function () {
    var q = encodeURI($('#current-query').text());
    $("#map_results").fancybox({
        beforeShow: function () {
            if ($('#resultMap').html().length == 0) {
                $('#resultMap').html('');
                initialize_map(q);
            }
        }
    });

    function initialize_map(q) {
        var langStr = getURLParameter('lang');
        if (langStr == 'null') {
            var lang = '';
        } else {
            var lang = langStr;
        }

        var mapboxKey = $('#mapboxKey').text();
        var baselayers = $('#baselayers').text().split(',');

        //baselayers
        var osm = L.tileLayer(
        'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            attribution: 'OpenStreetMap',
            maxZoom: 18
        });

        var imperium = L.tileLayer(
        'https://dh.gu.se/tiles/imperium/{z}/{x}/{y}.png', {
            maxZoom: 10,
            attribution: 'Powered by Leaflet. Map base: DARE, 2015 (cc-by-sa).'
        });
        var mb_physical = L.tileLayer(
    'https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', {
        attribution: 'Map data © OpenStreetMap contributors, ' +
        'CC-BY-SA, ' +
        'Imagery © Mapbox', id: 'mapbox/outdoors-v11', maxZoom: 12, accessToken: mapboxKey
    });

        var map = new L.Map('resultMap', {
            center: new L.LatLng(0, 0),
            zoom: 4,
            layers:[eval(baselayers[0])]
        });

        //add mintLayer from AJAX
        var mintLayer = L.geoJson.ajax("mints.geojson?q=" + q, {
            onEachFeature: onEachFeature,
            pointToLayer: renderPoints
        }).addTo(map);

        var subjectLayer = L.geoJson.ajax("subjects.geojson?q=" + q, {
            onEachFeature: onEachFeature,
            pointToLayer: renderPoints
        }).addTo(map);

        //add hoards, but don't make visible by default
        var markers = L.markerClusterGroup();
        var findspotLayer = L.geoJson.ajax("findspots.geojson?q=" + q, {
            onEachFeature: onEachFeature,
            pointToLayer: renderPoints
        });

        //add controls
        var baseMaps = {
        };
        //add baselayers
        var i;
        for (i = 0; i < baselayers.length; i++) {
            var label;
            switch (baselayers[i]) {
                case 'osm': label = "OpenStreetMap";
                break;
                case 'imperium': label = 'Imperium Romanum';
                break;
                case 'mb_physical': label = 'Terrain and Streets';
                break;
            }
            baseMaps[label] = eval(baselayers[i]);
        }

        var overlayMaps = {
            'Mints': mintLayer,
            'Findspots': markers,
            'Subjects': subjectLayer
        };

        //add controls
        var layerControl = L.control.layers(baseMaps, overlayMaps).addTo(map);

        //zoom to groups on AJAX complete
        mintLayer.on('data:loaded', function () {
            var group = new L.featureGroup([mintLayer, findspotLayer, subjectLayer]);
            map.fitBounds(group.getBounds());
        }.bind(this));

        subjectLayer.on('data:loaded', function () {
            var group = new L.featureGroup([mintLayer, findspotLayer, subjectLayer]);
            map.fitBounds(group.getBounds());
        }.bind(this));

        findspotLayer.on('data:loaded', function () {
            markers.addLayer(findspotLayer);
            map.addLayer(markers);

            var group = new L.featureGroup([mintLayer, findspotLayerr, subjectLayer]);
            map.fitBounds(group.getBounds());
        }.bind(this));

        /*****
         * Features for manipulating layers
         *****/
        function renderPoints(feature, latlng) {
            var fillColor;
            switch (feature.properties.type) {
                case 'mint':
                fillColor = '#6992fd';
                break;
                case 'findspot':
                fillColor = '#d86458';
                break;
                case 'subject':
                fillColor = '#a1d490';
            }

            return new L.CircleMarker(latlng, {
                radius: 5,
                fillColor: fillColor,
                color: "#000",
                weight: 1,
                opacity: 1,
                fillOpacity: 0.6
            });
        }

        function onEachFeature (feature, layer) {
            var label = feature.properties.name;
            if (feature.properties.type == 'subject') {
                var facet = 'subjectPlace';
            } else {
                var facet = feature.properties.type;
            }

            if (q.length > 0) {
                var query = q + ' AND ' + facet + '_facet:"' + label + '"';
            } else {
                var query = facet + '_facet:"' + label + '"';
            }

            if (q.indexOf('mint_facet') !== -1) {
                var str = label;
            } else {
                var str = "" + label + '';
            }

            str += ' ';
            layer.bindPopup(str);
        }
    }

    function getURLParameter(name) {
        return decodeURI(
        (RegExp(name + '=' + '(.+?)(&|$)').exec(location.search) ||[, null])[1]);
    }
});<commit_msg>refactored result maps for scaling and findspots<commit_after>$(document).ready(function () {
    var q = $('#current-query').text();
    $("#map_results").fancybox({
        beforeShow: function () {
            if ($('#resultMap').html().length == 0) {
                $('#resultMap').html('');
                initialize_map(q);
            }
        }
    });

    function initialize_map(q) {
        var path = $('#path').text();
        var mapboxKey = $('#mapboxKey').text();
        var baselayers = $('#baselayers').text().split(',');
        var collection_type = $('#collection_type').text();

        //baselayers
        var osm = L.tileLayer(
        'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            attribution: 'OpenStreetMap',
            maxZoom: 18
        });

        var imperium = L.tileLayer(
        'https://dh.gu.se/tiles/imperium/{z}/{x}/{y}.png', {
            maxZoom: 10,
            attribution: 'Powered by Leaflet. Map base: DARE, 2015 (cc-by-sa).'
        });
        var mb_physical = L.tileLayer(
        'https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', {
            attribution: 'Map data © OpenStreetMap contributors, ' +
            'CC-BY-SA, ' +
            'Imagery © Mapbox', id: 'mapbox/outdoors-v11', maxZoom: 12, accessToken: mapboxKey
        });

        var map = new L.Map('resultMap', {
            center: new L.LatLng(0, 0),
            zoom: 4,
            layers:[eval(baselayers[0])]
        });

        //add controls
        var baseMaps = {
        };
        //add baselayers
        var i;
        for (i = 0; i < baselayers.length; i++) {
            var label;
            switch (baselayers[i]) {
                case 'osm': label = "OpenStreetMap"; break;
                case 'imperium': label = 'Imperium Romanum'; break;
                case 'mb_physical': label = 'Terrain and Streets'; break;
            }
            baseMaps[label] = eval(baselayers[i]);
        }

        if (collection_type == 'hoard') {
            var mintLayer = L.geoJson.ajax(path + "mints.geojson?q=" + q, {
                onEachFeature: onEachFeature,
                pointToLayer: renderPoints
            }).addTo(map);

            var hoardLayer = L.geoJson.ajax(path + "hoards.geojson?q=" + q, {
                onEachFeature: onEachFeature,
                pointToLayer: renderPoints
            }).addTo(map);

            var overlayMaps = {
                'Hoards': hoardLayer,
                'Mints': mintLayer
            };

            //add controls
            var layerControl = L.control.layers(baseMaps, overlayMaps).addTo(map);

            //zoom to groups on AJAX complete
            mintLayer.on('data:loaded', function () {
                var group = new L.featureGroup([hoardLayer, mintLayer]);
                map.fitBounds(group.getBounds());
            }.bind(this));

            hoardLayer.on('data:loaded', function () {
                var group = new L.featureGroup([hoardLayer, mintLayer]);
                map.fitBounds(group.getBounds());
            }.bind(this));
        } else {

            var mintLayer = L.geoJson.ajax(path + "mints.geojson?q=" + q, {
                onEachFeature: onEachFeature,
                pointToLayer: renderPoints
            }).addTo(map);

            var subjectLayer = L.geoJson.ajax(path + "subjects.geojson?q=" + q, {
                onEachFeature: onEachFeature,
                pointToLayer: renderPoints
            }).addTo(map);

            var findspotLayer = L.geoJson.ajax(path + "findspots.geojson?q=" + q, {
                onEachFeature: onEachFeature,
                pointToLayer: renderPoints
            }).addTo(map);

            var overlayMaps = {
                'Mints': mintLayer,
                'Findspots': findspotLayer,
                'Subjects': subjectLayer
            };

            var layerControl = L.control.layers(baseMaps, overlayMaps).addTo(map);

            //zoom to groups on AJAX complete
            mintLayer.on('data:loaded', function () {
                var group = new L.featureGroup([mintLayer, findspotLayer, subjectLayer]);
                map.fitBounds(group.getBounds());
            }.bind(this));

            findspotLayer.on('data:loaded', function () {
                var group = new L.featureGroup([mintLayer, findspotLayer, subjectLayer]);
                map.fitBounds(group.getBounds());
            }.bind(this));

            subjectLayer.on('data:loaded', function () {
                var group = new L.featureGroup([mintLayer, findspotLayer, subjectLayer]);
                map.fitBounds(group.getBounds());
            }.bind(this));
        }

        /*****
         * Features for manipulating layers
         *****/
        function renderPoints(feature, latlng) {
            var fillColor;
            switch (feature.properties.type) {
                case 'mint':
                fillColor = '#6992fd';
                break;
                case 'hoard':
                fillColor = '#d86458';
                break;
                case 'findspot':
                fillColor = '#f98f0c';
                break;
                case 'subject':
                fillColor = '#a1d490';
            }

            return new L.CircleMarker(latlng, {
                radius: feature.properties.radius,
                fillColor: fillColor,
                color: "#000",
                weight: 1,
                opacity: 1,
                fillOpacity: 0.6
            });
        }

        function onEachFeature (feature, layer) {
            var label = feature.properties.name;
            if (feature.properties.type == 'subject') {
                var facet = 'subjectPlace';
            } else {
                var facet = feature.properties.type;
            }

            if (q.length > 0) {
                var query = q + ' AND ' + facet + '_facet:"' + label + '"';
            } else {
                var query = facet + '_facet:"' + label + '"';
            }

            if (q.indexOf('mint_facet') !== -1) {
                var str = label;
            } else {
                var str = "" + label + '';
            }

            str += ' ';
            layer.bindPopup(str);
        }
    }

    function getURLParameter(name) {
        return decodeURI(
        (RegExp(name + '=' + '(.+?)(&|$)').exec(location.search) ||[, null])[1]);
    }
});<|endoftext|>