<commit_before>const FileSaver = require('file-saver');
const fflate = require('fflate');
const requireJsSource = require('requirejs/require?raw');
const fflateJsSource = require('fflate/umd/index?raw');
const fileLoader = require('./helpers/fileLoader');
const templateStandalone = require('./snapshot_standalone.txt');
const templateOnline = require('./snapshot_online.txt');
const templateInline = require('./snapshot_inline.txt');
const semverRange = require('../../version').version;
const buffer = require('./helpers/buffer');

let sourceCode = window.k3dCompressed;
const scripts = document.getElementsByTagName('script');
let path;

if (typeof (sourceCode) === 'undefined') {
    sourceCode = '';

    for (let i = 0; i < scripts.length; i++) {
        // working in jupyter notebooks
        if (scripts[i].getAttribute('src')
            && scripts[i].getAttribute('src').includes('k3d')
            && scripts[i].getAttribute('src').includes('.js')) {
            path = scripts[i].getAttribute('src');
        }
    }

    if (typeof (path) !== 'undefined') {
        path = path.replace('k3d.js', 'standalone.js').replace('index.js', 'standalone.js');
    } else {
        // use npm repository
        path = `https://unpkg.com/k3d@${semverRange}/dist/standalone.js`;
    }

    fileLoader(path, (data) => {
        data = fflate.strToU8(data);
        sourceCode = buffer.arrayBufferToBase64(fflate.zlibSync(data));
    });
}

function getHTMLSnapshot(K3D, compressionLevel) {
    const data = buffer.arrayBufferToBase64(K3D.getSnapshot(compressionLevel));
    let filecontent;
    const timestamp = new Date().toUTCString();

    if (K3D.parameters.snapshotType === 'full') {
        filecontent = templateStandalone;
        filecontent = filecontent.split('[REQUIRE_JS]').join(requireJsSource);
        filecontent = filecontent.split('[FFLATE_JS]').join(fflateJsSource);
        filecontent = filecontent.split('[K3D_SOURCE]').join(sourceCode);
    } else if (K3D.parameters.snapshotType === 'online') {
        filecontent = templateOnline;
        filecontent = filecontent.split('[VERSION]').join(K3D.parameters.guiVersion);
    } else if (K3D.parameters.snapshotType === 'inline') {
        filecontent = templateInline;
        filecontent = filecontent.split('[ID]').join(Math.floor(Math.random() * 1e9));
        filecontent = filecontent.split('[HEIGHT]').join(K3D.parameters.height || 512);
        filecontent = filecontent.split('[VERSION]').join(K3D.parameters.guiVersion);
    }

    filecontent = filecontent.split('[DATA]').join(data);
    filecontent = filecontent.split('[TIMESTAMP]').join(timestamp);
    filecontent = filecontent.split('[ADDITIONAL]').join('//[ADDITIONAL]');

    return filecontent;
}

function handleFileSelect(K3D, evt) {
    const { files } = evt.dataTransfer;
    const HTMLSnapshotReader = new FileReader();
    const BinarySnapshotReader = new FileReader();
    const STLReader = new FileReader();

    evt.stopPropagation();
    evt.preventDefault();

    HTMLSnapshotReader.onload = function (event) {
        const snapshot = K3D.extractSnapshot(event.target.result);

        if (snapshot[1]) {
            K3D.setSnapshot(snapshot[1]);
        }
    };

    BinarySnapshotReader.onload = function (event) {
        K3D.setSnapshot(fflate.unzlibSync(event.target.result));
    };

    STLReader.onload = function (event) {
        const stl = event.target.result;

        K3D.load({
            objects: [{
                type: 'STL',
                model_matrix: {
                    data: [
                        1, 0, 0, 0,
                        0, 1, 0, 0,
                        0, 0, 1, 0,
                        0, 0, 0, 1],
                },
                binary: stl,
                visible: true,
                color: 0x0000ff,
                wireframe: false,
                flat_shading: false,
            }],
        });
    };

    if (files.length > 0) {
        if (files[0].name.substr(-4).toLowerCase() === 'html') {
            HTMLSnapshotReader.readAsText(files[0]);
            return;
        }

        if (files[0].name.substr(-3).toLowerCase() === 'stl') {
            STLReader.readAsArrayBuffer(files[0]);
            return;
        }

        BinarySnapshotReader.readAsArrayBuffer(files[0]);
    }
}

function handleDragOver(evt) {
    evt.stopPropagation();
    evt.preventDefault();
    evt.dataTransfer.dropEffect = 'copy';
}

function snapshotGUI(gui, K3D) {
    const obj = {
        snapshot() {
            let data = getHTMLSnapshot(K3D, 9);
            let filename = `K3D-snapshot-${Date.now()}.html`;

            if (K3D.parameters.name) {
                filename = `${K3D.parameters.name}.html`;
            }

            data = new Blob([data], { type: 'text/plain;charset=utf-8' });
            FileSaver.saveAs(data, filename);
        },
    };
    gui.add(obj, 'snapshot').name('Snapshot HTML');

    // Setup the dnd listeners.
    const targetDomNode = K3D.getWorld().targetDOMNode;

    targetDomNode.addEventListener('dragover', handleDragOver, false);
    targetDomNode.addEventListener('drop', handleFileSelect.bind(null, K3D), false);
}

module.exports = {
    snapshotGUI,
    getHTMLSnapshot,
};
<commit_msg>small fix<commit_after>const FileSaver = require('file-saver');
const fflate = require('fflate');
const requireJsSource = require('requirejs/require?raw');
const fflateJsSource = require('./../../../node_modules/fflate/umd/index?raw');
const fileLoader = require('./helpers/fileLoader');
const templateStandalone = require('./snapshot_standalone.txt');
const templateOnline = require('./snapshot_online.txt');
const templateInline = require('./snapshot_inline.txt');
const semverRange = require('../../version').version;
const buffer = require('./helpers/buffer');

let sourceCode = window.k3dCompressed;
const scripts = document.getElementsByTagName('script');
let path;

if (typeof (sourceCode) === 'undefined') {
    sourceCode = '';

    for (let i = 0; i < scripts.length; i++) {
        // working in jupyter notebooks
        if (scripts[i].getAttribute('src')
            && scripts[i].getAttribute('src').includes('k3d')
            && scripts[i].getAttribute('src').includes('.js')) {
            path = scripts[i].getAttribute('src');
        }
    }

    if (typeof (path) !== 'undefined') {
        path = path.replace('k3d.js', 'standalone.js').replace('index.js', 'standalone.js');
    } else {
        // use npm repository
        path = `https://unpkg.com/k3d@${semverRange}/dist/standalone.js`;
    }

    fileLoader(path, (data) => {
        data = fflate.strToU8(data);
        sourceCode = buffer.arrayBufferToBase64(fflate.zlibSync(data));
    });
}

function getHTMLSnapshot(K3D, compressionLevel) {
    const data = buffer.arrayBufferToBase64(K3D.getSnapshot(compressionLevel));
    let filecontent;
    const timestamp = new Date().toUTCString();

    if (K3D.parameters.snapshotType === 'full') {
        filecontent = templateStandalone;
        filecontent = filecontent.split('[REQUIRE_JS]').join(requireJsSource);
        filecontent = filecontent.split('[FFLATE_JS]').join(fflateJsSource);
        filecontent = filecontent.split('[K3D_SOURCE]').join(sourceCode);
    } else if (K3D.parameters.snapshotType === 'online') {
        filecontent = templateOnline;
        filecontent = filecontent.split('[VERSION]').join(K3D.parameters.guiVersion);
    } else if (K3D.parameters.snapshotType === 'inline') {
        filecontent = templateInline;
        filecontent = filecontent.split('[ID]').join(Math.floor(Math.random() * 1e9));
        filecontent = filecontent.split('[HEIGHT]').join(K3D.parameters.height || 512);
        filecontent = filecontent.split('[VERSION]').join(K3D.parameters.guiVersion);
    }

    filecontent = filecontent.split('[DATA]').join(data);
    filecontent = filecontent.split('[TIMESTAMP]').join(timestamp);
    filecontent = filecontent.split('[ADDITIONAL]').join('//[ADDITIONAL]');

    return filecontent;
}

function handleFileSelect(K3D, evt) {
    const { files } = evt.dataTransfer;
    const HTMLSnapshotReader = new FileReader();
    const BinarySnapshotReader = new FileReader();
    const STLReader = new FileReader();

    evt.stopPropagation();
    evt.preventDefault();

    HTMLSnapshotReader.onload = function (event) {
        const snapshot = K3D.extractSnapshot(event.target.result);

        if (snapshot[1]) {
            K3D.setSnapshot(snapshot[1]);
        }
    };

    BinarySnapshotReader.onload = function (event) {
        K3D.setSnapshot(fflate.unzlibSync(event.target.result));
    };

    STLReader.onload = function (event) {
        const stl = event.target.result;

        K3D.load({
            objects: [{
                type: 'STL',
                model_matrix: {
                    data: [
                        1, 0, 0, 0,
                        0, 1, 0, 0,
                        0, 0, 1, 0,
                        0, 0, 0, 1],
                },
                binary: stl,
                visible: true,
                color: 0x0000ff,
                wireframe: false,
                flat_shading: false,
            }],
        });
    };

    if (files.length > 0) {
        if (files[0].name.substr(-4).toLowerCase() === 'html') {
            HTMLSnapshotReader.readAsText(files[0]);
            return;
        }

        if (files[0].name.substr(-3).toLowerCase() === 'stl') {
            STLReader.readAsArrayBuffer(files[0]);
            return;
        }

        BinarySnapshotReader.readAsArrayBuffer(files[0]);
    }
}

function handleDragOver(evt) {
    evt.stopPropagation();
    evt.preventDefault();
    evt.dataTransfer.dropEffect = 'copy';
}

function snapshotGUI(gui, K3D) {
    const obj = {
        snapshot() {
            let data = getHTMLSnapshot(K3D, 9);
            let filename = `K3D-snapshot-${Date.now()}.html`;

            if (K3D.parameters.name) {
                filename = `${K3D.parameters.name}.html`;
            }

            data = new Blob([data], { type: 'text/plain;charset=utf-8' });
            FileSaver.saveAs(data, filename);
        },
    };
    gui.add(obj, 'snapshot').name('Snapshot HTML');

    // Setup the dnd listeners.
    const targetDomNode = K3D.getWorld().targetDOMNode;

    targetDomNode.addEventListener('dragover', handleDragOver, false);
    targetDomNode.addEventListener('drop', handleFileSelect.bind(null, K3D), false);
}

module.exports = {
    snapshotGUI,
    getHTMLSnapshot,
};
<|endoftext|>


<commit_before>var git = require('../'),
    rimraf = require('rimraf'),
    fs = require( 'fs' );

var historyCountKnownSHA = 'fce88902e66c72b5b93e75bdb5ae717038b221f6';

// Helper functions
var helper = {
  // Test if obj is a true function
  testFunction: function(test, obj, label) {
    // The object reports itself as a function
    test(typeof obj, 'function', label + ' reports as a function.');
    // This ensures the repo is actually a derivative of the Function [[Class]]
    test(toString.call(obj), '[object Function]', label + ' [[Class]] is of type function.');
  },
  // Test code and handle exception thrown
  testException: function(test, fun, label) {
    try {
      fun();
      test(false, label);
    }
    catch (ex) {
      test(true, label);
    }
  }
};

/**
 * Test that the commit object is present.
 */
exports.method = function(test){
  test.expect(2);
  helper.testFunction(test.equals, git.commit, 'Commmit');
  test.done();
};

exports.message = function(test) {
  test.expect(3);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      commit.message(function(error, message) {
        test.equals(error, null, 'There should be no error');
        test.notEqual(message, null, 'Message should not be null');
        test.equals(message, 'Update README.md', 'Message should match expected value');
        test.done();
      });
    });
  });
};

exports.sha = function(test) {
  test.expect(3);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      commit.sha(function(error, sha) {
        test.equals(error, null, 'There should be no error');
        test.notEqual(sha, null, 'SHA should not be null');
        test.equals(sha, historyCountKnownSHA, 'SHA should match expected value');
        test.done();
      });
    });
  });
};

exports.time = function(test) {
  test.expect(3);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      commit.time(function(error, time) {
        test.equals(error, null, 'There should be no error');
        test.notEqual(time, null, 'Time should not be null');
        test.equals(time, 1362012884, 'Time should match expected value');
        test.done();
      });
    });
  });
};

exports.offset = function(test) {
  test.expect(3);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      commit.offset(function(error, offset) {
        test.equals(error, null, 'There should be no error');
        test.notEqual(offset, null, 'Offset should not be null');
        test.equals(offset, 780, 'Offset should match expected value');
        test.done();
      });
    });
  });
};

exports.author = function(test) {
  test.expect(2);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      commit.author(function(error, author) {
        test.equals(error, null, 'There should be no error');
        test.notEqual(author, null, 'Author should not be null');
        test.done();
      });
    });
  });
};

exports.authorName = function(test) {
  test.expect(1);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      commit.author(function commitAuthor(error, author) {
        author.name(function authorName(error, name) {
          test.equals(name, 'Michael Robinson', 'The author name should match expected value');
          test.done();
        });
      });
    });
  });
};

exports.authorEmail = function(test) {
  test.expect(1);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      commit.author(function commitAuthor(error, author) {
        author.email(function authorName(error, email) {
          test.equals(email, 'mike@panmedia.co.nz', 'The author email should match expected value');
          test.done();
        });
      });
    });
  });
};

exports.committerName = function(test) {
  test.expect(1);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      commit.committer(function commitCommitter(error, committer) {
        committer.name(function committerName(error, name) {
          test.equals(name, 'Michael Robinson', 'The author name should match expected value');
          test.done();
        });
      });
    });
  });
};

exports.committerEmail = function(test) {
  test.expect(1);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      commit.committer(function commitCommitter(error, committer) {
        committer.email(function committerName(error, email) {
          test.equals(email, 'mike@panmedia.co.nz', 'The committer email should match expected value');
          test.done();
        });
      });
    });
  });
};

/**
 * Test that improper commit ID's result in an error message
 */
exports.improperCommitId = function(test) {
  test.expect(1);
  git.repo('../.git', function(error, repository) {
    repository.commit('not a proper commit sha', function(error, commit) {
      test.notEqual(error, null, 'Error should occur');
      test.done();
    });
  });
};

/**
 * Test that retreiving walking a given commit's history works as expected.
 */
exports.history = function(test) {
  test.expect(368);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      test.equals(null, error, 'Getting latest branch commit should not error');
      var historyCount = 0;
      var expectedHistoryCount = 364;
      commit.history().on('commit', function(error, commit) {
        test.equals(null, error, 'There should be no errors');
        historyCount++;
      }).on('end', function(error, commits) {
        test.equals(null, error, 'There should be no errors');
        test.equals(historyCount, expectedHistoryCount, 'Manual count does not match expected');
        test.equals(commits.length, expectedHistoryCount, '"end" count does not match expected');
        test.done();
      });
    });
  });
};

/**
 * Test that retreiving master branch's HEAD commit works as expected.
 */
exports.masterHead = function(test) {
  test.expect(2);
  git.repo('../.git', function(error, repository) {
    repository.branch('master', function(error, branch) {
      test.equals(error, null, 'Getting branch should not error');
      branch.sha(function(error, sha) {
        repository.commit(sha, function(error, commit) {
          test.equals(error, null, 'Getting latest branch commit should not error');
          test.done();
        });
      });
    });
  });
};

/**
 * Test that retreiving parent works as expected.
 *
 * @param  {Object} test
 */
exports.parents = function(test) {
  test.expect(3);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      commit.parents(function(error, parents) {
        test.equals(parents.length, 1, 'Commit should have exactly one parent');
        parents[0].sha(function parentSha(error, sha) {
          test.equals(error, null, 'Getting parent SHA should not error');
          test.equals(sha, 'ecfd36c80a3e9081f200dfda2391acadb56dac27', 'Parent SHA should match expected value');
          test.done();
        });
      });

    });
  });
};

/**
 * Test that retrieving and walking a commit's tree works as expected.
 */
exports.tree = function(test) {
  test.expect(2);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      test.equals(error, null, 'Getting latest branch commit should not error');

      var commitTreeEntryCount = 0;
      var expectedCommitTreeEntryCount = 198;

      commit.tree(function commitTree(error, tree) {
        tree.walk().on('entry', function(error, entry) {
          commitTreeEntryCount++;
        }).on('end', function(error, entries) {
          test.equals(commitTreeEntryCount, expectedCommitTreeEntryCount, 'Commit tree entry count does not match expected');

          test.done();
        });
      });
    });
  });
};

/**
 * Test that parentsDiffTrees works as expected.
 */
exports.parentsDiffTrees = function(test) {
  test.expect(1);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      commit.parentsDiffTrees(function(error, parentsDiffTrees) {
        test.equals(parentsDiffTrees.length, 1, 'Should be one item in parents diff trees');
        test.done();
      });
    });
  });
};
<commit_msg>Whitespace<commit_after>var git = require('../'),
    rimraf = require('rimraf'),
    fs = require( 'fs' );

var historyCountKnownSHA = 'fce88902e66c72b5b93e75bdb5ae717038b221f6';

// Helper functions
var helper = {
  // Test if obj is a true function
  testFunction: function(test, obj, label) {
    // The object reports itself as a function
    test(typeof obj, 'function', label + ' reports as a function.');
    // This ensures the repo is actually a derivative of the Function [[Class]]
    test(toString.call(obj), '[object Function]', label + ' [[Class]] is of type function.');
  },
  // Test code and handle exception thrown
  testException: function(test, fun, label) {
    try {
      fun();
      test(false, label);
    }
    catch (ex) {
      test(true, label);
    }
  }
};

/**
 * Test that the commit object is present.
 */
exports.method = function(test){
  test.expect(2);
  helper.testFunction(test.equals, git.commit, 'Commmit');
  test.done();
};

exports.message = function(test) {
  test.expect(3);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      commit.message(function(error, message) {
        test.equals(error, null, 'There should be no error');
        test.notEqual(message, null, 'Message should not be null');
        test.equals(message, 'Update README.md', 'Message should match expected value');
        test.done();
      });
    });
  });
};

exports.sha = function(test) {
  test.expect(3);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      commit.sha(function(error, sha) {
        test.equals(error, null, 'There should be no error');
        test.notEqual(sha, null, 'SHA should not be null');
        test.equals(sha, historyCountKnownSHA, 'SHA should match expected value');
        test.done();
      });
    });
  });
};

exports.time = function(test) {
  test.expect(3);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      commit.time(function(error, time) {
        test.equals(error, null, 'There should be no error');
        test.notEqual(time, null, 'Time should not be null');
        test.equals(time, 1362012884, 'Time should match expected value');
        test.done();
      });
    });
  });
};

exports.offset = function(test) {
  test.expect(3);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      commit.offset(function(error, offset) {
        test.equals(error, null, 'There should be no error');
        test.notEqual(offset, null, 'Offset should not be null');
        test.equals(offset, 780, 'Offset should match expected value');
        test.done();
      });
    });
  });
};

exports.author = function(test) {
  test.expect(2);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      commit.author(function(error, author) {
        test.equals(error, null, 'There should be no error');
        test.notEqual(author, null, 'Author should not be null');
        test.done();
      });
    });
  });
};

exports.authorName = function(test) {
  test.expect(1);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      commit.author(function commitAuthor(error, author) {
        author.name(function authorName(error, name) {
          test.equals(name, 'Michael Robinson', 'The author name should match expected value');
          test.done();
        });
      });
    });
  });
};

exports.authorEmail = function(test) {
  test.expect(1);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      commit.author(function commitAuthor(error, author) {
        author.email(function authorName(error, email) {
          test.equals(email, 'mike@panmedia.co.nz', 'The author email should match expected value');
          test.done();
        });
      });
    });
  });
};

exports.committerName = function(test) {
  test.expect(1);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      commit.committer(function commitCommitter(error, committer) {
        committer.name(function committerName(error, name) {
          test.equals(name, 'Michael Robinson', 'The author name should match expected value');
          test.done();
        });
      });
    });
  });
};

exports.committerEmail = function(test) {
  test.expect(1);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      commit.committer(function commitCommitter(error, committer) {
        committer.email(function committerName(error, email) {
          test.equals(email, 'mike@panmedia.co.nz', 'The committer email should match expected value');
          test.done();
        });
      });
    });
  });
};

/**
 * Test that improper commit ID's result in an error message
 */
exports.improperCommitId = function(test) {
  test.expect(1);
  git.repo('../.git', function(error, repository) {
    repository.commit('not a proper commit sha', function(error, commit) {
      test.notEqual(error, null, 'Error should occur');
      test.done();
    });
  });
};

/**
 * Test that retreiving walking a given commit's history works as expected.
 */
exports.history = function(test) {
  test.expect(368);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      test.equals(null, error, 'Getting latest branch commit should not error');
      var historyCount = 0;
      var expectedHistoryCount = 364;
      commit.history().on('commit', function(error, commit) {
        test.equals(null, error, 'There should be no errors');
        historyCount++;
      }).on('end', function(error, commits) {
        test.equals(null, error, 'There should be no errors');
        test.equals(historyCount, expectedHistoryCount, 'Manual count does not match expected');
        test.equals(commits.length, expectedHistoryCount, '"end" count does not match expected');
        test.done();
      });
    });
  });
};

/**
 * Test that retreiving master branch's HEAD commit works as expected.
 */
exports.masterHead = function(test) {
  test.expect(2);
  git.repo('../.git', function(error, repository) {
    repository.branch('master', function(error, branch) {
      test.equals(error, null, 'Getting branch should not error');
      branch.sha(function(error, sha) {
        repository.commit(sha, function(error, commit) {
          test.equals(error, null, 'Getting latest branch commit should not error');
          test.done();
        });
      });
    });
  });
};

/**
 * Test that retreiving parent works as expected.
 *
 * @param  {Object} test
 */
exports.parents = function(test) {
  test.expect(3);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      commit.parents(function(error, parents) {
        test.equals(parents.length, 1, 'Commit should have exactly one parent');
        parents[0].sha(function parentSha(error, sha) {
          test.equals(error, null, 'Getting parent SHA should not error');
          test.equals(sha, 'ecfd36c80a3e9081f200dfda2391acadb56dac27', 'Parent SHA should match expected value');
          test.done();
        });
      });
    });
  });
};

/**
 * Test that retrieving and walking a commit's tree works as expected.
 */
exports.tree = function(test) {
  test.expect(2);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      test.equals(error, null, 'Getting latest branch commit should not error');

      var commitTreeEntryCount = 0;
      var expectedCommitTreeEntryCount = 198;

      commit.tree(function commitTree(error, tree) {
        tree.walk().on('entry', function(error, entry) {
          commitTreeEntryCount++;
        }).on('end', function(error, entries) {
          test.equals(commitTreeEntryCount, expectedCommitTreeEntryCount, 'Commit tree entry count does not match expected');
          test.done();
        });
      });
    });
  });
};

/**
 * Test that parentsDiffTrees works as expected.
 */
exports.parentsDiffTrees = function(test) {
  test.expect(1);
  git.repo('../.git', function(error, repository) {
    repository.commit(historyCountKnownSHA, function(error, commit) {
      commit.parentsDiffTrees(function(error, parentsDiffTrees) {
        test.equals(parentsDiffTrees.length, 1, 'Should be one item in parents diff trees');
        test.done();
      });
    });
  });
};
<|endoftext|>


<commit_before>define(['stealth', 'bitcoinjs-lib', 'util/multiParty', 'util/djbec'],
function (Stealth, Bitcoin, multiParty, Curve25519) {
  'use strict';

  var CryptoJS = Bitcoin.Crypto;
  var SHA256 = Bitcoin.Crypto.SHA256;
  var convert = Bitcoin.convert;
  var BigInteger = Bitcoin.BigInteger;

  // Generate a pair of keys for the whole session
  var priv = multiParty.genPrivateKey();
  var pub = multiParty.genPublicKey();

  // Utility function to generate fingerprints like cryptocat
  var genFingerprint = function(key) {
	return CryptoJS.SHA512(
		convert.bytesToWordArray(
			Curve25519.bi2bytes(key, 32)
		)
	)
		.toString()
		.substring(0, 40)
		.toUpperCase()
  }

  /************************************
   * Channel
   */
  function Channel(transport, name) {
      var self = this;
      var client = transport.client;
      this.callbacks = {};
      this.priv = priv;
      this.pub = pub;
      this.transport = transport;
      this.name = name;

      // Set some identity variables
      this.fingerprint = multiParty.genFingerprint();
      multiParty.setNickName = this.fingerprint;

      var newMe = transport.initializePeer(pub.toByteArrayUnsigned());
      this.transport.comms.pubKeyHex = newMe.pubKeyHex;
      this.transport.comms.name = newMe.name;

      // hash channel name
      var channelHash = transport.hashChannelName(name);
      this.channelHash = channelHash;

      // Subscribe to channel updates
      if (this.subscribed != channelHash) {
        var _onChannelData = function(_data) {self.onChannelData(_data);};
        if (client.handler_map["chan.update." + channelHash]) {
            // update callback
            client.handler_map["chan.update." + channelHash] = _onChannelData;
        } else {
            this.channelSubscribe(function(err, data){
                if (!err) {
                    self.subscribed = channelHash;
                }
                console.log("channel subscribed", err, data)
                self.triggerCallbacks('subscribed', {})
            }, _onChannelData);
        }
      }
  }

  // Send opening messages when joining a channel
  Channel.prototype.sendOpening = function() {
      // Send announcement
      var data = JSON.parse(multiParty.sendPublicKey('all'));
      data.sender = this.fingerprint;
      data = JSON.stringify(data);

      // Send encrypted
      this.postEncrypted(data, function(err, data){
          //console.log("announcement posted", err, data)
      });
  }

  // Subscribe to given channel
  Channel.prototype.channelSubscribe = function(callback, update_cb) {
      var client = this.transport.client;
      client.chan_subscribe("b", this.channelHash, callback, update_cb);
  }

  // Post to given channel
  Channel.prototype.post = function(data, callback) {
      var client = this.transport.client;
      data.sender = this.fingerprint;
      client.chan_post("b", this.channelHash, data, callback);
  }

  Channel.prototype.postEncrypted = function(data, callback) {
      var encrypted = sjcl.encrypt(this.channelHash, data, {ks: 256, ts: 128});
      this.post(encrypted, callback);
  }

  // Callback for data received on channel
  Channel.prototype.addCallback = function(type, callback) {
      if (!this.callbacks.hasOwnProperty(type)) {
          this.callbacks[type] = [callback]
      } else {
          this.callbacks[type].push(callback)
      }
  }
  Channel.prototype.triggerCallbacks = function(type, data) {
      if (this.callbacks.hasOwnProperty(type)) {
          this.callbacks[type].forEach(function(cb) {cb(data)})
      }
  }

  Channel.prototype.onChannelData = function(message) {
      var transport = this.transport;
      var sessionKey = transport.getSessionKey();

      var decrypted, rawDecrypted;
      var decoded = JSON.parse(message.data);

      // An encrypted message coming on the channel
      if (decoded.cipher) {
          // channel layer
          rawDecrypted = sjcl.decrypt(this.channelHash, message.data);
          decrypted = JSON.parse(rawDecrypted);

          // cryptocat protocol layer
          var first;
          if (decrypted.type == 'publicKey') {
              first = Object.keys(decrypted.text)[0];
              var pubKeyB64 = decrypted.text[first]['message'];
              var pubKey = convert.base64ToBytes(pubKeyB64);
              var pubKeyBi = BigInteger.fromByteArrayUnsigned(pubKey);
              var newPeer = transport.addPeer(pubKey, pubKey);
              newPeer.fingerprint = genFingerprint(pubKeyBi)
              this.startPairing(newPeer.fingerprint, pubKey)
              // set key owner to 'myName' so cryptocat will import the key
              if (decrypted.sender == first) {
                multiParty.receiveMessage(decrypted.sender, first, rawDecrypted);
              } else {
                console.log('sender and pubkey owner dont match!')
              }
          }
          else if (decrypted.type == 'shout') {
              //console.log(decrypted.text)
          } else {
              multiParty.receiveMessage(decrypted.sender, this.fingerprint, rawDecrypted);
          }
          this.triggerCallbacks(decrypted.type, decrypted);
      }
      transport.update();
  }

  Channel.prototype.startPairing = function(fingerprint, pubKey) {
    console.log('startpairing', fingerprint, pubKey)
  }

  return Channel;
});
<commit_msg>fix receiving pubKey broadcasts.<commit_after>define(['stealth', 'bitcoinjs-lib', 'util/multiParty', 'util/djbec'],
function (Stealth, Bitcoin, multiParty, Curve25519) {
  'use strict';

  var CryptoJS = Bitcoin.Crypto;
  var SHA256 = Bitcoin.Crypto.SHA256;
  var convert = Bitcoin.convert;
  var BigInteger = Bitcoin.BigInteger;

  // Generate a pair of keys for the whole session
  var priv = multiParty.genPrivateKey();
  var pub = multiParty.genPublicKey();

  // Utility function to generate fingerprints like cryptocat
  var genFingerprint = function(key) {
	return CryptoJS.SHA512(
		convert.bytesToWordArray(
			Curve25519.bi2bytes(key, 32)
		)
	)
		.toString()
		.substring(0, 40)
		.toUpperCase()
  }

  /************************************
   * Channel
   */
  function Channel(transport, name) {
      var self = this;
      var client = transport.client;
      this.callbacks = {};
      this.priv = priv;
      this.pub = pub;
      this.transport = transport;
      this.name = name;

      // Set some identity variables
      this.fingerprint = multiParty.genFingerprint();
      multiParty.setNickName = this.fingerprint;

      var newMe = transport.initializePeer(pub.toByteArrayUnsigned());
      this.transport.comms.pubKeyHex = newMe.pubKeyHex;
      this.transport.comms.name = newMe.name;

      // hash channel name
      var channelHash = transport.hashChannelName(name);
      this.channelHash = channelHash;

      // Subscribe to channel updates
      if (this.subscribed != channelHash) {
        var _onChannelData = function(_data) {self.onChannelData(_data);};
        if (client.handler_map["chan.update." + channelHash]) {
            // update callback
            client.handler_map["chan.update." + channelHash] = _onChannelData;
        } else {
            this.channelSubscribe(function(err, data){
                if (!err) {
                    self.subscribed = channelHash;
                }
                console.log("channel subscribed", err, data)
                self.triggerCallbacks('subscribed', {})
            }, _onChannelData);
        }
      }
  }

  // Send opening messages when joining a channel
  Channel.prototype.sendOpening = function() {
      // Send announcement
      var data = JSON.parse(multiParty.sendPublicKey('all'));
      data.sender = this.fingerprint;
      data = JSON.stringify(data);

      // Send encrypted
      this.postEncrypted(data, function(err, data){
          //console.log("announcement posted", err, data)
      });
  }

  // Subscribe to given channel
  Channel.prototype.channelSubscribe = function(callback, update_cb) {
      var client = this.transport.client;
      client.chan_subscribe("b", this.channelHash, callback, update_cb);
  }

  // Post to given channel
  Channel.prototype.post = function(data, callback) {
      var client = this.transport.client;
      data.sender = this.fingerprint;
      client.chan_post("b", this.channelHash, data, callback);
  }

  Channel.prototype.postEncrypted = function(data, callback) {
      var encrypted = sjcl.encrypt(this.name, data, {ks: 256, ts: 128});
      this.post(encrypted, callback);
  }

  // Callback for data received on channel
  Channel.prototype.addCallback = function(type, callback) {
      if (!this.callbacks.hasOwnProperty(type)) {
          this.callbacks[type] = [callback]
      } else {
          this.callbacks[type].push(callback)
      }
  }
  Channel.prototype.triggerCallbacks = function(type, data) {
      if (this.callbacks.hasOwnProperty(type)) {
          this.callbacks[type].forEach(function(cb) {cb(data)})
      }
  }

  Channel.prototype.onChannelData = function(message) {
      var transport = this.transport;
      var sessionKey = transport.getSessionKey();

      var decrypted, rawDecrypted;
      var decoded = JSON.parse(message.data);

      // An encrypted message coming on the channel
      if (decoded.cipher) {
          // channel layer
          rawDecrypted = sjcl.decrypt(this.name, message.data);
          decrypted = JSON.parse(rawDecrypted);

          // cryptocat protocol layer
          var first;
          if (decrypted.type == 'publicKey') {
              first = Object.keys(decrypted.text)[0];
              var pubKeyB64 = decrypted.text[first]['message'];
              var pubKey = convert.base64ToBytes(pubKeyB64);
              var pubKeyBi = BigInteger.fromByteArrayUnsigned(pubKey);
              var newPeer = transport.addPeer(pubKey, pubKey);
              newPeer.fingerprint = genFingerprint(pubKeyBi)
              this.startPairing(newPeer.fingerprint, pubKey)
              // set key owner to 'myName' so cryptocat will import the key
              if (first == 'all') {
                multiParty.receiveMessage(decrypted.sender, first, rawDecrypted);
              } else {
                // normal cryptocat protocol
                multiParty.receiveMessage(decrypted.sender, this.fingerprint, rawDecrypted);

              }
          }
          else if (decrypted.type == 'shout') {
              //console.log(decrypted.text)
          } else {
              multiParty.receiveMessage(decrypted.sender, this.fingerprint, rawDecrypted);
          }
          this.triggerCallbacks(decrypted.type, decrypted);
      }
      transport.update();
  }

  Channel.prototype.startPairing = function(fingerprint, pubKey) {
    console.log('startpairing', fingerprint, pubKey)
  }

  return Channel;
});
<|endoftext|>


<commit_before>import React from 'react';
import * as activityFns from './activityfns.js';
import ActivityDay from './activityday.js';
import '../../styles/activities.css';
import * as DB from '../../constants/database.js';
import app from '../Firebase';

const db = app.firestore();

/**
 * ReactJS class component for the list of activities.
 *
 * @property {Object} props ReactJS props.
 * @property {string} tripId The tripID.
 */
class ActivityList extends React.Component {
  /** @override */
  constructor(props) {
    super(props);
    this.state = { days : [] };

    this.getActivityList = this.getActivityList.bind(this);
  }

  /**
   * Gets the list of activities from the server.
   *
   * This function sets `this.state.days` to the sorted days.
   *
   * @param {string} tripId The trip ID.
   */
  async getActivityList(tripId) {
    db.collection(DB.COLLECTION_TRIPS).doc(tripId)
    .collection(DB.COLLECTION_ACTIVITIES)
    .onSnapshot(querySnapshot => {
      let tripActivities = [];
      this.setState({days: []});
      querySnapshot.forEach(doc => {
        console.log(doc.data());
        let data = doc.data();
        data['id'] = doc.id;
        data['tripId'] = tripId;

        // TODO: if start date != end date, split into 2 days. (#37)

      // Eliminate nanoseconds, convert to milliseconds.
      data[DB.ACTIVITIES_START_TIME] =
        data[DB.ACTIVITIES_START_TIME]['seconds'] * 1000;
      data[DB.ACTIVITIES_END_TIME] =
        data[DB.ACTIVITIES_END_TIME]['seconds'] * 1000;

        tripActivities.push(data);
      });
      this.setState({ days: activityFns.sortByDate(tripActivities) });
    });

  }

  /**
   * @override
   *
   * Get sorted list of activities from the database.
   *
   * This function only queries the database if `this.state` is defined.
   * `this.state` gains an entry with key `days` with the list of activities
   * sorted by date.
   */
  async componentDidMount() {
    if (this.state === null || this.props.tripId === null) { return; }
    await this.getActivityList(this.props.tripId);
  }

  /** @override */
  render() {
    if (this.state === null) { return (

); }
    if (this.state.days === null) {
      return (
An error has occurred :(
 );
    } else if (this.state.days.length === 0) {
      return (
Plan your trip here!
);
    }

    return (

        {this.state.days.map((item, index) => (
          <ActivityDay date={item[0]} activities={item[1]} key={index}/>
        ))}

    );
  }
}

export default ActivityList;
<commit_msg>Add activity compatibility (it doesn't break)<commit_after>import React from 'react';
import * as activityFns from './activityfns.js';
import ActivityDay from './activityday.js';
import '../../styles/activities.css';
import * as DB from '../../constants/database.js';
import app from '../Firebase';

const db = app.firestore();

/**
 * ReactJS class component for the list of activities.
 *
 * @property {Object} props ReactJS props.
 * @property {string} tripId The tripID.
 */
class ActivityList extends React.Component {
  /** @override */
  constructor(props) {
    super(props);
    this.state = { days : [] };

    this.getActivityList = this.getActivityList.bind(this);
  }

  /**
   * Gets the list of activities from the server.
   *
   * This function sets `this.state.days` to the sorted days.
   *
   * @param {string} tripId The trip ID.
   */
  async getActivityList(tripId) {
    db.collection(DB.COLLECTION_TRIPS).doc(tripId)
    .collection(DB.COLLECTION_ACTIVITIES)
    .onSnapshot(querySnapshot => {
      let tripActivities = [];
      this.setState({days: []});
      querySnapshot.forEach(doc => {
        console.log(doc.data());
        let data = doc.data();
        data['id'] = doc.id;
        data['tripId'] = tripId;

        // TODO: if start date != end date, split into 2 days. (#37)

        if (data[DB.ACTIVITIES_START_TIME] !== undefined) {// not in new mode
          // Eliminate nanoseconds, convert to milliseconds.
          data[DB.ACTIVITIES_START_TIME] =
            data[DB.ACTIVITIES_START_TIME]['seconds'] * 1000;
          data[DB.ACTIVITIES_END_TIME] =
            data[DB.ACTIVITIES_END_TIME]['seconds'] * 1000;
        }
        tripActivities.push(data);
      });
      this.setState({ days: activityFns.sortByDate(tripActivities) });
    });

  }

  /**
   * @override
   *
   * Get sorted list of activities from the database.
   *
   * This function only queries the database if `this.state` is defined.
   * `this.state` gains an entry with key `days` with the list of activities
   * sorted by date.
   */
  async componentDidMount() {
    if (this.state === null || this.props.tripId === null) { return; }
    await this.getActivityList(this.props.tripId);
  }

  /** @override */
  render() {
    if (this.state === null) { return (

); }
    if (this.state.days === null) {
      return (
An error has occurred :(
 );
    } else if (this.state.days.length === 0) {
      return (
Plan your trip here!
);
    }

    return (

        {this.state.days.map((item, index) => (
          <ActivityDay date={item[0]} activities={item[1]} key={index}/>
        ))}

    );
  }
}

export default ActivityList;
<|endoftext|>


<commit_before>
// Sparky.filter

import {
	append,
	curry,
	contains,
	equals,
	formatDate,
	formatTime,
	get,
	getPath,
	invoke,
	is,
	isDefined,
	last,
	limit,
	addDate,
	addTime,
	subTime,
	max,
	min,
	mod,
	multiply,
	not,
	pow,
	postpad,
	prepad,
	prepend,
	exp,
	log,
	root,
	slugify,
	toCartesian,
	toPolar,
	toDeg,
	toRad,
	toLevel,
	todB,
	toInt,
	toFloat,
	toFixed,
	toString,
	toType,
	normalise,
	denormalise
} from '../../fn/fn.js';

import {
	escape,
	toPx,
	toRem
} from '../../dom/dom.js';

var debug     = true;
var A         = Array.prototype;
var assign    = Object.assign;

function interpolateLinear(xs, ys, x) {
	var n = -1;
	while (++n < xs.length && xs[n] < x);

	// Shortcut if x is lower than smallest x
	if (n === 0) {
		return ys[0];
	}

	// Shortcut if x is greater than biggest x
	if (n >= xs.length) {
		return last(ys);
	}

	// Shortcurt if x corresponds exactly to an interpolation coordinate
	if (x === xs[n]) {
		return ys[n];
	}

	// Linear interpolate
	var ratio = (x - xs[n - 1]) / (xs[n] - xs[n - 1]) ;
	return ratio * (ys[n] - ys[n - 1]) + ys[n - 1] ;
}

export const transformers = {
	add:         {
		tx: curry(function(a, b) { return b.add ? b.add(a) : b + a ; }),
		ix: curry(function(a, c) { return c.add ? c.add(-a) : c - a ; })
	},

	'add-date':  { tx: addDate,     ix: curry(function(d, n) { return addDate('-' + d, n); }) },
	'add-time':  { tx: addTime,     ix: subTime },
	decibels:    { tx: todB,        ix: toLevel },
	multiply:    { tx: multiply,    ix: curry(function(d, n) { return n / d; }) },
	degrees:     { tx: toDeg,       ix: toRad },
	radians:     { tx: toRad,       ix: toDeg },
	pow:         { tx: pow,         ix: function(n) { return pow(1/n); } },
	exp:         { tx: exp,         ix: log },
	log:         { tx: log,         ix: exp },
	int:         { tx: toFixed(0),  ix: toInt },
	float:       { tx: toFloat,     ix: toString },
	boolean:     { tx: Boolean,     ix: toString },
	normalise:   { tx: normalise,   ix: denormalise },
	denormalise: { tx: denormalise, ix: normalise },
	floatformat: { tx: toFixed,     ix: curry(function(n, str) { return parseFloat(str); }) },
	'int-string': { tx: function(value) { return value ? value + '' : '' ; }, ix: toInt },

	interpolate: {
		tx: function(point) {
			var xs = A.map.call(arguments, get('0'));
			var ys = A.map.call(arguments, get('1'));

			return function(value) {
				return interpolateLinear(xs, ys, value);
			};
		},

		ix: function(point) {
			var xs = A.map.call(arguments, get('0'));
			var ys = A.map.call(arguments, get('1'));

			return function(value) {
				return interpolateLinear(ys, xs, value);
			}
		}
	},

	cartesian: { tx: toCartesian, ix: toPolar },
	polar:     { tx: toPolar, ix: toCartesian },
	deg:       { tx: toDeg, ix: toRad },
	rad:       { tx: toRad, ix: toDeg },
	decibels:  { tx: todB, ix: toLevel },
	level:     { tx: toLevel, ix: todB },
	px:        { tx: toPx, ix: toRem },
	rem:       { tx: toRem, ix: toPx }
};

export const transforms = {

	contains:     contains,
	equals:       equals,
	escape:       escape,
	exp:          exp,
	formatdate:   formatDate,
	formattime:   formatTime,
	formatfloat:  toFixed,

	// formatfloat...
	//curry(function(n, value) {
	//	return typeof value === 'number' ? Number.prototype.toFixed.call(value, n) :
	//		!isDefined(value) ? '' :
	//		(Sparky.debug && console.warn('Sparky: filter floatformat: ' + n + ' called on non-number ' + value)) ;
	//}),

	get:          getPath,
	invoke:       invoke,
	is:           is,
	last:         last,
	limit:        limit,
	log:          log,
	max:          max,
	min:          min,
	mod:          mod,
	not:          not,
	percent:      multiply(100),

	// Strings
	append:       append,
	prepend:      prepend,
	prepad:       prepad,
	postpad:      postpad,
	slugify:      slugify,

	// root(2) - square root
	// root(3) - cubed root, etc.
	root:         root,

	// slugify('Howdy, Michael')
	// > 'howdy-michael'

	type:         toType,

	//toStringType: Fn.toStringType,

	// Sparky transforms

	divide: curry(function(n, value) {
		if (typeof value !== 'number') { return; }
		return value / n;
	}),

	'find-in': curry(function(path, id) {
		if (!isDefined(id)) { return; }
		var array = getPath(path, window);
		return array && array.find(compose(is(id), get('id')));
	}),

	floatformat: curry(function(n, value) {
		return typeof value === 'number' ? Number.prototype.toFixed.call(value, n) :
			!isDefined(value) ? '' :
			(debug && console.warn('Sparky: filter floatformat: ' + n + ' called on non-number ' + value)) ;
	}),

	floor: Math.floor,

	"greater-than": curry(function(value2, value1) {
		return value1 > value2;
	}),

	invert: function(value) {
		return typeof value === 'number' ? 1 / value : !value ;
	},

	join: curry(function(string, value) {
		return A.join.call(value, string);
	}),

	json: JSON.stringify,

	"less-than": curry(function(value2, value1) {
		return value1 < value2 ;
	}),

	localise: curry(function(digits, value) {
		var locale = document.documentElement.lang;
		var options = {};

		if (isDefined(digits)) {
			options.minimumFractionDigits = digits;
			options.maximumFractionDigits = digits;
		}

		// Todo: localise value where toLocaleString not supported
		return value.toLocaleString ? value.toLocaleString(locale, options) : value ;
	}),

	lowercase: function(value) {
		if (typeof value !== 'string') { return; }
		return String.prototype.toLowerCase.apply(value);
	},

	map: function(method, params) {
		var fn;

		if (typeof params === undefined) {
			fn = parse(method);

			return function(array) {
				return array.map(fn);
			};
		}

		fn = ((transformers[method] && transformers[method].tx)
			|| transforms[method]).apply(null, params);

		return function(array) {
			return array && array.map(fn);
		};
	},

	filter: curry(function(method, args, array) {
		return array && array.map(transforms[method].apply(null,args));
	}, true),

	match: curry(function(regex, string) {
		regex = typeof regex === 'string' ? RegExp(regex) : regex ;
		return regex.exec(string);
	}),

	matches: curry(function(regex, string) {
		regex = typeof regex === 'string' ? RegExp(regex) : regex ;
		return !!regex.test(string);
	}),

	pluralise: curry(function(str1, str2, lang, value) {
		if (typeof value !== 'number') { return; }

		str1 = str1 || '';
		str2 = str2 || 's';

		// In French, numbers less than 2 are considered singular, where in
		// English, Italian and elsewhere only 1 is singular.
		return lang === 'fr' ?
			(value < 2 && value >= 0) ? str1 : str2 :
			value === 1 ? str1 : str2 ;
	}),

	reduce: curry(function(name, initialValue, array) {
		return array && array.reduce(Fn[name], initialValue || 0);
	}, true),

	replace: curry(function(str1, str2, value) {
		if (typeof value !== 'string') { return; }
		return value.replace(RegExp(str1, 'g'), str2);
	}),

	round: curry(function round(n, value) {
		return Math.round(value / n) * n;
	}),

	slice: curry(function(i0, i1, value) {
		return typeof value === 'string' ?
			value.slice(i0, i1) :
			Array.prototype.slice.call(value, i0, i1) ;
	}, true),

	striptags: (function() {
		var rtag = /<(?:[^>'"]|"[^"]*"|'[^']*')*>/g;

		return function(value) {
			return value.replace(rtag, '');
		};
	})(),

	switch: function(value) {
		if (typeof value === 'boolean') { value = Number(value); }
		if (typeof value === 'string') { value = parseInt(value, 10); }
		if (typeof value !== 'number' || Number.isNaN(value)) { return; }
		return arguments[value + 1];
	},

	translate: (function() {
		var warned = {};

		return function(value) {
			var translations = translations;

			if (!translations) {
				if (!warned.missingTranslations) {
					console.warn('Sparky: Missing lookup object Sparky.translations');
					warned.missingTranslations = true;
				}
				return value;
			}

			var text = translations[value] ;

			if (!text) {
				if (!warned[value]) {
					console.warn('Sparky: Sparky.translations contains no translation for "' + value + '"');
					warned[value] = true;
				}

				return value;
			}

			return text ;
		};
	})(),

	truncatechars: curry(function(n, value) {
		return value.length > n ?
			value.slice(0, n) + '…' :
			value ;
	}),

	uppercase: function(value) {
		if (typeof value !== 'string') { return; }
		return String.prototype.toUpperCase.apply(value);
	},

	//urlencode
	//urlize
	//urlizetrunc
	//wordcount
	//wordwrap

	yesno: curry(function(truthy, falsy, value) {
		return value ? truthy : falsy ;
	})
};
<commit_msg>reduce transform looks up reducers<commit_after>
// Sparky.filter

import {
	add,
	append,
	curry,
	contains,
	equals,
	formatDate,
	formatTime,
	get,
	getPath,
	invoke,
	is,
	isDefined,
	last,
	limit,
	addDate,
	addTime,
	subTime,
	max,
	min,
	mod,
	multiply,
	not,
	pow,
	postpad,
	prepad,
	prepend,
	exp,
	log,
	root,
	slugify,
	toCartesian,
	toPolar,
	toDeg,
	toRad,
	toLevel,
	todB,
	toInt,
	toFloat,
	toFixed,
	toString,
	toType,
	normalise,
	denormalise
} from '../../fn/fn.js';

import {
	escape,
	toPx,
	toRem
} from '../../dom/dom.js';

var debug     = true;
var A         = Array.prototype;
var assign    = Object.assign;

const reducers = {
	sum: add
};

function interpolateLinear(xs, ys, x) {
	var n = -1;
	while (++n < xs.length && xs[n] < x);

	// Shortcut if x is lower than smallest x
	if (n === 0) {
		return ys[0];
	}

	// Shortcut if x is greater than biggest x
	if (n >= xs.length) {
		return last(ys);
	}

	// Shortcurt if x corresponds exactly to an interpolation coordinate
	if (x === xs[n]) {
		return ys[n];
	}

	// Linear interpolate
	var ratio = (x - xs[n - 1]) / (xs[n] - xs[n - 1]) ;
	return ratio * (ys[n] - ys[n - 1]) + ys[n - 1] ;
}

export const transformers = {
	add:         {
		tx: curry(function(a, b) { return b.add ? b.add(a) : b + a ; }),
		ix: curry(function(a, c) { return c.add ? c.add(-a) : c - a ; })
	},

	'add-date':  { tx: addDate,     ix: curry(function(d, n) { return addDate('-' + d, n); }) },
	'add-time':  { tx: addTime,     ix: subTime },
	decibels:    { tx: todB,        ix: toLevel },
	multiply:    { tx: multiply,    ix: curry(function(d, n) { return n / d; }) },
	degrees:     { tx: toDeg,       ix: toRad },
	radians:     { tx: toRad,       ix: toDeg },
	pow:         { tx: pow,         ix: function(n) { return pow(1/n); } },
	exp:         { tx: exp,         ix: log },
	log:         { tx: log,         ix: exp },
	int:         { tx: toFixed(0),  ix: toInt },
	float:       { tx: toFloat,     ix: toString },
	boolean:     { tx: Boolean,     ix: toString },
	normalise:   { tx: normalise,   ix: denormalise },
	denormalise: { tx: denormalise, ix: normalise },
	floatformat: { tx: toFixed,     ix: curry(function(n, str) { return parseFloat(str); }) },
	'int-string': { tx: function(value) { return value ? value + '' : '' ; }, ix: toInt },

	interpolate: {
		tx: function(point) {
			var xs = A.map.call(arguments, get('0'));
			var ys = A.map.call(arguments, get('1'));

			return function(value) {
				return interpolateLinear(xs, ys, value);
			};
		},

		ix: function(point) {
			var xs = A.map.call(arguments, get('0'));
			var ys = A.map.call(arguments, get('1'));

			return function(value) {
				return interpolateLinear(ys, xs, value);
			}
		}
	},

	cartesian: { tx: toCartesian, ix: toPolar },
	polar:     { tx: toPolar, ix: toCartesian },
	deg:       { tx: toDeg, ix: toRad },
	rad:       { tx: toRad, ix: toDeg },
	decibels:  { tx: todB, ix: toLevel },
	level:     { tx: toLevel, ix: todB },
	px:        { tx: toPx, ix: toRem },
	rem:       { tx: toRem, ix: toPx }
};

export const transforms = {

	contains:     contains,
	equals:       equals,
	escape:       escape,
	exp:          exp,
	formatdate:   formatDate,
	formattime:   formatTime,
	formatfloat:  toFixed,

	// formatfloat...
	//curry(function(n, value) {
	//	return typeof value === 'number' ? Number.prototype.toFixed.call(value, n) :
	//		!isDefined(value) ? '' :
	//		(Sparky.debug && console.warn('Sparky: filter floatformat: ' + n + ' called on non-number ' + value)) ;
	//}),

	get:          getPath,
	invoke:       invoke,
	is:           is,
	last:         last,
	limit:        limit,
	log:          log,
	max:          max,
	min:          min,
	mod:          mod,
	not:          not,
	percent:      multiply(100),

	// Strings
	append:       append,
	prepend:      prepend,
	prepad:       prepad,
	postpad:      postpad,
	slugify:      slugify,

	// root(2) - square root
	// root(3) - cubed root, etc.
	root:         root,

	// slugify('Howdy, Michael')
	// > 'howdy-michael'

	type:         toType,

	//toStringType: Fn.toStringType,

	// Sparky transforms

	divide: curry(function(n, value) {
		if (typeof value !== 'number') { return; }
		return value / n;
	}),

	'find-in': curry(function(path, id) {
		if (!isDefined(id)) { return; }
		var array = getPath(path, window);
		return array && array.find(compose(is(id), get('id')));
	}),

	floatformat: curry(function(n, value) {
		return typeof value === 'number' ? Number.prototype.toFixed.call(value, n) :
			!isDefined(value) ? '' :
			(debug && console.warn('Sparky: filter floatformat: ' + n + ' called on non-number ' + value)) ;
	}),

	floor: Math.floor,

	"greater-than": curry(function(value2, value1) {
		return value1 > value2;
	}),

	invert: function(value) {
		return typeof value === 'number' ? 1 / value : !value ;
	},

	join: curry(function(string, value) {
		return A.join.call(value, string);
	}),

	json: JSON.stringify,

	"less-than": curry(function(value2, value1) {
		return value1 < value2 ;
	}),

	localise: curry(function(digits, value) {
		var locale = document.documentElement.lang;
		var options = {};

		if (isDefined(digits)) {
			options.minimumFractionDigits = digits;
			options.maximumFractionDigits = digits;
		}

		// Todo: localise value where toLocaleString not supported
		return value.toLocaleString ? value.toLocaleString(locale, options) : value ;
	}),

	lowercase: function(value) {
		if (typeof value !== 'string') { return; }
		return String.prototype.toLowerCase.apply(value);
	},

	map: function(method, params) {
		var fn;

		if (typeof params === undefined) {
			fn = parse(method);
			return function(array) {
				return array.map(fn);
			};
		}

		fn = ((transformers[method] && transformers[method].tx)
			|| transforms[method]).apply(null, params);

		return function(array) {
			return array && array.map(fn);
		};
	},

	filter: curry(function(method, args, array) {
		return array && array.map(transforms[method].apply(null,args));
	}, true),

	match: curry(function(regex, string) {
		regex = typeof regex === 'string' ? RegExp(regex) : regex ;
		return regex.exec(string);
	}),

	matches: curry(function(regex, string) {
		regex = typeof regex === 'string' ? RegExp(regex) : regex ;
		return !!regex.test(string);
	}),

	pluralise: curry(function(str1, str2, lang, value) {
		if (typeof value !== 'number') { return; }

		str1 = str1 || '';
		str2 = str2 || 's';

		// In French, numbers less than 2 are considered singular, where in
		// English, Italian and elsewhere only 1 is singular.
		return lang === 'fr' ?
			(value < 2 && value >= 0) ? str1 : str2 :
			value === 1 ? str1 : str2 ;
	}),

	reduce: curry(function(name, initialValue, array) {
		return array && array.reduce(reducers[name], initialValue || 0);
	}, true),

	replace: curry(function(str1, str2, value) {
		if (typeof value !== 'string') { return; }
		return value.replace(RegExp(str1, 'g'), str2);
	}),

	round: curry(function round(n, value) {
		return Math.round(value / n) * n;
	}),

	slice: curry(function(i0, i1, value) {
		return typeof value === 'string' ?
			value.slice(i0, i1) :
			Array.prototype.slice.call(value, i0, i1) ;
	}, true),

	striptags: (function() {
		var rtag = /<(?:[^>'"]|"[^"]*"|'[^']*')*>/g;

		return function(value) {
			return value.replace(rtag, '');
		};
	})(),

	switch: function(value) {
		if (typeof value === 'boolean') { value = Number(value); }
		if (typeof value === 'string') { value = parseInt(value, 10); }
		if (typeof value !== 'number' || Number.isNaN(value)) { return; }
		return arguments[value + 1];
	},

	translate: (function() {
		var warned = {};

		return function(value) {
			var translations = translations;

			if (!translations) {
				if (!warned.missingTranslations) {
					console.warn('Sparky: Missing lookup object Sparky.translations');
					warned.missingTranslations = true;
				}
				return value;
			}

			var text = translations[value] ;

			if (!text) {
				if (!warned[value]) {
					console.warn('Sparky: Sparky.translations contains no translation for "' + value + '"');
					warned[value] = true;
				}

				return value;
			}

			return text ;
		};
	})(),

	truncatechars: curry(function(n, value) {
		return value.length > n ?
			value.slice(0, n) + '…' :
			value ;
	}),

	uppercase: function(value) {
		if (typeof value !== 'string') { return; }
		return String.prototype.toUpperCase.apply(value);
	},

	//urlencode
	//urlize
	//urlizetrunc
	//wordcount
	//wordwrap

	yesno: curry(function(truthy, falsy, value) {
		return value ? truthy : falsy ;
	})
};
<|endoftext|>


<commit_before>"use strict";

var fontFamilies = [
	'Arial',
	'Arial Black',
	'Comic Sans MS',
	'Courier New',
	'Georgia',
	'Impact',
	'Tahoma',
	'Times New Roman',
	'Trebuchet MS',
	'Verdana'
].map(function (family) {

	return {
		id: family,
		value: family,
		weights: ['normal', 'bold']
			.map(function (value) {

				return {
					id: value,
					value: value
				};
			})
	};
});

function normalizeColorCode (color) {

	return color[0] != '#' ?
		color :
		(color.length == 7 ?
			color :
			'#'+color[1]+color[1]+color[2]+color[2]+color[3]+color[3]
		);
}

var colors = [
	'black',
	'#444',
	'#666',
	'#ccc',
	'#eee',
	'white',
	'#f4a',
	'#fb0',
	'#ae0',
	'#5cf'
].map(function (color) {

	return {
		id: normalizeColorCode(color),
		value: color
	};
});

var sizes = [
	'1em',
	'1.125em',
	'1.25em',
	'1.5em',
	'1.75em',
	'2em',
	'2.25em',
	'2.5em',
	'3em',
].map(function (size) {

	return {
		id: size,
		value: size
	};
});

var StyleDialogView = Backbone.View.extend({

	events: {

		'click i.js-cancel': 'cancel',
		'click div.bottom-buttons button.js-save'  : 'save'
	},

	$template: $($.parseHTML(
		$('script#style-dialog-template[type="text/template-html"]').text()
	)),

	initialize: function (options) {

		// Copy model attributes for cancel.
		this.oldModelAttributes = this.model.toJSON();

		// Show the dialog.
		$('body').addClass('modal-overlay');
		$('#modal-overlay').append(this.$el);

		this.subViews = [];
		var $pickers = this.$el.find('.js-pickers');
		var makePickerViewFromData = function (pickerData) {

			pickerData.collection = new Backbone.Collection(pickerData.items);

			var selectedValue = this.model.get(pickerData.id);
			pickerData.model = new Backbone.Model({
				selectedValue: selectedValue
			});

			var pickerView = new (pickerData.PickerView || PickerView)(pickerData);
			this.subViews.push(pickerView);

			// Update the style.
			// TODO: The picker messages the style diealog to update the site style.
			// Make it instead use a model that is the style property. new CssProperty({name: 'color', value: 'black'})
			pickerView.on('select', function (model) {

				this.model.set(pickerData.id, model.get('value'));
			}.bind(this));

			pickerView.$el.appendTo($pickers);

			return pickerView;
		}.bind(this);

		var pickerViews = [

			{
				id: 'color',
				title: 'Color',
				ItemView: ColorPickerItemView,
				items: colors
			},

			{
				id: 'font-size',
				title: 'Size',
				ItemView: SizePickerItemView,
				items: sizes
			},

			{
				id: 'font-family',
				title: 'Font Family',
				ItemView: FontFamilyPickerItemView,
				items: fontFamilies
			}
		]
			.map(function (pickerData) {

				return makePickerViewFromData(pickerData);
			});

		// Set this up separately, since it has a dependency.
		// It is a bit special, since it has to listen to the font family change,
		// and list the sizes available for it.
		pickerViews.fontWeight = makePickerViewFromData({
			id: 'font-weight',
			title: 'Font Weight',
			PickerView: FontWeightPickerView,
			ItemView: FontWeightPickerItemView,
			fontFamilyPickerView: pickerViews[2]
		});
	},

	save: function () {

		// Triggers the REST call.
		// this.model.save();

		this.close();
	},

	cancel: function () {

		this.model.set(this.oldModelAttributes);
		this.close();
	},

	close: function () {

		// Close the dialog.
		this.remove();
		$('body').removeClass('modal-overlay');
	},

	// TODO: Move this to the Backbone.View base class.
	remove: function () {

		Backbone.View.prototype.remove.apply(this, arguments);

		this.subViews
			.forEach(function(view){
				view.remove();
			});
	}
});
<commit_msg>Cleanup.<commit_after>"use strict";

var fontFamilies = [
	'Arial',
	'Arial Black',
	'Comic Sans MS',
	'Courier New',
	'Georgia',
	'Impact',
	'Tahoma',
	'Times New Roman',
	'Trebuchet MS',
	'Verdana'
].map(function (family) {

	return {
		id: family,
		value: family,
		weights: ['normal', 'bold']
			.map(function (value) {

				return {
					id: value,
					value: value
				};
			})
	};
});

function normalizeColorCode (color) {

	return color[0] != '#' ?
		color :
		(color.length == 7 ?
			color :
			'#'+color[1]+color[1]+color[2]+color[2]+color[3]+color[3]
		);
}

var colors = [
	'black',
	'#444',
	'#666',
	'#ccc',
	'#eee',
	'white',
	'#f4a',
	'#fb0',
	'#ae0',
	'#5cf'
].map(function (color) {

	return {
		id: normalizeColorCode(color),
		value: color
	};
});

var sizes = [
	'1em',
	'1.125em',
	'1.25em',
	'1.5em',
	'1.75em',
	'2em',
	'2.25em',
	'2.5em',
	'3em',
].map(function (size) {

	return {
		id: size,
		value: size
	};
});

var StyleDialogView = Backbone.View.extend({

	events: {

		'click i.js-cancel': 'cancel',
		'click div.bottom-buttons button.js-save'  : 'save'
	},

	$template: $($.parseHTML(
		$('script#style-dialog-template[type="text/template-html"]').text()
	)),

	initialize: function (options) {

		// Copy model attributes for cancel.
		this.oldModelAttributes = this.model.toJSON();

		// Show the dialog.
		$('body').addClass('modal-overlay');
		$('#modal-overlay').append(this.$el);

		var $pickers = this.$el.find('.js-pickers');
		var makePickerViewFromData = function (pickerData) {

			pickerData.collection = new Backbone.Collection(pickerData.items);

			var selectedValue = this.model.get(pickerData.id);
			pickerData.model = new Backbone.Model({
				selectedValue: selectedValue
			});

			var pickerView = new (pickerData.PickerView || PickerView)(pickerData);

			// Update the style.
			// TODO: The picker messages the style diealog to update the site style.
			// Make it instead use a model that is the style property. new CssProperty({name: 'color', value: 'black'})
			pickerView.on('select', function (model) {

				this.model.set(pickerData.id, model.get('value'));
			}.bind(this));

			pickerView.$el.appendTo($pickers);

			return pickerView;
		}.bind(this);

		this.subViews = [
			{
				id: 'color',
				title: 'Color',
				ItemView: ColorPickerItemView,
				items: colors
			},

			{
				id: 'font-size',
				title: 'Size',
				ItemView: SizePickerItemView,
				items: sizes
			},

			{
				id: 'font-family',
				title: 'Font Family',
				ItemView: FontFamilyPickerItemView,
				items: fontFamilies
			}
		]
			.map(function (pickerData) {

				return makePickerViewFromData(pickerData);
			});

		// Set this up separately, since it has a dependency.
		// It is a bit special, since it has to listen to the font family change,
		// and list the sizes available for it.
		this.subViews.push(makePickerViewFromData({
			id: 'font-weight',
			title: 'Font Weight',
			PickerView: FontWeightPickerView,
			ItemView: FontWeightPickerItemView,
			fontFamilyPickerView: this.subViews[2]
		}));
	},

	save: function () {

		// Triggers the REST call.
		// this.model.save();

		this.close();
	},

	cancel: function () {

		this.model.set(this.oldModelAttributes);
		this.close();
	},

	close: function () {

		// Close the dialog.
		this.remove();
		$('body').removeClass('modal-overlay');
	},

	// TODO: Move this to the Backbone.View base class.
	remove: function () {

		Backbone.View.prototype.remove.apply(this, arguments);

		this.subViews
			.forEach(function(view){
				view.remove();
			});
	}
});
<|endoftext|>


<commit_before>/* ========================================================================
 * Bootstrap: transition.js v3.3.4
 * http://getbootstrap.com/javascript/#transitions
 * ========================================================================
 * Copyright 2011-2015 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * ======================================================================== */

+function ($) {
  'use strict';

  // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
  // ============================================================

  function transitionEnd() {
    var el = document.createElement('bootstrap')

    var transEndEventNames = {
      WebkitTransition : 'webkitTransitionEnd',
      MozTransition    : 'transitionend',
      OTransition      : 'oTransitionEnd otransitionend',
      transition       : 'transitionend'
    }

    for (var name in transEndEventNames) {
      if (el.style[name] !== undefined) {
        return { end: transEndEventNames[name] }
      }
    }

    return false // explicit for ie8 (  ._.)
  }

  // http://blog.alexmaccaw.com/css-transitions
  $.fn.emulateTransitionEnd = function (duration) {
    var called = false
    var $el = this
    $(this).one('bsTransitionEnd', function () { called = true })
    var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
    setTimeout(callback, duration)
    return this
  }

  $(function () {
    $.support.transition = transitionEnd()

    if (!$.support.transition) return

    $.event.special.bsTransitionEnd = {
      bindType: $.support.transition.end,
      delegateType: $.support.transition.end,
      handle: function (e) {
        if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments)
      }
    }
  })

}(jQuery);
<commit_msg>Delete transition.js<commit_after><|endoftext|>


<commit_before>"use strict";

var fontFamilies = [
	'Arial',
	'Arial Black',
	'Comic Sans MS',
	'Courier New',
	'Georgia',
	'Impact',
	'Tahoma',
	'Times New Roman',
	'Trebuchet MS',
	'Verdana'
].map(function (family) {

	return {
		id: family,
		value: family,
		weights: ['normal', 'bold']
			.map(function (value) {

				return {
					id: value,
					value: value
				};
			})
	};
});

function normalizeColorCode (color) {

	return color[0] != '#' ?
		color :
		(color.length == 7 ?
			color :
			'#'+color[1]+color[1]+color[2]+color[2]+color[3]+color[3]
		);
}

var colors = [
	'black',
	'#444',
	'#666',
	'#ccc',
	'#eee',
	'white',
	'#f4a',
	'#fb0',
	'#ae0',
	'#5cf'
].map(function (color) {

	return {
		id: normalizeColorCode(color),
		value: color
	};
});

var sizes = [
	'1em',
	'1.125em',
	'1.25em',
	'1.5em',
	'1.75em',
	'2em',
	'2.25em',
	'2.5em',
	'3em',
].map(function (size) {

	return {
		id: size,
		value: size
	};
});

var StyleDialogView = Backbone.View.extend({

	events: {

		'click i.js-cancel': 'cancel',
		'click div.bottom-buttons button.js-save'  : 'save'
	},

	$template: $($.parseHTML(
		$('script#style-dialog-template[type="text/template-html"]').text()
	)),

	initialize: function (options) {

		// Copy model attributes for cancel.
		this.oldModelAttributes = this.model.toJSON();

		// Show the dialog.
		$('body').addClass('modal-overlay');
		$('#modal-overlay').append(this.$el);

		this.subViews = [];
		var $pickers = this.$el.find('.js-pickers');
		var THIS = this;
		function makePickerViewFromData (pickerData) {

			pickerData.collection = new Backbone.Collection(pickerData.items);

			var selectedValue = THIS.model.get(pickerData.id);
			pickerData.model = new Backbone.Model({
				selectedValue: selectedValue
			});

			var pickerView = new (pickerData.PickerView || PickerView)(pickerData);
			THIS.subViews.push(pickerView);

			// Update the style.
			// TODO: The picker messages the style diealog to update the site style.
			// Make it instead use a model that is the style property. new CssProperty({name: 'color', value: 'black'})
			pickerView.on('select', function (model) {

				THIS.model.set(pickerData.id, model.get('value'));
			});

			pickerView.$el.appendTo($pickers);

			return pickerView;
		}

		var pickerViews = [

			{
				id: 'color',
				title: 'Color',
				ItemView: ColorPickerItemView,
				items: colors
			},

			{
				id: 'font-size',
				title: 'Size',
				ItemView: SizePickerItemView,
				items: sizes
			},

			{
				id: 'font-family',
				title: 'Font Family',
				ItemView: FontFamilyPickerItemView,
				items: fontFamilies
			}
		]
			.map(function (pickerData) {

				return makePickerViewFromData(pickerData);
			});

		// Set this up separately, since it has a dependency.
		// It is a bit special, since it has to listen to the font family change,
		// and list the sizes available for it.
		pickerViews.fontWeight = makePickerViewFromData({
			id: 'font-weight',
			title: 'Font Weight',
			PickerView: FontWeightPickerView,
			ItemView: FontWeightPickerItemView,
			fontFamilyPickerView: pickerViews[2]
		});
	},

	save: function () {

		// Triggers the REST call.
		// this.model.save();

		this.close();
	},

	cancel: function () {

		this.model.set(this.oldModelAttributes);
		this.close();
	},

	close: function () {

		// Close the dialog.
		this.remove();
		$('body').removeClass('modal-overlay');
	},

	// TODO: Move this to the Backbone.View base class.
	remove: function () {

		Backbone.View.prototype.remove.apply(this, arguments);

		this.subViews
			.forEach(function(view){
				view.remove();
			});
	}
});
<commit_msg>Removed stupid this workaround.<commit_after>"use strict";

var fontFamilies = [
	'Arial',
	'Arial Black',
	'Comic Sans MS',
	'Courier New',
	'Georgia',
	'Impact',
	'Tahoma',
	'Times New Roman',
	'Trebuchet MS',
	'Verdana'
].map(function (family) {

	return {
		id: family,
		value: family,
		weights: ['normal', 'bold']
			.map(function (value) {

				return {
					id: value,
					value: value
				};
			})
	};
});

function normalizeColorCode (color) {

	return color[0] != '#' ?
		color :
		(color.length == 7 ?
			color :
			'#'+color[1]+color[1]+color[2]+color[2]+color[3]+color[3]
		);
}

var colors = [
	'black',
	'#444',
	'#666',
	'#ccc',
	'#eee',
	'white',
	'#f4a',
	'#fb0',
	'#ae0',
	'#5cf'
].map(function (color) {

	return {
		id: normalizeColorCode(color),
		value: color
	};
});

var sizes = [
	'1em',
	'1.125em',
	'1.25em',
	'1.5em',
	'1.75em',
	'2em',
	'2.25em',
	'2.5em',
	'3em',
].map(function (size) {

	return {
		id: size,
		value: size
	};
});

var StyleDialogView = Backbone.View.extend({

	events: {

		'click i.js-cancel': 'cancel',
		'click div.bottom-buttons button.js-save'  : 'save'
	},

	$template: $($.parseHTML(
		$('script#style-dialog-template[type="text/template-html"]').text()
	)),

	initialize: function (options) {

		// Copy model attributes for cancel.
		this.oldModelAttributes = this.model.toJSON();

		// Show the dialog.
		$('body').addClass('modal-overlay');
		$('#modal-overlay').append(this.$el);

		this.subViews = [];
		var $pickers = this.$el.find('.js-pickers');
		var makePickerViewFromData = function (pickerData) {

			pickerData.collection = new Backbone.Collection(pickerData.items);

			var selectedValue = this.model.get(pickerData.id);
			pickerData.model = new Backbone.Model({
				selectedValue: selectedValue
			});

			var pickerView = new (pickerData.PickerView || PickerView)(pickerData);
			this.subViews.push(pickerView);

			// Update the style.
			// TODO: The picker messages the style diealog to update the site style.
			// Make it instead use a model that is the style property. new CssProperty({name: 'color', value: 'black'})
			pickerView.on('select', function (model) {

				this.model.set(pickerData.id, model.get('value'));
			}.bind(this));

			pickerView.$el.appendTo($pickers);

			return pickerView;
		}.bind(this);

		var pickerViews = [

			{
				id: 'color',
				title: 'Color',
				ItemView: ColorPickerItemView,
				items: colors
			},

			{
				id: 'font-size',
				title: 'Size',
				ItemView: SizePickerItemView,
				items: sizes
			},

			{
				id: 'font-family',
				title: 'Font Family',
				ItemView: FontFamilyPickerItemView,
				items: fontFamilies
			}
		]
			.map(function (pickerData) {

				return makePickerViewFromData(pickerData);
			});

		// Set this up separately, since it has a dependency.
		// It is a bit special, since it has to listen to the font family change,
		// and list the sizes available for it.
		pickerViews.fontWeight = makePickerViewFromData({
			id: 'font-weight',
			title: 'Font Weight',
			PickerView: FontWeightPickerView,
			ItemView: FontWeightPickerItemView,
			fontFamilyPickerView: pickerViews[2]
		});
	},

	save: function () {

		// Triggers the REST call.
		// this.model.save();

		this.close();
	},

	cancel: function () {

		this.model.set(this.oldModelAttributes);
		this.close();
	},

	close: function () {

		// Close the dialog.
		this.remove();
		$('body').removeClass('modal-overlay');
	},

	// TODO: Move this to the Backbone.View base class.
	remove: function () {

		Backbone.View.prototype.remove.apply(this, arguments);

		this.subViews
			.forEach(function(view){
				view.remove();
			});
	}
});
<|endoftext|>