<commit_before>angular.module('formbuilder', [])
.directive('myFormbuilder', function() {
    return {
        restrict: 'A'
      , link: function(scope, element, attrs) {
            $(element).formbuilder({'useJson': true});
            $(element).children('ul').sortable({ opacity: 0.6, cursor: 'move', axis: "y" });
        }
    }
})
.directive('myFormbuilderload', function() {
    return {
        restrict: 'A'
      , link: function(scope, element, attrs) {
            var widget_key = $(element).attr('widget_key');
            $(element).formbuilder({'load_url': '/feedsetup/load_formbuilder/' + widget_key, 'useJson': true});
            $(element).children('ul').sortable({ opacity: 0.6, cursor: 'move', axis: "y" });
        }
    }
})
.directive('myCreateform', function() {
    return {
        restrict: 'A'
      , link: function(scope, element, attrs) {

           $(element.parents('form')).validate({
                submitHandler: function(form) {

                    var form_db_id;

                    $(form).ajaxSubmit({
                        dataType: 'json'
                      , beforeSubmit: function(formData, jqForm, options) {
                            new Status().notify("Processing...", 1000);
                        }
                      , success: function(responseText, statusText, xhr, $form) {
                            /*
                            var widget_key      = responseText.submit.widget.widgetkey;
                            var widget_store_id = responseText.submit.widget.widgetstoreid;
                            var company_id      = responseText.submit.widget.company_id;
                            */
                            //stub values
                            var widget_key = '123123';
                            var widget_store_id = '123131';
                            var company_id = 6;

                            var formcode_url = $("#formcode-manager-url").attr('hrefaction') + "/" + widget_key;

                            $.ajax({
                                type: "POST"
                              , dataType: 'json'
                              , url: "/feedsetup/buildmetadata_options"
                              , data: $("ul[id^=frmb-]").serializeFormList({ prepend: "frmb" }) + "&form_id=" + widget_store_id + "&company_id=" + company_id
                              , success: function(msg) {
                                    if(msg.validation.length > 0) {
                                        console.log(msg);
                                    } else {
                                        console.log("All is clear! Its a GO for redirection.");
                                    }
                                    //window.location = formcode_url;
                                }
                            });
                        }
                    });
                }
              , errorElement: "em"
              , rules: {
                    theme_name: { required: true }
                }
            });
        }
    }
})
/*
window.onbeforeunload = function() {
    return "Are you sure you want to navigate away from this page?";
};
*/
<commit_msg>You smell like piss<commit_after>angular.module('formbuilder', [])
.directive('myFormbuilder', function() {
    return {
        restrict: 'A'
      , link: function(scope, element, attrs) {
            $(element).formbuilder({'useJson': true});
            $(element).children('ul').sortable({ opacity: 0.6, cursor: 'move', axis: "y" });
        }
    }
})
.directive('myFormbuilderload', function() {
    return {
        restrict: 'A'
      , link: function(scope, element, attrs) {
            var widget_key = $(element).attr('widget_key');
            $(element).formbuilder({'load_url': '/feedsetup/load_formbuilder/' + widget_key, 'useJson': true});
            $(element).children('ul').sortable({ opacity: 0.6, cursor: 'move', axis: "y" });
        }
    }
})
.directive('myCreateform', function() {
    return {
        restrict: 'A'
      , link: function(scope, element, attrs) {

           $(element.parents('form')).validate({
                submitHandler: function(form) {

                    var form_db_id;

                    $(form).ajaxSubmit({
                        dataType: 'json'
                      , beforeSubmit: function(formData, jqForm, options) {
                            new Status().notify("Processing...", 1000);
                        }
                      , success: function(responseText, statusText, xhr, $form) {
                            /*
                            var widget_key      = responseText.submit.widget.widgetkey;
                            var widget_store_id = responseText.submit.widget.widgetstoreid;
                            var company_id      = responseText.submit.widget.company_id;
                            */
                            //stub values
                            var widget_key = '123123';
                            var widget_store_id = '123131';
                            var company_id = 6;

                            var formcode_url = $("#formcode-manager-url").attr('hrefaction') + "/" + widget_key;

                            $.ajax({
                                type: "POST"
                              , dataType: 'json'
                              , url: "/feedsetup/buildmetadata_options"
                              , data: $("ul[id^=frmb-]").serializeFormList({ prepend: "frmb" }) + "&form_id=" + widget_store_id + "&company_id=" + company_id
                              , success: function(msg) {
                                    if(msg.validation.length > 0) {
                                        for(var i=0; i<msg.validation; i++) {
                                            console.log(msg.validation[i]);
                                        }
                                    } else {
                                        console.log("All is clear! Its a GO for redirection.");
                                    }
                                    //window.location = formcode_url;
                                }
                            });
                        }
                    });
                }
              , errorElement: "em"
              , rules: {
                    theme_name: { required: true }
                }
            });
        }
    }
})
/*
window.onbeforeunload = function() {
    return "Are you sure you want to navigate away from this page?";
};
*/
<|endoftext|>


<commit_before>const glob = require('glob');
const fs = require('fs');
const path = require('path');
const marked = require('marked');
const renderer = new marked.Renderer();
const formatDate = require('date-fns/format');
const camelCase = require('camel-case');
const titleCase = require('title-case');
const shell = require('shelljs');
const getFileNameFromPath = require('@lukeboyle/get-filename-from-path');

renderer.heading = function(code, level) {
	if (level === 1) {
		return `

${code}

		`;
	} else {
		return `<h${level}>${code}</h${level}>`;
	}
};

renderer.code = function(code, language) {
	return `

		${code.split('\n').map(codeBlock => {
		const codeWithEscapedQuotes = codeBlock.split('"').join('\\"');
		const codeWithEscapedHashes = codeWithEscapedQuotes.split('#').join('\\#');
		return `<div>{"${codeWithEscapedHashes}"}</div>`;
	}).join('')}

`;
};

renderer.table = function(header, body) {
	if (header.includes('Metadata name')) {
		const rows = body.split('
');
		const dateRow = rows.find(row => row.includes('post_date'));
		const rawDate = dateRow.split('')[2];
		const date = rawDate.slice(0, rawDate.length - 12);
		return `

			<time datetime="${date}">${formatDate(date, 'Do of MMMM, YYYY')}</time>

`
	} else {
		return header + body;
	}
};

renderer.image = function(href, title, text) {
	return `<img src="${href}" alt="${text}"/>`;
};

function getMarkupFromMarkdown(markdownString) {
	return marked(markdownString, {renderer: renderer, gfm: true});
}

function generateComponent(acc, curr, index) {
	const fileName = getFileNameFromPath(curr.path);
	const camelCaseName = camelCase(fileName);
	const postContents = curr.contents;

	// TODO: use regex
	const postStatusIndex = postContents.indexOf('post_status');
	const almostPostStatus = postContents.slice(postStatusIndex + 13);
	const postStatus = almostPostStatus.slice(0, almostPostStatus.indexOf('|')).trim();

	const lines = postContents.split('\n');
	// TODO: fuck this off
	let tableMode = false;
	const contents = lines.reduce((acc, curr, index) => {
		if (curr.slice(0, 2) === '# ') {
			return Object.assign({}, acc, {
				title: curr.slice(2)
			});
		}

		// Table Start
		if (curr.includes('| Metadata name |')) {
			tableMode = true;
		}

		if (tableMode === true) {
			if (curr.slice(0, 2) === '| ') {
				const lineWithoutFirstDelimeter = curr.slice(2);
				const key = lineWithoutFirstDelimeter.slice(0, lineWithoutFirstDelimeter.indexOf(' | '));
				const value = lineWithoutFirstDelimeter.slice(lineWithoutFirstDelimeter.indexOf(' | ') + 3, -2);

				return Object.assign({}, acc, {
					metaData: Object.assign({}, acc.metaData, {
						[key]: value
					})
				});
			} else {
				tableMode = false;
			}
		}

		return Object.assign({}, acc, {
			contents: `${acc.contents || ''}\n${curr}`
		})
	}, {});

	if (postStatus !== 'draft') {
		acc.push({
			path: curr.path,
			fileName,
			componentName: camelCaseName[0].toUpperCase() + camelCaseName.slice(1),
			component: `
			import React from 'react';
			import BlogPost from '../../../../components/blog-post.jsx';

			export default class ${camelCaseName} extends React.Component {
				render() {
					return (
						<BlogPost
							isSinglePostPage={!this.props.isBlogPage}
							title="${contents.title}"
							publishDate="${contents.metaData['post_date']}"
							slug="/${curr.path.replace('.md', '')}"
						>
							${getMarkupFromMarkdown(contents.contents)}
						</BlogPost>
					);
				}
			}
		`
		});
	}

	return acc;
}

(() => {
	glob('blog-posts/**/*.md', {}, (err, files) => {
		const blogPosts = files.reduce((acc, curr) => {
			acc.push({
				path: curr,
				contents: fs.readFileSync(curr, {encoding: 'utf-8'})
			});

			return acc;
		}, []);

		const reversedComponents = blogPosts.reduce(generateComponent, []);

		shell.rm('-rf', path.resolve(`${__dirname}/../src/pages/blog-posts`));

		reversedComponents.forEach(component => {
			shell.mkdir('-p', path.resolve(`${__dirname}/../src/pages/${component.path.replace(`/${component.fileName}.md`, '')}`));
			fs.writeFileSync(path.resolve(`${__dirname}/../src/pages/${component.path.replace('.md', '.jsx')}`), component.component);
		});

		const components = reversedComponents.reverse();
		const postsPerPage = 4;
		const pages = components.reduce((acc, curr, index) => {
			const pagesSoFar = Object.keys(acc);

			const lastPage = parseInt(pagesSoFar[pagesSoFar.length - 1], 10) || 0;

			if (index % postsPerPage === 0) {
				const newPage = lastPage + 1;

				return Object.assign(
					{},
					acc,
					{
						[newPage]: [
							curr
						]
					}
				);
			}

			return Object.assign(
				{},
				acc,
				{
					[lastPage]: [
						...acc[lastPage],
						curr
					]
				}
			);
		}, {});

		Object.entries(pages).forEach(([key, value], index) => {
			const blogPage = `
			import React from 'react';
			import Helmet from 'react-helmet';
			${value.reduce((acc, curr) => {
				return acc + `import ${curr.componentName} from '${index === 0 ? './' : '../'}${curr.path.replace('.md', '.jsx')}';\n`;
			}, '')}

			export default class Blog extends React.Component {
				render() {
					return (

							<Helmet>
								<title>${index === 0 ? 'Blog | Luke Boyle' : `Page ${parseInt(key, 10) - 1} | Blog`}</title>
							</Helmet>

									Boyleing Point

									7/11 was an inside job

								${value.reduce((acc, curr) => {
				return acc + `<${curr.componentName} isBlogPage={true}/>\n`;
			}, '')}

									${index > 0 ? `<li><a href="${key === '2' ? '/blog' : `/blog/${parseInt(key, 10) - 2}`}">Newer</a></li>` : ''}
									${index !== Object.values(pages).length - 1 ? `<li className="pagination__next"><a href="/blog/${parseInt(key, 10)}">Older</a></li>` : ''}

					);
				}
			}
		`;

			const fileName = index === 0
				? `${__dirname}/../src/pages/blog.jsx`
				: `${__dirname}/../src/pages/blog/${index}.jsx`;

			fs.writeFileSync(path.resolve(fileName), blogPage);
		});
	});
})();<commit_msg>chore: use object.keys instead of entries for node compatibility<commit_after>const glob = require('glob');
const fs = require('fs');
const path = require('path');
const marked = require('marked');
const renderer = new marked.Renderer();
const formatDate = require('date-fns/format');
const camelCase = require('camel-case');
const titleCase = require('title-case');
const shell = require('shelljs');
const getFileNameFromPath = require('@lukeboyle/get-filename-from-path');

renderer.heading = function(code, level) {
	if (level === 1) {
		return `

${code}

		`;
	} else {
		return `<h${level}>${code}</h${level}>`;
	}
};

renderer.code = function(code, language) {
	return `

		${code.split('\n').map(codeBlock => {
		const codeWithEscapedQuotes = codeBlock.split('"').join('\\"');
		const codeWithEscapedHashes = codeWithEscapedQuotes.split('#').join('\\#');
		return `<div>{"${codeWithEscapedHashes}"}</div>`;
	}).join('')}

`;
};

renderer.table = function(header, body) {
	if (header.includes('Metadata name')) {
		const rows = body.split('
');
		const dateRow = rows.find(row => row.includes('post_date'));
		const rawDate = dateRow.split('')[2];
		const date = rawDate.slice(0, rawDate.length - 12);
		return `

			<time datetime="${date}">${formatDate(date, 'Do of MMMM, YYYY')}</time>

`
	} else {
		return header + body;
	}
};

renderer.image = function(href, title, text) {
	return `<img src="${href}" alt="${text}"/>`;
};

function getMarkupFromMarkdown(markdownString) {
	return marked(markdownString, {renderer: renderer, gfm: true});
}

function generateComponent(acc, curr, index) {
	const fileName = getFileNameFromPath(curr.path);
	const camelCaseName = camelCase(fileName);
	const postContents = curr.contents;

	// TODO: use regex
	const postStatusIndex = postContents.indexOf('post_status');
	const almostPostStatus = postContents.slice(postStatusIndex + 13);
	const postStatus = almostPostStatus.slice(0, almostPostStatus.indexOf('|')).trim();

	const lines = postContents.split('\n');
	// TODO: fuck this off
	let tableMode = false;
	const contents = lines.reduce((acc, curr, index) => {
		if (curr.slice(0, 2) === '# ') {
			return Object.assign({}, acc, {
				title: curr.slice(2)
			});
		}

		// Table Start
		if (curr.includes('| Metadata name |')) {
			tableMode = true;
		}

		if (tableMode === true) {
			if (curr.slice(0, 2) === '| ') {
				const lineWithoutFirstDelimeter = curr.slice(2);
				const key = lineWithoutFirstDelimeter.slice(0, lineWithoutFirstDelimeter.indexOf(' | '));
				const value = lineWithoutFirstDelimeter.slice(lineWithoutFirstDelimeter.indexOf(' | ') + 3, -2);

				return Object.assign({}, acc, {
					metaData: Object.assign({}, acc.metaData, {
						[key]: value
					})
				});
			} else {
				tableMode = false;
			}
		}

		return Object.assign({}, acc, {
			contents: `${acc.contents || ''}\n${curr}`
		})
	}, {});

	if (postStatus !== 'draft') {
		acc.push({
			path: curr.path,
			fileName,
			componentName: camelCaseName[0].toUpperCase() + camelCaseName.slice(1),
			component: `
			import React from 'react';
			import BlogPost from '../../../../components/blog-post.jsx';

			export default class ${camelCaseName} extends React.Component {
				render() {
					return (
						<BlogPost
							isSinglePostPage={!this.props.isBlogPage}
							title="${contents.title}"
							publishDate="${contents.metaData['post_date']}"
							slug="/${curr.path.replace('.md', '')}"
						>
							${getMarkupFromMarkdown(contents.contents)}
						</BlogPost>
					);
				}
			}
		`
		});
	}

	return acc;
}

(() => {
	glob('blog-posts/**/*.md', {}, (err, files) => {
		const blogPosts = files.reduce((acc, curr) => {
			acc.push({
				path: curr,
				contents: fs.readFileSync(curr, {encoding: 'utf-8'})
			});

			return acc;
		}, []);

		const reversedComponents = blogPosts.reduce(generateComponent, []);

		shell.rm('-rf', path.resolve(`${__dirname}/../src/pages/blog-posts`));

		reversedComponents.forEach(component => {
			shell.mkdir('-p', path.resolve(`${__dirname}/../src/pages/${component.path.replace(`/${component.fileName}.md`, '')}`));
			fs.writeFileSync(path.resolve(`${__dirname}/../src/pages/${component.path.replace('.md', '.jsx')}`), component.component);
		});

		const components = reversedComponents.reverse();
		const postsPerPage = 4;
		const pages = components.reduce((acc, curr, index) => {
			const pagesSoFar = Object.keys(acc);

			const lastPage = parseInt(pagesSoFar[pagesSoFar.length - 1], 10) || 0;

			if (index % postsPerPage === 0) {
				const newPage = lastPage + 1;

				return Object.assign(
					{},
					acc,
					{
						[newPage]: [
							curr
						]
					}
				);
			}

			return Object.assign(
				{},
				acc,
				{
					[lastPage]: [
						...acc[lastPage],
						curr
					]
				}
			);
		}, {});

		Object.keys(pages).forEach((key, index) => {
			const blogPage = `
			import React from 'react';
			import Helmet from 'react-helmet';
			${pages[key].reduce((acc, curr) => {
				return acc + `import ${curr.componentName} from '${index === 0 ? './' : '../'}${curr.path.replace('.md', '.jsx')}';\n`;
			}, '')}

			export default class Blog extends React.Component {
				render() {
					return (

							<Helmet>
								<title>${index === 0 ? 'Blog | Luke Boyle' : `Page ${parseInt(key, 10) - 1} | Blog`}</title>
							</Helmet>

									Boyleing Point

									7/11 was an inside job

								${pages[key].reduce((acc, curr) => {
				return acc + `<${curr.componentName} isBlogPage={true} />\n`;
			}, '')}

									${index > 0 ? `<li><a href="${key === '2' ? '/blog' : `/blog/${parseInt(key, 10) - 2}`}">Newer</a></li>` : ''}
									${index !== Object.values(pages).length - 1 ? `<li className="pagination__next"><a href="/blog/${parseInt(key, 10)}">Older</a></li>` : ''}

					);
				}
			}
		`;

			const fileName = index === 0
				? `${__dirname}/../src/pages/blog.jsx`
				: `${__dirname}/../src/pages/blog/${index}.jsx`;

			fs.writeFileSync(path.resolve(fileName), blogPage);
		});
	});
})();<|endoftext|>


<commit_before>/*
    Copyright (c) 2014 Zachary Seguin

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.
*/

'use strict';

angular
    .module('YouWantFood')
    .factory('YouWantFoodAPI', ['$q', '$resource', function($q, $resource) {

        var cache = {};

        // API Functions
        var YouWantFoodAPI = {

           outlets: function() {
              if (!cache.outlets) {
                 cache.outlets = $resource('api/?data=outlets', {}, { get: { cache: true }});
              }

              return cache.outlets;
           },
           outlet: function(id) {
               var deferred = $q.defer();

               this.outlets().query(function(outlets) {
                  for (var indx in outlets) {
                     if (outlets[indx].outlet_id == id) {
                        deferred.resolve(outlets[indx]);
                        break;
                     }
                  }

                  deferred.reject('outlet not found');
               });

               return deferred.promise;
           },

           menu: function(id) {
              if (!cache.menu) {
                 cache.menu = [];
              }

              if (!cache.menu[id]) {
                 cache.menu[id] = $resource('api/?data=menu&outlet_id=:outlet_id', {outlet_id: '@id'}, { get: {params: {outlet_id: id}, isArray: false, cache: true}});
              }

              return cache.menu[id];
           }

        };

        return YouWantFoodAPI;
    }]);
<commit_msg>Minor change<commit_after>/*
    Copyright (c) 2014 Zachary Seguin

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.
*/

'use strict';

angular
    .module('YouWantFood')
    .factory('YouWantFoodAPI', ['$q', '$resource', function($q, $resource) {

        var cache = {};

        // API Functions
        var YouWantFoodAPI = {

           outlets: function() {
              if (!cache.outlets) {
                 cache.outlets = $resource('api/?data=outlets', {}, { get: { cache: true }});
              }

              return cache.outlets;
           },
           outlet: function(id) {
               var deferred = $q.defer();

               this.outlets().query(function(outlets) {
                  for (var indx in outlets) {
                     if (outlets[indx].outlet_id == id) {
                        deferred.resolve(outlets[indx]);
                        break;
                     }
                  }

                  deferred.reject('outlet not found');
               });

               return deferred.promise;
           },

           menu: function(id) {
              if (!cache.menu) {
                 cache.menu = [];
              }

              if (!cache.menu[id]) {
                 cache.menu[id] = $resource('api/?data=menu&outlet_id=:outlet_id', {outlet_id: '@outlet_id'}, { get: {params: {outlet_id: id}, isArray: false, cache: true}});
              }

              return cache.menu[id];
           }

        };

        return YouWantFoodAPI;
    }]);
<|endoftext|>


<commit_before>describe("diamondash.widgets.pie", function() {
  var models = diamondash.widgets.pie.models,
      views = diamondash.widgets.pie.views,
      fixtures = diamondash.test.fixtures;

  describe(".PieDimensions", function() {
    var dims;

    beforeEach(function() {
      dims = new views.PieDimensions({width: 64});
    });

    it("should expose its width as its height", function() {
        assert.equal(dims.height(), 64);
    });

    it("should expose its radius", function() {
        assert.equal(dims.radius(), 64 / 2);
    });

    it("should expose its offset", function() {
        assert.deepEqual(dims.offset(), {
            x: 64 / 2,
            y: 64 / 2
        });
    });
  });

  describe(".PieView", function() {
    var pie;

    beforeEach(function() {
      pie = new views.PieView({
        el: $('<div>').width(960),
        model: new models.PieModel(
          fixtures.get('diamondash.widgets.pie.models.PieModel:simple'))
      });
    });

    describe(".render", function() {
      it("should render a pie chart from its model's metrics", function() {
        assert.lengthOf(pie.$('.arc'), 0);
        pie.render();
        assert.lengthOf(pie.$('.arc'), 3);

        var $svg = $('<svg>');
        d3.select($svg.get(0))
          .selectAll('.arc')
          .data(d3.layout.pie()([18, 8, 34]))
          .enter()
            .append('g')
            .attr('class', 'arc')
              .append('path')
              .attr('d', d3.svg.arc()
                .outerRadius(960 / 2)
                .innerRadius(0));

        var $actual = pie.$('.arc path');
        var $expected = $svg.find('.arc path');
        assert.equal($actual.eq(0).attr('d'), $expected.eq(0).attr('d'));
        assert.equal($actual.eq(1).attr('d'), $expected.eq(1).attr('d'));
        assert.equal($actual.eq(2).attr('d'), $expected.eq(2).attr('d'));

        var metrics = pie.model.get('metrics');
        assert.equal($actual.eq(0).css('fill'), metrics.at(0).get('color'));
        assert.equal($actual.eq(1).css('fill'), metrics.at(1).get('color'));
        assert.equal($actual.eq(2).css('fill'), metrics.at(2).get('color'));
      });
    });
  });
});
<commit_msg>Failing test to show that we don't re-render pie charts correctly<commit_after>describe("diamondash.widgets.pie", function() {
  var models = diamondash.widgets.pie.models,
      testUtils = diamondash.test.utils,
      views = diamondash.widgets.pie.views,
      fixtures = diamondash.test.fixtures;

  describe(".PieDimensions", function() {
    var dims;

    beforeEach(function() {
      dims = new views.PieDimensions({width: 64});
    });

    it("should expose its width as its height", function() {
        assert.equal(dims.height(), 64);
    });

    it("should expose its radius", function() {
        assert.equal(dims.radius(), 64 / 2);
    });

    it("should expose its offset", function() {
        assert.deepEqual(dims.offset(), {
            x: 64 / 2,
            y: 64 / 2
        });
    });
  });

  describe(".PieView", function() {
    var pie;

    function draw_pie(opts) {
      var $svg = $('<svg>');

      var layout = d3.layout.pie().value(function(d) {
        return d.v;
      });

      d3.select($svg.get(0))
        .selectAll('.arc')
        .data(layout(opts.data), function(d) {
          return d.data.k;
        })
        .enter()
          .append('g')
          .attr('class', 'arc')
            .append('path')
            .attr('d', d3.svg.arc()
              .outerRadius(opts.radius)
              .innerRadius(0));

      return $svg;
    }

    beforeEach(function() {
      pie = new views.PieView({
        el: $('<div>').width(960),
        model: new models.PieModel(
          fixtures.get('diamondash.widgets.pie.models.PieModel:simple'))
      });
    });

    afterEach(function() {
      testUtils.unregisterModels();
    });

    describe(".render", function() {
      it("should render a pie chart from its model's metrics", function() {
        assert.lengthOf(pie.$('.arc'), 0);
        pie.render();
        assert.lengthOf(pie.$('.arc'), 3);

        var $pie = draw_pie({
          radius: 960 / 2,
          data: [{
            k: 'metric-a',
            v: 18
          }, {
            k: 'metric-b',
            v: 8
          }, {
            k: 'metric-c',
            v: 34
          }]
        });

        var $actual = pie.$('.arc path');
        var $expected = $pie.find('.arc path');
        assert.equal($actual.eq(0).attr('d'), $expected.eq(0).attr('d'));
        assert.equal($actual.eq(1).attr('d'), $expected.eq(1).attr('d'));
        assert.equal($actual.eq(2).attr('d'), $expected.eq(2).attr('d'));

        var metrics = pie.model.get('metrics');
        assert.equal($actual.eq(0).css('fill'), metrics.at(0).get('color'));
        assert.equal($actual.eq(1).css('fill'), metrics.at(1).get('color'));
        assert.equal($actual.eq(2).css('fill'), metrics.at(2).get('color'));
      });

      it("should re-render correctly based on model changes", function() {
        pie.render();

        pie.model.get('metrics').set([{
          id: 'metric-a',
          name: 'metric-a',
          datapoints: [
            {x: 1340875995000, y: 3}]
        }, {
          id: 'metric-b',
          name: 'metric-b',
          datapoints: [
            {x: 1340875995000, y: 2}]
        }, {
          id: 'metric-c',
          name: 'metric-c',
          datapoints: [
            {x: 1340875995000, y: 9}]
        }]);

        pie.render();

        var $pie = draw_pie({
          radius: 960 / 2,
          data: [{
            k: 'metric-a',
            v: 3
          }, {
            k: 'metric-b',
            v: 2
          }, {
            k: 'metric-c',
            v: 9
          }]
        });

        var $actual = pie.$('.arc path');
        var $expected = $pie.find('.arc path');
        assert.equal($actual.eq(0).attr('d'), $expected.eq(0).attr('d'));
        assert.equal($actual.eq(1).attr('d'), $expected.eq(1).attr('d'));
        assert.equal($actual.eq(2).attr('d'), $expected.eq(2).attr('d'));
      });
    });
  });
});
<|endoftext|>


<commit_before>import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import Application from '../Application';

chai.use(chaiAsPromised);
const expect = chai.expect;

const A_VIEW = 'Contact/View';
const A_VIEW_CMD = 'doSomeStuff';
const A_VIEW_PARAM_STRING = '&param=XXX';
const A_SESSKEY = 'ABCDEF';
const A_VIEW_CMD_URL = 'index.php?k=ABCDEF&view=Contact%2FView&cmd=doSomeStuff&param=XXX';

describe('Application', () => {

	let router = {};
	let browserDetect = {};

	let app;

	beforeEach(() => {
		app = new Application(router, {
			browserDetect
		});

		app.SESSION_KEY = A_SESSKEY;
	});

	describe('getViewUrl', () => {

		it('should return the view url', () => {
			let url = app.getViewUrl(A_VIEW, A_VIEW_CMD, A_VIEW_PARAM_STRING);
			expect(url).to.equal(A_VIEW_CMD_URL);
		});
	});

});<commit_msg>Added unit tests<commit_after>import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import Application from '../Application';

chai.use(chaiAsPromised);
const expect = chai.expect;

const A_VIEW = 'Contact/View';
const A_VIEW_CMD = 'doSomeStuff';
const A_VIEW_PARAM_STRING = '&param=XXX';
const A_SESSKEY = 'ABCDEF';
const A_VIEW_CMD_URL = 'index.php?k=ABCDEF&view=Contact%2FView&cmd=doSomeStuff&param=XXX';

const NUMBER_TO_FORMAT = 3333333.3333;

describe('Application', () => {

	let router = {};
	let browserDetect = {};

	let app;

	beforeEach(() => {
		app = new Application(router, {
			browserDetect
		});

		app.SESSION_KEY = A_SESSKEY;
		app.locale = 'en-US';
	});

	describe('getViewUrl', () => {

		it('should return the view url', () => {
			let url = app.getViewUrl(A_VIEW, A_VIEW_CMD, A_VIEW_PARAM_STRING);
			expect(url).to.equal(A_VIEW_CMD_URL);
		});
	});

	describe('formatNumber', () => {

		it('should return number with 2 decimals as default', () => {
			let number = app.formatNumber(NUMBER_TO_FORMAT);

			expect(number).to.equal('3,333,333.33');
		});

		it('should return number with no decimals', () => {
			let number = app.formatNumber(
				NUMBER_TO_FORMAT,
				{minimumFractionDigits: 0, maximumFractionDigits: 0}
			);

			expect(number).to.equal('3,333,333');
		});

		it('should return number with 3 decimals', () => {
			let number = app.formatNumber(
				NUMBER_TO_FORMAT,
				{minimumFractionDigits: 0, maximumFractionDigits: 3}
			);

			expect(number).to.equal('3,333,333.333');
		});
	});

	describe('normalizeNumberFormatOptions', () => {

		it('should return default values', () => {
			let options = app.normalizeNumberFormatOptions({});

			expect(options).to.deep.equal({
				minimumFractionDigits: 0,
				maximumFractionDigits: 2
			});
		});

		it('should map precision option to maximumFractionDigits', () => {
			let options = app.normalizeNumberFormatOptions({precision: 5});

			expect(options).to.deep.equal({
				precision: 5,
				minimumFractionDigits: 0,
				maximumFractionDigits: 5
			});
		});

		it('should map maximumFractionDigits to minimumFractionDigits if facultative_decimals is false', () => {
			let options = app.normalizeNumberFormatOptions(
				{facultative_decimals: false}
			);

			expect(options).to.deep.equal({
				facultative_decimals: false,
				minimumFractionDigits: 2,
				maximumFractionDigits: 2
			});
		});

		it('should not map maximumFractionDigits to minimumFractionDigits if facultative_decimals is true', () => {
			let options = app.normalizeNumberFormatOptions(
				{facultative_decimals: true}
			);

			expect(options).to.deep.equal({
				facultative_decimals: true,
				minimumFractionDigits: 0,
				maximumFractionDigits: 2
			});
		});
	});

});
<|endoftext|>


<commit_before>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2016
 */

import { tableStrings } from '../../localization';

const PAGE_COLUMN_WIDTHS = {
  customerInvoice: [2, 4, 2, 2, 1],
  supplierInvoice: [2, 4, 2, 2, 1],
  supplierInvoices: [1.5, 2.5, 2, 1.5, 3, 1],
  customerInvoices: [1.5, 2.5, 2, 1.5, 3, 1],
  supplierRequisitions: [1.5, 2, 1, 1, 1, 1],
  supplierRequisition: [1.4, 3.5, 2, 1.5, 2, 2, 1],
  supplierRequisitionWithProgram: [1.5, 3.5, 0.5, 0.5, 2, 1.5, 2, 2, 1],
  stocktakes: [6, 2, 2, 1],
  stocktakeManager: [2, 6, 1],
  stocktakeEditor: [1, 2.8, 1.2, 1.2, 1, 0.8],
  stocktakeEditorWithReasons: [1, 2.8, 1.2, 1.2, 1, 1, 0.8],
  customerRequisitions: [1.4, 3.5, 2, 1.5, 2, 2, 1],
};

const PAGE_COLUMNS = {
  customerInvoice: ['itemCode', 'itemName', 'availableQuantity', 'totalQuantity', 'remove'],
  customerInvoices: ['serialNumber', 'customer', 'status', 'entryDate', 'comment', 'remove'],
  supplierInvoice: ['itemCode', 'itemName', 'totalQuantity', 'expiryDate', 'remove'],
  supplierInvoices: ['serialNumber', 'supplier', 'status', 'entryDate', 'comment', 'remove'],
  supplierRequisitions: [
    'serialNumber',
    'supplier',
    'numberOfItems',
    'entryDate',
    'status',
    'remove',
  ],
  supplierRequisition: [
    'itemCode',
    'itemName',
    'ourStockOnHand',
    'monthlyUsage',
    'suggestedQuantity',
    'requiredQuantity',
    'remove',
  ],
  supplierRequisitionWithProgram: [
    'itemCode',
    'itemName',
    'unit',
    'price',
    'ourStockOnHand',
    'monthlyUsage',
    'suggestedQuantity',
    'requiredQuantity',
    'remove',
  ],
  stocktakes: ['name', 'createdDate', 'status', 'remove'],
  stocktakeManager: ['code', 'name', 'selected'],
  stocktakeEditor: [
    'itemCode',
    'itemName',
    'snapshotTotalQuantity',
    'countedTotalQuantity',
    'difference',
    'batches',
  ],
  stocktakeEditorWithReasons: [
    'itemCode',
    'itemName',
    'snapshotTotalQuantity',
    'countedTotalQuantity',
    'difference',
    'reason',
    'batches',
  ],
  customerRequisitions: [
    'serialNumber',
    'supplier',
    'numberOfItems',
    'entryDate',
    'status',
    'remove',
  ],
};

const COLUMNS = () => ({
  // CODE COLUMNS
  serialNumber: {
    type: 'string',
    key: 'serialNumber',
    title: tableStrings.invoice_number,
    sortable: true,
    editable: false,
  },
  itemCode: {
    type: 'string',
    key: 'itemCode',
    title: tableStrings.item_code,
    sortable: true,
    editable: false,
  },
  code: {
    type: 'string',
    key: 'code',
    title: tableStrings.code,
    alignText: 'left',
    sortable: true,
    editable: false,
  },

  // STRING COLUMNS

  itemName: {
    type: 'string',
    key: 'itemName',
    title: tableStrings.item_name,
    sortable: true,
    editable: false,
  },
  name: {
    type: 'string',
    key: 'name',
    title: tableStrings.name,
    alignText: 'left',
    sortable: true,
    editable: false,
  },
  supplier: {
    type: 'string',
    key: 'otherPartyName',
    title: tableStrings.supplier,
    sortable: true,
    editable: false,
  },
  customer: {
    type: 'string',
    key: 'otherPartyName',
    title: tableStrings.customer,
    sortable: true,
    editable: false,
  },
  comment: {
    type: 'string',
    key: 'comment',
    title: tableStrings.comment,
    lines: 2,
    editable: false,
  },
  unit: {
    type: 'string',
    key: 'unit',
    title: tableStrings.unit,
    alignText: 'center',
    sortable: false,
    editable: false,
  },
  status: {
    type: 'string',
    key: 'status',
    title: tableStrings.status,
    sortable: true,
    editable: false,
  },

  // EDITABLE STRING COLUMNS

  batchName: {
    type: 'editableString',
    key: 'batch',
    title: tableStrings.batch_name,
    alignText: 'center',
  },

  // NUMERIC COLUMNS

  availableQuantity: {
    type: 'numeric',
    key: 'availableQuantity',
    title: tableStrings.available_stock,
    alignText: 'right',
    sortable: true,
    editable: false,
  },
  numberOfItems: {
    type: 'numeric',
    key: 'numberOfItems',
    title: tableStrings.items,
    alignText: 'right',
    sortable: true,
    editable: false,
  },
  ourStockOnHand: {
    type: 'numeric',
    key: 'ourStockOnHand',
    title: tableStrings.current_stock,
    alignText: 'right',
    sortable: true,
    editable: false,
  },
  suggestedQuantity: {
    type: 'numeric',
    key: 'suggestedQuantity',
    title: tableStrings.suggested_quantity,
    alignText: 'right',
    sortable: true,
    editable: false,
  },
  difference: {
    type: 'numeric',
    key: 'difference',
    title: tableStrings.difference,
    alignText: 'right',
    sortable: true,
    editable: false,
  },
  snapshotTotalQuantity: {
    type: 'numeric',
    key: 'snapshotTotalQuantity',
    title: tableStrings.snapshot_quantity,
    alignText: 'right',
    sortable: true,
    editable: false,
  },
  theirStockOnHand: {
    type: 'numeric',
    key: 'stockOnHand',
    title: tableStrings.their_stock,
    alignText: 'right',
    sortable: true,
    editable: false,
  },
  price: {
    type: 'numeric',
    key: 'price',
    title: tableStrings.price,
    alignText: 'center',
    editable: false,
    sortable: true,
  },
  monthlyUsage: {
    type: 'numeric',
    key: 'monthlyUsage',
    title: tableStrings.monthly_usage,
    alignText: 'right',
    sortable: true,
    editable: false,
  },

  // EDITABLE NUMERIC COLUMNS

  requiredQuantity: {
    type: 'editableNumeric',
    key: 'requiredQuantity',
    title: tableStrings.required_quantity,
    alignText: 'right',
    sortable: true,
    editable: true,
  },
  countedTotalQuantity: {
    type: 'editableNumeric',
    key: 'countedTotalQuantity',
    title: tableStrings.actual_quantity,
    alignText: 'right',
    sortable: true,
    editable: true,
  },
  totalQuantity: {
    type: 'editableNumeric',
    key: 'totalQuantity',
    title: tableStrings.quantity,
    alignText: 'right',
    sortable: true,
    editable: true,
  },

  // DATE COLUMNS

  createdDate: {
    type: 'date',
    key: 'createdDate',
    title: tableStrings.created_date,
    alignText: 'left',
    sortable: true,
    editable: false,
  },
  entryDate: {
    type: 'date',
    key: 'entryDate',
    title: tableStrings.entered_date,
    sortable: true,
    editable: false,
  },

  // EDITABLE DATE COLUMNS
  expiryDate: {
    type: 'editableDate',
    key: 'expiryDate',
    title: tableStrings.batch_expiry,
    alignText: 'center',
    sortable: false,
    editable: true,
  },

  // CHECKABLE COLUMNS
  remove: {
    type: 'checkable',
    key: 'remove',
    title: tableStrings.remove,
    alignText: 'center',
    sortable: false,
    editable: false,
  },

  selected: {
    type: 'checkable',
    key: 'selected',
    title: tableStrings.selected,
    alignText: 'center',
    sortable: false,
    editable: false,
  },

  // MISC COLUMNS

  batches: {
    type: 'icon',
    key: 'batch',
    title: tableStrings.batches,
    sortable: false,
    alignText: 'center',
    editable: false,
  },
  reason: {
    type: 'dropDown',
    key: 'mostUsedReasonTitle',
    title: tableStrings.reason,
    alignText: 'center',
    sortable: false,
    editable: false,
  },
});

const getColumns = page => {
  const widths = PAGE_COLUMN_WIDTHS[page];
  const columnKeys = PAGE_COLUMNS[page];

  if (!columnKeys) return [];
  if (!(columnKeys.length === widths.length)) return [];
  const columns = COLUMNS();
  return columnKeys.map((columnKey, i) => ({ ...columns[columnKey], width: widths[i] }));
};

export default getColumns;
<commit_msg>Add correct column widths for CustomerRequisitionsPage<commit_after>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2016
 */

import { tableStrings } from '../../localization';

const PAGE_COLUMN_WIDTHS = {
  customerInvoice: [2, 4, 2, 2, 1],
  supplierInvoice: [2, 4, 2, 2, 1],
  supplierInvoices: [1.5, 2.5, 2, 1.5, 3, 1],
  customerInvoices: [1.5, 2.5, 2, 1.5, 3, 1],
  supplierRequisitions: [1.5, 2, 1, 1, 1, 1],
  supplierRequisition: [1.4, 3.5, 2, 1.5, 2, 2, 1],
  supplierRequisitionWithProgram: [1.5, 3.5, 0.5, 0.5, 2, 1.5, 2, 2, 1],
  stocktakes: [6, 2, 2, 1],
  stocktakeManager: [2, 6, 1],
  stocktakeEditor: [1, 2.8, 1.2, 1.2, 1, 0.8],
  stocktakeEditorWithReasons: [1, 2.8, 1.2, 1.2, 1, 1, 0.8],
  customerRequisitions: [1.5, 2, 1, 1, 1, 1],
};

const PAGE_COLUMNS = {
  customerInvoice: ['itemCode', 'itemName', 'availableQuantity', 'totalQuantity', 'remove'],
  customerInvoices: ['serialNumber', 'customer', 'status', 'entryDate', 'comment', 'remove'],
  supplierInvoice: ['itemCode', 'itemName', 'totalQuantity', 'expiryDate', 'remove'],
  supplierInvoices: ['serialNumber', 'supplier', 'status', 'entryDate', 'comment', 'remove'],
  supplierRequisitions: [
    'serialNumber',
    'supplier',
    'numberOfItems',
    'entryDate',
    'status',
    'remove',
  ],
  supplierRequisition: [
    'itemCode',
    'itemName',
    'ourStockOnHand',
    'monthlyUsage',
    'suggestedQuantity',
    'requiredQuantity',
    'remove',
  ],
  supplierRequisitionWithProgram: [
    'itemCode',
    'itemName',
    'unit',
    'price',
    'ourStockOnHand',
    'monthlyUsage',
    'suggestedQuantity',
    'requiredQuantity',
    'remove',
  ],
  stocktakes: ['name', 'createdDate', 'status', 'remove'],
  stocktakeManager: ['code', 'name', 'selected'],
  stocktakeEditor: [
    'itemCode',
    'itemName',
    'snapshotTotalQuantity',
    'countedTotalQuantity',
    'difference',
    'batches',
  ],
  stocktakeEditorWithReasons: [
    'itemCode',
    'itemName',
    'snapshotTotalQuantity',
    'countedTotalQuantity',
    'difference',
    'reason',
    'batches',
  ],
  customerRequisitions: [
    'serialNumber',
    'supplier',
    'numberOfItems',
    'entryDate',
    'status',
    'remove',
  ],
};

const COLUMNS = () => ({
  // CODE COLUMNS
  serialNumber: {
    type: 'string',
    key: 'serialNumber',
    title: tableStrings.invoice_number,
    sortable: true,
    editable: false,
  },
  itemCode: {
    type: 'string',
    key: 'itemCode',
    title: tableStrings.item_code,
    sortable: true,
    editable: false,
  },
  code: {
    type: 'string',
    key: 'code',
    title: tableStrings.code,
    alignText: 'left',
    sortable: true,
    editable: false,
  },

  // STRING COLUMNS

  itemName: {
    type: 'string',
    key: 'itemName',
    title: tableStrings.item_name,
    sortable: true,
    editable: false,
  },
  name: {
    type: 'string',
    key: 'name',
    title: tableStrings.name,
    alignText: 'left',
    sortable: true,
    editable: false,
  },
  supplier: {
    type: 'string',
    key: 'otherPartyName',
    title: tableStrings.supplier,
    sortable: true,
    editable: false,
  },
  customer: {
    type: 'string',
    key: 'otherPartyName',
    title: tableStrings.customer,
    sortable: true,
    editable: false,
  },
  comment: {
    type: 'string',
    key: 'comment',
    title: tableStrings.comment,
    lines: 2,
    editable: false,
  },
  unit: {
    type: 'string',
    key: 'unit',
    title: tableStrings.unit,
    alignText: 'center',
    sortable: false,
    editable: false,
  },
  status: {
    type: 'string',
    key: 'status',
    title: tableStrings.status,
    sortable: true,
    editable: false,
  },

  // EDITABLE STRING COLUMNS

  batchName: {
    type: 'editableString',
    key: 'batch',
    title: tableStrings.batch_name,
    alignText: 'center',
  },

  // NUMERIC COLUMNS

  availableQuantity: {
    type: 'numeric',
    key: 'availableQuantity',
    title: tableStrings.available_stock,
    alignText: 'right',
    sortable: true,
    editable: false,
  },
  numberOfItems: {
    type: 'numeric',
    key: 'numberOfItems',
    title: tableStrings.items,
    alignText: 'right',
    sortable: true,
    editable: false,
  },
  ourStockOnHand: {
    type: 'numeric',
    key: 'ourStockOnHand',
    title: tableStrings.current_stock,
    alignText: 'right',
    sortable: true,
    editable: false,
  },
  suggestedQuantity: {
    type: 'numeric',
    key: 'suggestedQuantity',
    title: tableStrings.suggested_quantity,
    alignText: 'right',
    sortable: true,
    editable: false,
  },
  difference: {
    type: 'numeric',
    key: 'difference',
    title: tableStrings.difference,
    alignText: 'right',
    sortable: true,
    editable: false,
  },
  snapshotTotalQuantity: {
    type: 'numeric',
    key: 'snapshotTotalQuantity',
    title: tableStrings.snapshot_quantity,
    alignText: 'right',
    sortable: true,
    editable: false,
  },
  theirStockOnHand: {
    type: 'numeric',
    key: 'stockOnHand',
    title: tableStrings.their_stock,
    alignText: 'right',
    sortable: true,
    editable: false,
  },
  price: {
    type: 'numeric',
    key: 'price',
    title: tableStrings.price,
    alignText: 'center',
    editable: false,
    sortable: true,
  },
  monthlyUsage: {
    type: 'numeric',
    key: 'monthlyUsage',
    title: tableStrings.monthly_usage,
    alignText: 'right',
    sortable: true,
    editable: false,
  },

  // EDITABLE NUMERIC COLUMNS

  requiredQuantity: {
    type: 'editableNumeric',
    key: 'requiredQuantity',
    title: tableStrings.required_quantity,
    alignText: 'right',
    sortable: true,
    editable: true,
  },
  countedTotalQuantity: {
    type: 'editableNumeric',
    key: 'countedTotalQuantity',
    title: tableStrings.actual_quantity,
    alignText: 'right',
    sortable: true,
    editable: true,
  },
  totalQuantity: {
    type: 'editableNumeric',
    key: 'totalQuantity',
    title: tableStrings.quantity,
    alignText: 'right',
    sortable: true,
    editable: true,
  },

  // DATE COLUMNS

  createdDate: {
    type: 'date',
    key: 'createdDate',
    title: tableStrings.created_date,
    alignText: 'left',
    sortable: true,
    editable: false,
  },
  entryDate: {
    type: 'date',
    key: 'entryDate',
    title: tableStrings.entered_date,
    sortable: true,
    editable: false,
  },

  // EDITABLE DATE COLUMNS
  expiryDate: {
    type: 'editableDate',
    key: 'expiryDate',
    title: tableStrings.batch_expiry,
    alignText: 'center',
    sortable: false,
    editable: true,
  },

  // CHECKABLE COLUMNS
  remove: {
    type: 'checkable',
    key: 'remove',
    title: tableStrings.remove,
    alignText: 'center',
    sortable: false,
    editable: false,
  },

  selected: {
    type: 'checkable',
    key: 'selected',
    title: tableStrings.selected,
    alignText: 'center',
    sortable: false,
    editable: false,
  },

  // MISC COLUMNS

  batches: {
    type: 'icon',
    key: 'batch',
    title: tableStrings.batches,
    sortable: false,
    alignText: 'center',
    editable: false,
  },
  reason: {
    type: 'dropDown',
    key: 'mostUsedReasonTitle',
    title: tableStrings.reason,
    alignText: 'center',
    sortable: false,
    editable: false,
  },
});

const getColumns = page => {
  const widths = PAGE_COLUMN_WIDTHS[page];
  const columnKeys = PAGE_COLUMNS[page];

  if (!columnKeys) return [];
  if (!(columnKeys.length === widths.length)) return [];
  const columns = COLUMNS();
  return columnKeys.map((columnKey, i) => ({ ...columns[columnKey], width: widths[i] }));
};

export default getColumns;
<|endoftext|>


<commit_before>import Ember from 'ember';
import { sanitize, sanitizeElement } from '../utils/sanitize';

function loadConfig(container, name) {
  if (!name) { return; }
  return container.lookup("sanitizer:"+name);
}

export default Ember.Mixin.create({
  sanitizeElement: function(selector, configName) {
    var element = this.$(selector)[0];
    return sanitizeElement(element, loadConfig(configName));
  },

  sanitizeHTML: function(html, configName) {
    return sanitize(html, loadConfig(configName));
  }
});<commit_msg>pass container to loadConfig<commit_after>import Ember from 'ember';
import { sanitize, sanitizeElement } from '../utils/sanitize';

function loadConfig(container, name) {
  if (!name) { return; }
  return container.lookup("sanitizer:"+name);
}

export default Ember.Mixin.create({
  sanitizeElement: function(selector, configName) {
    var element = this.$(selector)[0];
    return sanitizeElement(element, loadConfig(this.container, configName));
  },

  sanitizeHTML: function(html, configName) {
    return sanitize(html, loadConfig(this.container, configName));
  }
});<|endoftext|>


<commit_before>/*
 *  Project: SKT HTML5 Framework
 *  CodeName : CornerStone
 *  FileName : featured-datatable.js
 *  Description: 데이터테이블을 반응형 웹에 맞는 화면을 보여주고, 코너스톤 UI에 맞게 기본적으로 설정하며, DATA-API를 사용해서 스크립트 사용없이 마크업
 *  속성만으로 작동되도록 구현함.
 *  Author: 김우섭
 *  License :
 */

(function (root, doc, factory) {
	// 코너스톤 MVC 프레임워크인 경우 이 위젯을 모듈화 한다.
	// if (typeof Cornerstone === "object" && typeof define === "function" && define.amd) {
	if (typeof define === "function" && define.amd) {
		// AMD Hybrid 포맷
		define([ "backbone", "underscore", "jquery", "datatable" ], function (Backbone, _, $) {
			factory($, root, doc);
			return Backbone.View.extend({
				initialize: function () {
					this.listenTo(model, "change", this.render);
					this.listenTo(model, "reset", this.render);
				},

				render: function () {
					if (!this.dataTable) {
						this.dataTable = this.$el.featuredDataTable(this.options);
					}

					this.options = $.extend({}, this.options, this.toJSON());
					this.dataTable.fnClearTable();
					this.dataTable.fnAddData(view.options.aaData);
					this.dataTable.fnDraw();
					return this;
				}
			});
		});
	} else {
		// Browser globals
		factory(root.jQuery, root, doc);
	}
}(this, document, function ($, window, document) {
	var pluginName = "featuredDataTable";

	// 데이터테이블 플러그인 랩핑 및 기본값 설정
	$.fn[pluginName] = function (options) {
		var defaultOptions = {
			"bProcessing": false,
			sPaginationType: "bootstrap",
			sDom: "<'row'<'col col-lg-8 col-12'l><'col col-lg-4 col-12'f>r>t<'row'<'col col-12'i><'col col-12'p>>",
			oLanguage: {sLengthMenu: "_MENU_ 페이지별 레코드수", sInfo: "총 레코드 수:_TOTAL_ (시작 번호:_START_, 끝 번호:_END_)"}
		};

		options = $.extend(true, defaultOptions, options);
		var dt = $(this).data('featuredDataTable', $(this).dataTable(options));

		dt.off('click', 'tr').on('click', 'tr', function () {
			var rowData = $(this).closest('table').data('featuredDataTable').fnGetData(this);
			$(this).trigger('itemClick.cs.datatables', { 'data': rowData });
		});
		return dt.data('featuredDataTable');
	};

	/* 기본 클래스명 정의 */
	$.extend($.fn.dataTableExt.oStdClasses, {
		"sSortAsc": "header headerSortDown",
		"sSortDesc": "header headerSortUp",
		"sSortable": "header"
	});

	/* API method to get paging information */
	$.fn.dataTableExt.oApi.fnPagingInfo = function (oSettings) {
		return {
			"iStart": oSettings._iDisplayStart,
			"iEnd": oSettings.fnDisplayEnd(),
			"iLength": oSettings._iDisplayLength,
			"iTotal": oSettings.fnRecordsTotal(),
			"iFilteredTotal": oSettings.fnRecordsDisplay(),
			"iPage": Math.ceil(oSettings._iDisplayStart / oSettings._iDisplayLength),
			"iTotalPages": Math.ceil(oSettings.fnRecordsDisplay() / oSettings._iDisplayLength)
		};
	};

	/* 부트스트랩 페이지네이션 스타일링 기능 확장 */
	$.extend($.fn.dataTableExt.oPagination, {
		"bootstrap": {
			"fnInit": function (oSettings, nPaging, fnDraw) {
				var fnClickHandler = function (e) {
					e.preventDefault();
					if (oSettings.oApi._fnPageChange(oSettings, e.data.action)) {
						fnDraw(oSettings);
					}
				};

				$(nPaging).append(
					'
' +
						'

' +
						'

' +
						'
'
				);
				var els = $('a', nPaging);
				$(els[0]).bind('click.DT', { action: "previous" }, fnClickHandler);
				$(els[1]).bind('click.DT', { action: "next" }, fnClickHandler);
			},

			"fnUpdate": function (oSettings, fnDraw) {
				var iListLength = 4;
				var oPaging = oSettings.oInstance.fnPagingInfo();
				var an = oSettings.aanFeatures.p;
				var i, j, sClass, iStart, iEnd, iHalf = Math.floor(iListLength / 2);

				if (oPaging.iTotalPages < iListLength) {
					iStart = 1;
					iEnd = oPaging.iTotalPages;
				}
				else if (oPaging.iPage <= iHalf) {
					iStart = 1;
					iEnd = iListLength;
				} else if (oPaging.iPage >= (oPaging.iTotalPages - iHalf)) {
					iStart = oPaging.iTotalPages - iListLength + 1;
					iEnd = oPaging.iTotalPages;
				} else {
					iStart = oPaging.iPage - iHalf + 1;
					iEnd = iStart + iListLength - 1;
				}

				for (i = 0, iLen = an.length; i < iLen; i++) {
					// Remove the middle elements
					$('li:gt(0)', an[i]).filter(':not(:last)').remove();

					// Add the new list items and their event handlers
					for (j = iStart; j <= iEnd; j++) {
						sClass = (j == oPaging.iPage + 1) ? 'class="active"' : '';
						$('
' + j + '
')
							.insertBefore($('li:last', an[i])[0])
							.bind('click', function (e) {
								e.preventDefault();
								oSettings._iDisplayStart = (parseInt($('a', this).text(), 10) - 1) * oPaging.iLength;
								fnDraw(oSettings);
							});
					}

					// Add / remove disabled classes from the static elements
					if (oPaging.iPage === 0) {
						$('li:first', an[i]).addClass('disabled');
					} else {
						$('li:first', an[i]).removeClass('disabled');
					}

					if (oPaging.iPage === oPaging.iTotalPages - 1 || oPaging.iTotalPages === 0) {
						$('li:last', an[i]).addClass('disabled');
					} else {
						$('li:last', an[i]).removeClass('disabled');
					}
				}

				var $inputFilter = $(".dataTables_filter input");
				$inputFilter.hasClass("form-control") || $inputFilter.addClass("form-control");
			}
		}
	});

	$(function () {
		/**
		 * DATA API (HTML5 Data Attribute)
		 */
		$("[data-featured=datatable]").each(function () {
			$(this)[pluginName]({
				"sAjaxSource": $(this).data("datatableBind")
			});
		});

		$(".dataTables_paginate a").on("click", function (e) {
			e.preventDefault();
		});
	});
}));<commit_msg>Widget Datatable BackboneView 글로벌에 추가하는 기능 구현 및 코드 리팩토링<commit_after>/*
 *  Project: SKT HTML5 Framework
 *  CodeName : CornerStone
 *  FileName : featured-datatable.js
 *  Description: 데이터테이블을 반응형 웹에 맞는 화면을 보여주고, 코너스톤 UI에 맞게 기본적으로 설정하며, DATA-API를 사용해서 스크립트 사용없이 마크업
 *  속성만으로 작동되도록 구현함.
 *  Author: 김우섭
 *  License :
 */
;(function (root, factory) {

    // Require.js가 있을 경우
    if (typeof define === 'function' && define.amd)
        define([ "jquery", "underscore", "backbone", "datatable" ], factory);
    else
        root.Datatable = factory(root.$, root._, root.Backbone);

}(window, function ($, _, Backbone) {
    var pluginName = "featuredDataTable";

    // 데이터테이블 플러그인 랩핑 및 기본값 설정
    $.fn[pluginName] = function (options) {
        var defaultOptions = {
            "bProcessing": false,
            sPaginationType: "bootstrap",
            sDom: "<'row'<'col col-lg-8 col-12'l><'col col-lg-4 col-12'f>r>t<'row'<'col col-12'i><'col col-12'p>>",
            oLanguage: {sLengthMenu: "_MENU_ 페이지별 레코드수", sInfo: "총 레코드 수:_TOTAL_ (시작 번호:_START_, 끝 번호:_END_)"}
        };

        options = $.extend(true, defaultOptions, options);
        var dt = $(this).data('featuredDataTable', $(this).dataTable(options));

        dt.off('click', 'tr').on('click', 'tr', function () {
            var rowData = $(this).closest('table').data('featuredDataTable').fnGetData(this);
            $(this).trigger('itemClick.cs.datatables', { 'data': rowData });
        });
        return dt.data('featuredDataTable');
    };

    /* 기본 클래스명 정의 */
    $.extend($.fn.dataTableExt.oStdClasses, {
        "sSortAsc": "header headerSortDown",
        "sSortDesc": "header headerSortUp",
        "sSortable": "header"
    });

    /* API method to get paging information */
    $.fn.dataTableExt.oApi.fnPagingInfo = function (oSettings) {
        return {
            "iStart": oSettings._iDisplayStart,
            "iEnd": oSettings.fnDisplayEnd(),
            "iLength": oSettings._iDisplayLength,
            "iTotal": oSettings.fnRecordsTotal(),
            "iFilteredTotal": oSettings.fnRecordsDisplay(),
            "iPage": Math.ceil(oSettings._iDisplayStart / oSettings._iDisplayLength),
            "iTotalPages": Math.ceil(oSettings.fnRecordsDisplay() / oSettings._iDisplayLength)
        };
    };

    /* 부트스트랩 페이지네이션 스타일링 기능 확장 */
    $.extend($.fn.dataTableExt.oPagination, {
        "bootstrap": {
            "fnInit": function (oSettings, nPaging, fnDraw) {
                var fnClickHandler = function (e) {
                    e.preventDefault();
                    if (oSettings.oApi._fnPageChange(oSettings, e.data.action)) {
                        fnDraw(oSettings);
                    }
                };

                $(nPaging).append(
                '
' +
                '

' +
                '

' +
                '
'
                );
                var els = $('a', nPaging);
                $(els[0]).bind('click.DT', { action: "previous" }, fnClickHandler);
                $(els[1]).bind('click.DT', { action: "next" }, fnClickHandler);
            },

            "fnUpdate": function (oSettings, fnDraw) {
                var iListLength = 4;
                var oPaging = oSettings.oInstance.fnPagingInfo();
                var an = oSettings.aanFeatures.p;
                var i, j, sClass, iStart, iEnd, iHalf = Math.floor(iListLength / 2);

                if (oPaging.iTotalPages < iListLength) {
                    iStart = 1;
                    iEnd = oPaging.iTotalPages;
                }
                else if (oPaging.iPage <= iHalf) {
                    iStart = 1;
                    iEnd = iListLength;
                } else if (oPaging.iPage >= (oPaging.iTotalPages - iHalf)) {
                    iStart = oPaging.iTotalPages - iListLength + 1;
                    iEnd = oPaging.iTotalPages;
                } else {
                    iStart = oPaging.iPage - iHalf + 1;
                    iEnd = iStart + iListLength - 1;
                }

                for (i = 0, iLen = an.length; i < iLen; i++) {
                    // Remove the middle elements
                    $('li:gt(0)', an[i]).filter(':not(:last)').remove();

                    // Add the new list items and their event handlers
                    for (j = iStart; j <= iEnd; j++) {
                        sClass = (j == oPaging.iPage + 1) ? 'class="active"' : '';
                        $('
' + j + '
')
                        .insertBefore($('li:last', an[i])[0])
                        .bind('click', function (e) {
                            e.preventDefault();
                            oSettings._iDisplayStart = (parseInt($('a', this).text(), 10) - 1) * oPaging.iLength;
                            fnDraw(oSettings);
                        });
                    }

                    // Add / remove disabled classes from the static elements
                    if (oPaging.iPage === 0) {
                        $('li:first', an[i]).addClass('disabled');
                    } else {
                        $('li:first', an[i]).removeClass('disabled');
                    }

                    if (oPaging.iPage === oPaging.iTotalPages - 1 || oPaging.iTotalPages === 0) {
                        $('li:last', an[i]).addClass('disabled');
                    } else {
                        $('li:last', an[i]).removeClass('disabled');
                    }
                }

                var $inputFilter = $(".dataTables_filter input");
                $inputFilter.hasClass("form-control") || $inputFilter.addClass("form-control");
            }
        }
    });

    $(function () {
        /**
         * DATA API (HTML5 Data Attribute)
         */
        $("[data-featured=datatable]").each(function () {
            $(this)[pluginName]({
                "sAjaxSource": $(this).data("datatableBind")
            });
        });

        $(".dataTables_paginate a").on("click", function (e) {
            e.preventDefault();
        });
    });

    return Backbone && Backbone.View.extend({
        initialize: function () {
            this.listenTo(this.model, "change", this.render);
            this.listenTo(this.model, "reset", this.render);
        },

        render: function () {
            if (!this.dataTable) {
                this.dataTable = this.$el.featuredDataTable(this.options);
            }

            this.options = $.extend({}, this.options, this.toJSON());
            this.dataTable.fnClearTable();
            this.dataTable.fnAddData(view.options.aaData);
            this.dataTable.fnDraw();
            return this;
        }
    });
}));<|endoftext|>


<commit_before>(function ( Backbone, _ ) {

  'use strict';

  var bbExtend = Backbone.Model.extend;

  Backbone.Model.extend = function ( properties, classProperties ) {
    var prop, action, propSpec, deps;

    var computedProps = properties.properties;
    delete properties.properties;

    var Class = bbExtend.call( this, properties, classProperties );

    if ( computedProps ) {
      for ( prop in computedProps ) {
        if ( computedProps.hasOwnProperty( prop ) ) {
          propSpec = computedProps[ prop ];
          if ( propSpec instanceof Function ) {
            action = propSpec;
            deps = [];
          } else {
            action = propSpec.action;
            deps = propSpec.deps || [];
          }
          computedProps[ prop ] = {
            action: action,
            deps: deps
          };
        }
      }
    }

    var get = Class.prototype.get;
    Class.prototype.get = function ( attr ) {
      if ( computedProps[ attr ] ) {
        return computedProps[ attr ].action.call( this );
      }
      return get.apply( this, arguments );
    };

    var set = Class.prototype.set;
    Class.prototype.set = function ( attr, value ) {
      if ( computedProps[ attr ] ) {
        computedProps[ attr ].action.call( this, value );
        this.trigger( 'change:' + attr, this, attr );
        return this;
      }
      return set.apply( this, arguments );
    };

    return Class;
  };

}( this.Backbone, this.underscore ));
<commit_msg>Override default constructor<commit_after>(function ( Backbone, _ ) {

  'use strict';

  var bbExtend = Backbone.Model.extend;

  Backbone.Model.extend = function ( properties, classProperties ) {
    var prop, action, propSpec, deps;
    var parent = this;

    properties = properties || {};
    var computedProps = properties.properties;
    delete properties.properties;

    // Override Boostrap's default constructor
    properties.constructor = function () {
      return parent.apply( this, arguments );
    };

    // Extend our class from Boostrap.Model
    var Class = bbExtend.call( this, properties, classProperties );

    // Setup computed properties
    if ( computedProps ) {
      for ( prop in computedProps ) {
        if ( computedProps.hasOwnProperty( prop ) ) {
          propSpec = computedProps[ prop ];
          if ( propSpec instanceof Function ) {
            action = propSpec;
            deps = [];
          } else {
            action = propSpec.action;
            deps = propSpec.deps || [];
          }
          computedProps[ prop ] = {
            action: action,
            deps: deps
          };
        }
      }
    }

    // Override Bootstrap's default getter
    var get = Class.prototype.get;
    Class.prototype.get = function ( attr ) {
      if ( computedProps[ attr ] ) {
        return computedProps[ attr ].action.call( this );
      }
      return get.apply( this, arguments );
    };

    // Override Bootstrap's default setter
    var set = Class.prototype.set;
    Class.prototype.set = function ( attr, value ) {
      if ( computedProps[ attr ] ) {
        computedProps[ attr ].action.call( this, value );
        this.trigger( 'change:' + attr, this, attr );
        return this;
      }
      return set.apply( this, arguments );
    };

    return Class;
  };

}( this.Backbone, this.underscore ));
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

CLASS({
  package: 'foam.demos',
  name: 'DragonLiveCoding',
  extendsModel: 'foam.ui.View',

  requires: [
    'foam.ui.DetailView',
    'foam.graphics.Turntable',
    'foam.input.Mouse',
    'foam.demos.graphics.Dragon',
    'foam.ui.SlidePanel'
  ],

  exports: [ 'timer' ],

  properties: [
    {
      name: 'dragonModel',
      view: 'foam.ui.DetailView',
      factory: function() { return this.Dragon; }
    },
    {
      name: 'dragon',
      factory: function() { return this.Dragon.create({scaleX: 0.7, scaleY: 0.7, x: 700}); }
    },
    {
      name: 'timer',
      view: 'foam.ui.DetailView',
      factory: function() { return Timer.create(); }
    },
    {
      name: 'turntable',
      factory: function() { return this.Turntable.create(); }
    },
    {
      name: 'mouse',
      factory: function() { return this.Mouse.create(); }
    }
  ],

  methods: {
    init: function() {
      this.SUPER();
      this.data = this;
    },
    initHTML: function() {
      this.SUPER();

      this.turntable.time$ = this.timer.time$;
      this.timer.start();
      this.mouse.connect(this.dragon.$);
      this.dragon.eyes.watch(this.mouse);
    }
  },

  templates: [
    function CSS() {/*
      span[name="sourcePath"] {
        font-size: smaller;
        margin-left: 12px;
      }
      .detailView .label {
        color: #444;
        font-size: smaller;
      }
      .panel .detailView {
        width: 820px;
      }
      .main .dragon-cell canvas {
        margin-top: -150px;
      }
    */},
    function toHTML() {/*
    <foam model="foam.ui.SlidePanel" minWidth="1700" minPanelWidth="850" side="left" XXXstripWidth="16">
      <panelView>

          $$dragonModel

      </panelView>
      <mainView>

            $$timer{showActions: true}
<%= this.data.turntable %>
            <%= this.data.dragon %>

      </mainView>
     </foam>
    */}
  ]
});
<commit_msg>Changed DragonLiveCoding positioning.<commit_after>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

CLASS({
  package: 'foam.demos',
  name: 'DragonLiveCoding',
  extendsModel: 'foam.ui.View',

  requires: [
    'foam.ui.DetailView',
    'foam.graphics.Turntable',
    'foam.input.Mouse',
    'foam.demos.graphics.Dragon',
    'foam.ui.SlidePanel'
  ],

  exports: [ 'timer' ],

  properties: [
    {
      name: 'dragonModel',
      view: 'foam.ui.DetailView',
      factory: function() { return this.Dragon; }
    },
    {
      name: 'dragon',
      factory: function() { return this.Dragon.create({scaleX: 0.7, scaleY: 0.7, x: 1000}); }
    },
    {
      name: 'timer',
      view: 'foam.ui.DetailView',
      factory: function() { return Timer.create(); }
    },
    {
      name: 'turntable',
      factory: function() { return this.Turntable.create(); }
    },
    {
      name: 'mouse',
      factory: function() { return this.Mouse.create(); }
    }
  ],

  methods: {
    init: function() {
      this.SUPER();
      this.data = this;
    },
    initHTML: function() {
      this.SUPER();

      this.turntable.time$ = this.timer.time$;
      this.timer.start();
      this.mouse.connect(this.dragon.$);
      this.dragon.eyes.watch(this.mouse);
    }
  },

  templates: [
    function CSS() {/*
      span[name="sourcePath"] {
        font-size: smaller;
        margin-left: 12px;
      }
      .detailView .label {
        color: #444;
        font-size: smaller;
      }
      .panel .detailView {
        width: 820px;
      }
      .main .dragon-cell canvas {
        margin-top: -150px;
      }
    */},
    function toHTML() {/*
    <foam model="foam.ui.SlidePanel" minWidth="1700" minPanelWidth="850" side="left" XXXstripWidth="16">
      <panelView>

          $$dragonModel

      </panelView>
      <mainView>

            $$timer{showActions: true}
<%= this.data.turntable %>
            <%= this.data.dragon %>

      </mainView>
     </foam>
    */}
  ]
});
<|endoftext|>


<commit_before>'use strict';

// --- Dependencies ---
var debug      = require('debug')('arkivo:mailer');
var assert     = require('assert');

var slice      = Array.prototype.slice;
var properties = Object.defineProperties;

var config = require('arkivo/lib/config');
var common = require('arkivo/lib/common');
var extend = common.extend;

var B  = require('bluebird');
var co = B.coroutine;

var nm = require('nodemailer');

/**
 * Arkivo Mailer.
 *
 * @class Mailer
 * @constructor
 */
function Mailer(options, sync) {
  assert(sync);
  assert(sync.subscription);

  var defaults = config.has('mailer')
    ? extend({}, Mailer.defaults, config.get('mailer'))
    : Mailer.defaults;

  this.options = options || {};
  this.options.mimetypes = defaults.mimetypes.slice();

  this.sync  = sync;
}

/**
 * Mailer default configuration.
 *
 * @property defaults
 * @type Object
 * @static
 */
Mailer.defaults = {

  from: 'arkivo@zotero.org',
  subject: 'Zotero Update',

  mimetypes: [
    'application/pdf'
  ]
};

properties(Mailer.prototype, {
  /**
   * @property created
   * @type Array
   */
  created: {
    get: function get$created() {
      return this.collect(this.sync.created);
    }
  },

  /**
   * @property updated
   * @type Array
   */
  updated: {
    get: function get$updated() {
      return this.collect(this.sync.updated);
    }
  },

  transport: {
    get: function () {
      return nm.createTransport(this.options.transport);
    }
  }
});

/**
 * Send the given item.
 *
 * @method send
 * @return {Promise}
 */
Mailer.prototype.send = function (item) {
  this.debug('sending item %s...', item.key);

  return new B(function (resolve, reject) {
    this.transport.sendMail({
      to: this.options.recipient,
      from: this.options.from,
      subject: this.options.subject,
      text: this.options.text,
      html: this.options.html,
      attachments: item.attachments

    }, function (error) {
      if (error) return reject(error);
      resolve();
    });

  }.bind(this));
};

/**
 * Downloads the item's attachment.
 * @method download
 * @return {Promise}
 */
Mailer.prototype.download = function (item) {
  this.debug('downloading attachment %s...', item.key);

  return this
    .sync
    .attachment(item)
    .then(function (message) {

      if (!message.data)
        throw Error('attachment is blank');
      if (common.md5(message.data) !== item.data.md5)
        throw Error('attachment checksum mismatch');

      return message.data;
    });
};

/**
 * Collect Zotero items with attachments that can
 * be sent by mail.
 *
 * @method collect
 * @param {Array} keys The list of Zotero keys to look at.
 *
 * @return {Promise<Array>} The collected items.
 */
Mailer.prototype.collect = co(function* (keys) {
  var i, ii, item, child, data, collection = {};

  for (i = 0, ii = keys.length; i < ii; ++i) {
    try {
      item = this.expand(keys[i]);

      if (!item) {
        this.debug('cannot expand "%s": item missing', keys[i]);
        continue;
      }

      // Duplicate items are possible, because child items are
      // expanded to their parents; therefore, if a sync session
      // includes an item and one or more of its children, we
      // might process the item multiple times!
      if (collection[item.key]) continue;

      // Skip items without attachments!
      if (!item.children) continue;

      child = this.select(item.children);

      if (!child) {
        this.debug('skipping "%s": no suitable attachments found', item.key);
        continue;
      }

      data = yield this.download(child);

      collection.push(this.convert(item, child, data));

    } catch (error) {
      this.debug('failed to collect item: %s', error.message);
      debug(error.stack);

      continue;
    }
  }

  return collection;
});

/**
 * Converts items to syntax used by the mailer.
 *
 * @method convert
 * @return Object The converted item.
 */
Mailer.prototype.convert = function (item, child, data) {
  return {
    attachment: {
      filename: child.filename,
      contentType: child.contentType,
      content: data,
      encoding: 'binary'
    }
  };
};

/**
 * Selects the first suitable attachment item.
 *
 * @method select
 * @param {Array} items
 * @return {Object}
 */
Mailer.prototype.select = function (items) {
  if (!items) return undefined;
  if (!items.length) return undefined;

  var i, ii, item, next;

  for (i = 0, ii = items.length; i < ii; ++i) {
    next = items[i];

    if (next.data.itemType !== 'attachment')
      continue;

    if (next.data.linkMode !== 'imported_file')
      continue;

    if (this.options.mimetypes.indexOf(next.data.contentType) < 0)
      continue;

    if (item) {

      if (item.dateAdded < next.dateAdded) continue;

    }

    item = next;
  }

  return item;
};

/**
 * Returns the Zotero item for key; if the item has
 * a parent, returns parent item instead.
 *
 * @method expand
 * @private
 */
Mailer.prototype.expand = function (key) {
  var item = this.sync.items[key];

  if (item && item.data.parentItem)
    return this.expand(item.data.parentItem);

  return item;
};

Mailer.prototype.debug = function (message) {
  debug.apply(null, [
    '[%s] ' + message, this.sync.id
  ].concat(slice.call(arguments, 1)));

  return this;
};

// --- Exports ---
module.exports = Mailer;
<commit_msg>revert to base64 encoding<commit_after>'use strict';

// --- Dependencies ---
var debug      = require('debug')('arkivo:mailer');
var assert     = require('assert');

var slice      = Array.prototype.slice;
var properties = Object.defineProperties;

var config = require('arkivo/lib/config');
var common = require('arkivo/lib/common');
var extend = common.extend;
var md5    = common.md5;
var base64 = common.base64;

var B  = require('bluebird');
var co = B.coroutine;

var nm = require('nodemailer');

/**
 * Arkivo Mailer.
 *
 * @class Mailer
 * @constructor
 */
function Mailer(options, sync) {
  assert(sync);
  assert(sync.subscription);

  var defaults = config.has('mailer')
    ? extend({}, Mailer.defaults, config.get('mailer'))
    : Mailer.defaults;

  this.options = options || {};
  this.options.mimetypes = defaults.mimetypes.slice();

  this.sync  = sync;
}

/**
 * Mailer default configuration.
 *
 * @property defaults
 * @type Object
 * @static
 */
Mailer.defaults = {

  from: 'arkivo@zotero.org',
  subject: 'Zotero Update',

  mimetypes: [
    'application/pdf'
  ]
};

properties(Mailer.prototype, {
  /**
   * @property created
   * @type Array
   */
  created: {
    get: function get$created() {
      return this.collect(this.sync.created);
    }
  },

  /**
   * @property updated
   * @type Array
   */
  updated: {
    get: function get$updated() {
      return this.collect(this.sync.updated);
    }
  },

  transport: {
    get: function () {
      return nm.createTransport(this.options.transport);
    }
  }
});

/**
 * Send the given item.
 *
 * @method send
 * @return {Promise}
 */
Mailer.prototype.send = function (item) {
  this.debug('sending item %s...', item.key);

  return new B(function (resolve, reject) {
    this.transport.sendMail({
      to: this.options.recipient,
      from: this.options.from,
      subject: this.options.subject,
      text: this.options.text,
      html: this.options.html,
      attachments: item.attachments

    }, function (error) {
      if (error) return reject(error);
      resolve();
    });

  }.bind(this));
};

/**
 * Downloads the item's attachment.
 * @method download
 * @return {Promise}
 */
Mailer.prototype.download = function (item) {
  this.debug('downloading attachment %s...', item.key);

  return this
    .sync
    .attachment(item)
    .then(function (message) {

      if (!message.data)
        throw Error('attachment is blank');
      if (md5(message.data) !== item.data.md5)
        throw Error('attachment checksum mismatch');

      return message.data;
    });
};

/**
 * Collect Zotero items with attachments that can
 * be sent by mail.
 *
 * @method collect
 * @param {Array} keys The list of Zotero keys to look at.
 *
 * @return {Promise<Array>} The collected items.
 */
Mailer.prototype.collect = co(function* (keys) {
  var i, ii, item, child, data, collection = [];

  for (i = 0, ii = keys.length; i < ii; ++i) {
    try {
      item = this.expand(keys[i]);

      if (!item) {
        this.debug('cannot expand "%s": item missing', keys[i]);
        continue;
      }

      // Duplicate items are possible, because child items are
      // expanded to their parents; therefore, if a sync session
      // includes an item and one or more of its children, we
      // might process the item multiple times!
      if (collection[item.key]) continue;

      // Skip items without attachments!
      if (!item.children) continue;

      child = this.select(item.children);

      if (!child) {
        this.debug('skipping "%s": no suitable attachments found', item.key);
        continue;
      }

      data = yield this.download(child);

      collection.push(this.convert(item, child, data));

    } catch (error) {
      this.debug('failed to collect item: %s', error.message);
      debug(error.stack);

      continue;
    }
  }

  return collection;
});

/**
 * Converts items to syntax used by the mailer.
 *
 * @method convert
 * @return Object The converted item.
 */
Mailer.prototype.convert = function (item, child, data) {
  return {
    attachment: {
      filename: child.filename,
      contentType: child.contentType,
      content: base64(data),
      encoding: 'base64'
    }
  };
};

/**
 * Selects the first suitable attachment item.
 *
 * @method select
 * @param {Array} items
 * @return {Object}
 */
Mailer.prototype.select = function (items) {
  if (!items) return undefined;
  if (!items.length) return undefined;

  var i, ii, item, next;

  for (i = 0, ii = items.length; i < ii; ++i) {
    next = items[i];

    if (next.data.itemType !== 'attachment')
      continue;

    if (next.data.linkMode !== 'imported_file')
      continue;

    if (this.options.mimetypes.indexOf(next.data.contentType) < 0)
      continue;

    if (item) {

      if (item.dateAdded < next.dateAdded) continue;

    }

    item = next;
  }

  return item;
};

/**
 * Returns the Zotero item for key; if the item has
 * a parent, returns parent item instead.
 *
 * @method expand
 * @private
 */
Mailer.prototype.expand = function (key) {
  var item = this.sync.items[key];

  if (item && item.data.parentItem)
    return this.expand(item.data.parentItem);

  return item;
};

Mailer.prototype.debug = function (message) {
  debug.apply(null, [
    '[%s] ' + message, this.sync.id
  ].concat(slice.call(arguments, 1)));

  return this;
};

// --- Exports ---
module.exports = Mailer;
<|endoftext|>


<commit_before>
/*!
 * socket.io-node
 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

/**
 * Test dependencies.
 */

var io = require('socket.io')
  , parser = io.parser
  , http = require('http')
  , https = require('https')
  , WebSocket = require('../support/node-websocket-client/lib/websocket').WebSocket;

/**
 * Exports.
 */

var should = module.exports = require('should');

should.HTTPClient = HTTPClient;

/**
 * Client utility.
 *
 * @api publiC
 */

function HTTPClient (port) {
  this.port = port;
  this.agent = new http.Agent({
      host: 'localhost'
    , port: port
  });
};

/**
 * Issue a request
 *
 * @api private
 */

HTTPClient.prototype.request = function (path, opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  opts = opts || {};
  opts.agent = this.agent;
  opts.host = 'localhost';
  opts.port = this.port;
  opts.path = path.replace(/{protocol}/g, io.protocol);

  opts.headers = opts.headers || {};
  opts.headers.Host = 'localhost';
  opts.headers.Connection = 'keep-alive';

  var req = http.request(opts, function (res) {
    if (false === opts.buffer)
      return fn && fn(res);

    var buf = '';

    res.on('data', function (chunk) {
      buf += chunk;
    });

    res.on('end', function () {
      fn && fn(res, opts.parse ? opts.parse(buf) : buf);
    });
  });

  req.on('error', function (err) { });

  if (undefined !== opts.data)
    req.write(opts.data);

  req.end();

  return req;
};

/**
 * Terminates the client and associated connections.
 *
 * @api public
 */

HTTPClient.prototype.end = function () {
  var self = this;
  Object.keys(this.agent.sockets).forEach(function (socket) {
    if (self.agent.sockets[socket].length > 0) {
      self.agent.sockets[socket][0]._handle.socket.end();
    }
  });
};

/**
 * Issue a GET request
 *
 * @api public
 */

HTTPClient.prototype.get = function (path, opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  opts = opts || {};
  opts.method = 'GET';

  // override the parser for transport requests
  if (/\/(xhr-polling|htmlfile|jsonp-polling)\//.test(path)) {
    // parser that might be necessary for transport-specific framing
    var transportParse = opts.parse;
    opts.parse = function (data) {
      if (data === '') return data;

      data = transportParse ? transportParse(data) : data;
      return parser.decodePayload(data);
    };
  } else {
    opts.parse = undefined;
  }

  return this.request(path, opts, fn);
};

/**
 * Issue a POST request
 *
 * @api private
 */

HTTPClient.prototype.post = function (path, data, opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  opts = opts || {};
  opts.method = 'POST';
  opts.data = data;

  return this.request(path, opts, fn);
};

/**
 * Issue a HEAD request
 *
 * @api private
 */

HTTPClient.prototype.head = function (path, opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  opts = opts || {};
  opts.method = 'HEAD';

  return this.request(path, opts, fn);
};

/**
 * Performs a handshake (GET) request
 *
 * @api private
 */

HTTPClient.prototype.handshake = function (opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  return this.get('/socket.io/{protocol}', opts, function (res, data) {
    fn && fn.apply(null, data.split(':'));
  });
};

/**
 * Generates a new client for the given port.
 *
 * @api private
 */

client = function (port) {
  return new HTTPClient(port);
};

/**
 * Create a socket.io server.
 */

create = function (cl) {
  var manager = io.listen(cl.port);
  manager.set('client store expiration', 0);
  return manager;
};

/**
 * WebSocket socket.io client.
 *
 * @api private
 */

function WSClient (port, sid, transport) {
  this.sid = sid;
  this.port = port;
  this.transportName = transport || 'websocket';
  WebSocket.call(
      this
    , 'ws://localhost:' + port + '/socket.io/'
        + io.protocol + '/' + this.transportName + '/' + sid
  );
};

/**
 * Inherits from WebSocket.
 */

WSClient.prototype.__proto__ = WebSocket.prototype;

/**
 * Overrides message event emission.
 *
 * @api private
 */

WSClient.prototype.emit = function (name) {
  var args = arguments;

  if (name == 'message' || name == 'data') {
    args[1] = parser.decodePacket(args[1].toString());
  }

  return WebSocket.prototype.emit.apply(this, arguments);
};

/**
 * Writes a packet
 */

WSClient.prototype.packet = function (pack) {
  this.write(parser.encodePacket(pack));
  return this;
};

/**
 * Creates a websocket client.
 *
 * @api public
 */

websocket = function (cl, sid, transport) {
  return new WSClient(cl.port, sid, transport);
};
<commit_msg>further checks to not close sockets that aren't there<commit_after>
/*!
 * socket.io-node
 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
 * MIT Licensed
 */

/**
 * Test dependencies.
 */

var io = require('socket.io')
  , parser = io.parser
  , http = require('http')
  , https = require('https')
  , WebSocket = require('../support/node-websocket-client/lib/websocket').WebSocket;

/**
 * Exports.
 */

var should = module.exports = require('should');

should.HTTPClient = HTTPClient;

/**
 * Client utility.
 *
 * @api publiC
 */

function HTTPClient (port) {
  this.port = port;
  this.agent = new http.Agent({
      host: 'localhost'
    , port: port
  });
};

/**
 * Issue a request
 *
 * @api private
 */

HTTPClient.prototype.request = function (path, opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  opts = opts || {};
  opts.agent = this.agent;
  opts.host = 'localhost';
  opts.port = this.port;
  opts.path = path.replace(/{protocol}/g, io.protocol);

  opts.headers = opts.headers || {};
  opts.headers.Host = 'localhost';
  opts.headers.Connection = 'keep-alive';

  var req = http.request(opts, function (res) {
    if (false === opts.buffer)
      return fn && fn(res);

    var buf = '';

    res.on('data', function (chunk) {
      buf += chunk;
    });

    res.on('end', function () {
      fn && fn(res, opts.parse ? opts.parse(buf) : buf);
    });
  });

  req.on('error', function (err) { });

  if (undefined !== opts.data)
    req.write(opts.data);

  req.end();

  return req;
};

/**
 * Terminates the client and associated connections.
 *
 * @api public
 */

HTTPClient.prototype.end = function () {
  var self = this;
  Object.keys(this.agent.sockets).forEach(function (socket) {
    if (self.agent.sockets[socket].length > 0 &&
        self.agent.sockets[socket][0]._handle) {
      self.agent.sockets[socket][0]._handle.socket.end();
    }
  });
};

/**
 * Issue a GET request
 *
 * @api public
 */

HTTPClient.prototype.get = function (path, opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  opts = opts || {};
  opts.method = 'GET';

  // override the parser for transport requests
  if (/\/(xhr-polling|htmlfile|jsonp-polling)\//.test(path)) {
    // parser that might be necessary for transport-specific framing
    var transportParse = opts.parse;
    opts.parse = function (data) {
      if (data === '') return data;

      data = transportParse ? transportParse(data) : data;
      return parser.decodePayload(data);
    };
  } else {
    opts.parse = undefined;
  }

  return this.request(path, opts, fn);
};

/**
 * Issue a POST request
 *
 * @api private
 */

HTTPClient.prototype.post = function (path, data, opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  opts = opts || {};
  opts.method = 'POST';
  opts.data = data;

  return this.request(path, opts, fn);
};

/**
 * Issue a HEAD request
 *
 * @api private
 */

HTTPClient.prototype.head = function (path, opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  opts = opts || {};
  opts.method = 'HEAD';

  return this.request(path, opts, fn);
};

/**
 * Performs a handshake (GET) request
 *
 * @api private
 */

HTTPClient.prototype.handshake = function (opts, fn) {
  if ('function' == typeof opts) {
    fn = opts;
    opts = {};
  }

  return this.get('/socket.io/{protocol}', opts, function (res, data) {
    fn && fn.apply(null, data.split(':'));
  });
};

/**
 * Generates a new client for the given port.
 *
 * @api private
 */

client = function (port) {
  return new HTTPClient(port);
};

/**
 * Create a socket.io server.
 */

create = function (cl) {
  var manager = io.listen(cl.port);
  manager.set('client store expiration', 0);
  return manager;
};

/**
 * WebSocket socket.io client.
 *
 * @api private
 */

function WSClient (port, sid, transport) {
  this.sid = sid;
  this.port = port;
  this.transportName = transport || 'websocket';
  WebSocket.call(
      this
    , 'ws://localhost:' + port + '/socket.io/'
        + io.protocol + '/' + this.transportName + '/' + sid
  );
};

/**
 * Inherits from WebSocket.
 */

WSClient.prototype.__proto__ = WebSocket.prototype;

/**
 * Overrides message event emission.
 *
 * @api private
 */

WSClient.prototype.emit = function (name) {
  var args = arguments;

  if (name == 'message' || name == 'data') {
    args[1] = parser.decodePacket(args[1].toString());
  }

  return WebSocket.prototype.emit.apply(this, arguments);
};

/**
 * Writes a packet
 */

WSClient.prototype.packet = function (pack) {
  this.write(parser.encodePacket(pack));
  return this;
};

/**
 * Creates a websocket client.
 *
 * @api public
 */

websocket = function (cl, sid, transport) {
  return new WSClient(cl.port, sid, transport);
};
<|endoftext|>


<commit_before>define([
	"dojo/_base/declare", "dijit/layout/BorderContainer", "dojo/on", "dojo/_base/Deferred",
	"dojo/dom-class", "dijit/layout/ContentPane", "dojo/dom-construct",
	"dojo/_base/xhr", "dojo/_base/lang", "./PageGrid", "./formatter", "../store/SubSystemMemoryStore", "dojo/request",
	"dojo/aspect", "./GridSelector", "dojo/when"
], function(declare, BorderContainer, on, Deferred,
			domClass, ContentPane, domConstruct,
			xhr, lang, Grid, formatter, Store, request,
			aspect, selector, when){
	return declare([Grid], {
		region: "center",
		query: (this.query || ""),
		apiToken: window.App.authorizationToken,
		apiServer: window.App.dataServiceURL,
		store: null,
		dataModel: "subsystem",
		primaryKey: "id",
		selectionModel: "extended",
		loadingMessage: "Loading subsystems.  This may take several minutes...",
		deselectOnRefresh: true,
		fullSelectAll: true,
		columns: {
			"Selection Checkboxes": selector({unhidable: true}),
			class: {label: "Class", field: "class"},
			subclass: {label: 'Subclass', field: 'subclass'},
			subsystem_id: {label: 'Subsystem ID', field: 'subsystem_id', hidden: true},
			subsystem_name: {label: 'Subsystem Name', field: 'subsystem_name'},
			role_id: {label: "Role ID", field: "role_id", hidden: true},
			role_name: {label: "Role Name", field: "role_name"},
			active: {label: "Active", field: "active"},
			patric_id: {label: "Patric ID", field: "patric_id"},
			gene: {label: "Gene", field: "gene"},
			product: {label: "Product", field: "product"},
			count: {label: "Count", field: ""}

		},

		startup: function(){
			var _self = this;

			this.on(".dgrid-content .dgrid-row:dblclick", function(evt){
				var row = _self.row(evt);
				//console.log("dblclick row:", row);
				on.emit(_self.domNode, "ItemDblClick", {
					item_path: row.data.path,
					item: row.data,
					bubbles: true,
					cancelable: true
				});
			});

			this.on("dgrid-select", function(evt){
				//console.log('dgrid-select: ', evt);
				var newEvt = {
					rows: evt.rows,
					selected: evt.grid.selection,
					grid: _self,
					bubbles: true,
					cancelable: true
				};
				on.emit(_self.domNode, "select", newEvt);
			});

			this.on("dgrid-deselect", function(evt){
				//console.log("dgrid-deselect");
				var newEvt = {
					rows: evt.rows,
					selected: evt.grid.selection,
					grid: _self,
					bubbles: true,
					cancelable: true
				};
				on.emit(_self.domNode, "deselect", newEvt);
			});

			aspect.before(_self, 'renderArray', function(results){
				Deferred.when(results.total, function(x){
					_self.set("totalRows", x);
				});
			});
			this._started = true;
		},
		state: null,

		_setApiServer: function(server){
			this.apiServer = server;
		},

		_setState: function(state){

			var oldState = this.get('state');

			var ov, nv;
			if(oldState){
				ov = oldState.search;
				if(oldState.hashParams.filter){
					ov = ov + oldState.hashParams.filter;
				}
			}

			if(state){
				nv = state.search;
				if(state.hashParams.filter){
					nv = nv + state.hashParams.filter;
				}
			}

			this.state = state;

			if(ov != nv){
				//console.log("New State in subsystems Memory Grid: ", nv);

				if(!this.store){
					this.set('store', this.createStore(this.apiServer, this.apiToken || window.App.authorizationToken, state));
				}else{
					this.store.set("state", lang.mixin({}, state));
				}

				this.refresh()
			}else{
				this.refresh()
			}

		},

		refresh: function(){
			this.inherited(arguments);

		},

		createStore: function(server, token, state){
			if(this.store){
				return this.store
			}

			return new Store({
				token: token,
				apiServer: this.apiServer || window.App.dataServiceURL,
				state: state || this.state
			});
		},
		_selectAll: function(){
			var _self = this;
			var def = new Deferred();
			when(this.store.query({}, this.queryOptions), function(results){
				_self._unloadedData = {};

				def.resolve(results.map(function(obj){
					_self._unloadedData[obj[_self.primaryKey]] = obj;
					return obj[_self.primaryKey];
				}));
			});
			return def.promise;
		}
	});
});
<commit_msg>remove diagnostic column from object model<commit_after>define([
	"dojo/_base/declare", "dijit/layout/BorderContainer", "dojo/on", "dojo/_base/Deferred",
	"dojo/dom-class", "dijit/layout/ContentPane", "dojo/dom-construct",
	"dojo/_base/xhr", "dojo/_base/lang", "./PageGrid", "./formatter", "../store/SubSystemMemoryStore", "dojo/request",
	"dojo/aspect", "./GridSelector", "dojo/when"
], function(declare, BorderContainer, on, Deferred,
			domClass, ContentPane, domConstruct,
			xhr, lang, Grid, formatter, Store, request,
			aspect, selector, when){
	return declare([Grid], {
		region: "center",
		query: (this.query || ""),
		apiToken: window.App.authorizationToken,
		apiServer: window.App.dataServiceURL,
		store: null,
		dataModel: "subsystem",
		primaryKey: "id",
		selectionModel: "extended",
		loadingMessage: "Loading subsystems.  This may take several minutes...",
		deselectOnRefresh: true,
		fullSelectAll: true,
		columns: {
			"Selection Checkboxes": selector({unhidable: true}),
			class: {label: "Class", field: "class"},
			subclass: {label: 'Subclass', field: 'subclass'},
			subsystem_id: {label: 'Subsystem ID', field: 'subsystem_id', hidden: true},
			subsystem_name: {label: 'Subsystem Name', field: 'subsystem_name'},
			role_id: {label: "Role ID", field: "role_id", hidden: true},
			role_name: {label: "Role Name", field: "role_name"},
			active: {label: "Active", field: "active"},
			patric_id: {label: "Patric ID", field: "patric_id"},
			gene: {label: "Gene", field: "gene"},
			product: {label: "Product", field: "product"}

		},

		startup: function(){
			var _self = this;

			this.on(".dgrid-content .dgrid-row:dblclick", function(evt){
				var row = _self.row(evt);
				//console.log("dblclick row:", row);
				on.emit(_self.domNode, "ItemDblClick", {
					item_path: row.data.path,
					item: row.data,
					bubbles: true,
					cancelable: true
				});
			});

			this.on("dgrid-select", function(evt){
				//console.log('dgrid-select: ', evt);
				var newEvt = {
					rows: evt.rows,
					selected: evt.grid.selection,
					grid: _self,
					bubbles: true,
					cancelable: true
				};
				on.emit(_self.domNode, "select", newEvt);
			});

			this.on("dgrid-deselect", function(evt){
				//console.log("dgrid-deselect");
				var newEvt = {
					rows: evt.rows,
					selected: evt.grid.selection,
					grid: _self,
					bubbles: true,
					cancelable: true
				};
				on.emit(_self.domNode, "deselect", newEvt);
			});

			aspect.before(_self, 'renderArray', function(results){
				Deferred.when(results.total, function(x){
					_self.set("totalRows", x);
				});
			});
			this._started = true;
		},
		state: null,

		_setApiServer: function(server){
			this.apiServer = server;
		},

		_setState: function(state){

			var oldState = this.get('state');

			var ov, nv;
			if(oldState){
				ov = oldState.search;
				if(oldState.hashParams.filter){
					ov = ov + oldState.hashParams.filter;
				}
			}

			if(state){
				nv = state.search;
				if(state.hashParams.filter){
					nv = nv + state.hashParams.filter;
				}
			}

			this.state = state;

			if(ov != nv){
				//console.log("New State in subsystems Memory Grid: ", nv);

				if(!this.store){
					this.set('store', this.createStore(this.apiServer, this.apiToken || window.App.authorizationToken, state));
				}else{
					this.store.set("state", lang.mixin({}, state));
				}

				this.refresh()
			}else{
				this.refresh()
			}

		},

		refresh: function(){
			this.inherited(arguments);

		},

		createStore: function(server, token, state){
			if(this.store){
				return this.store
			}

			return new Store({
				token: token,
				apiServer: this.apiServer || window.App.dataServiceURL,
				state: state || this.state
			});
		},
		_selectAll: function(){
			var _self = this;
			var def = new Deferred();
			when(this.store.query({}, this.queryOptions), function(results){
				_self._unloadedData = {};

				def.resolve(results.map(function(obj){
					_self._unloadedData[obj[_self.primaryKey]] = obj;
					return obj[_self.primaryKey];
				}));
			});
			return def.promise;
		}
	});
});
<|endoftext|>