<commit_before>/* To convert to and from how the document is stored in the database to how ProseMirror expects it.
 We use the DOM import for ProseMirror as the JSON we store in the database is really jsonized HTML.
*/

import {obj2Node} from "../exporter/tools/json"
import {docSchema} from "./document"
import {randomHeadingId, randomFigureId} from "./common"

export const getSettings = function(pmArticle) {
    const settings = JSON.parse(JSON.stringify(pmArticle.attrs))
    return settings
}

export const updateDoc = function(doc, bibliography, docVersion) {
    /* This is to clean documents taking all the accepted formatting from older
       versions and outputting the current version of the doc format.
       Notice that the docVersion isn't the same as the version of the FW export
       file in Fidus Writer < 3.2 (docVersion/FW file versions versions -1.X).
       While the FW file version also says something about what files could be
       available inside the FW zip, the doc_version refers to how the data is
       stored in those files.
       In general, an update to the doc_version will likely also trigger an
       update to the version of the FW export file, the reverse is not always
       true.
    */

    switch(docVersion) {
        // Import from versions up to 3.0 no longer supported starting with Fidus Writer 3.5
        case 1: // Fidus Writer 3.1 prerelease
            doc = convertDocV1(doc)
            doc = convertDocV11(doc)
            doc = convertDocV12(doc)
            doc = convertDocV13(doc, bibliography)
            doc = convertDocV20(doc)
            doc = convertDocV21(doc)
            break
        case 1.1: // Fidus Writer 3.1
            doc = convertDocV11(doc)
            doc = convertDocV12(doc)
            doc = convertDocV13(doc, bibliography)
            doc = convertDocV20(doc)
            doc = convertDocV21(doc)
            break
        case 1.2: // Fidus Writer 3.2
            doc = convertDocV12(doc)
            doc = convertDocV13(doc, bibliography)
            doc = convertDocV20(doc)
            doc = convertDocV21(doc)
            break
        case 1.3: // Fidus Writer 3.3 prerelease
            doc = convertDocV13(doc, bibliography)
            doc = convertDocV20(doc)
            doc = convertDocV21(doc)
            break
        case 2.0: // Fidus Writer 3.3
            doc = convertDocV20(doc)
            doc = convertDocV21(doc)
            break
        case 2.1: // Fidus Writer 3.4
            doc = convertDocV21(doc)
            break
    }
    return doc
}

const convertDocV1 = function(doc) {
    const returnDoc = JSON.parse(JSON.stringify(doc))
    convertNodeV1(returnDoc.contents)
    return returnDoc
}

const convertNodeV1 = function(node) {
    switch (node.type) {
        case 'citation':
            const prefixes = node.attrs.bibBefore
            prefixes = prefixes ? prefixes.split(',,,') : []
            const locators = node.attrs.bibPage
            locators = locators ? locators.split(',,,') : []
            const ids = node.attrs.bibEntry
            ids = ids ? ids.split(',') : []
            const references = ids.map((id, index) => {
                const returnObj = {id: parseInt(id)}
                if (prefixes[index] && prefixes[index] !== '') {
                    returnObj['prefix'] = prefixes[index]
                }
                if (locators[index] && locators[index] !== '') {
                    returnObj['locator'] = locators[index]
                }
                return returnObj
            })
            node.attrs = {
                format: node.attrs.bibFormat,
                references
            }
            break
        case 'footnote':
            if (node.attrs && node.attrs.footnote) {
                node.attrs.footnote.forEach(childNode => {
                    convertNodeV1(childNode)
                })
            }
            break
    }
    if (node.content) {
        node.content.forEach(childNode => {
            convertNodeV1(childNode)
        })
    }
}

const convertDocV11 = function(doc) {
    const returnDoc = JSON.parse(JSON.stringify(doc))
    convertNodeV11(returnDoc.contents)
    return returnDoc
}

const convertNodeV11 = function(node, ids = []) {
    switch (node.type) {
        case 'heading':
            const blockId = node.attrs.id
            while (!blockId || ids.includes(blockId)) {
                blockId = randomHeadingId()
            }
            node.attrs.id = blockId
            ids.push(blockId)
            break
    }
    if (node.content) {
        node.content.forEach(childNode => {
            convertNodeV11(childNode, ids)
        })
    }
}

const convertDocV12 = function(doc) {
    const returnDoc = JSON.parse(JSON.stringify(doc))
    convertNodeV12(returnDoc.contents)
    return returnDoc
}

const convertNodeV12 = function(node, ids = []) {
    switch (node.type) {
        case 'figure':
            let blockId = node.attrs.id
            while (!blockId || ids.includes(blockId)) {
                blockId = randomFigureId()
            }
            node.attrs.id = blockId
            ids.push(blockId)
            break
    }
    if (node.content) {
        node.content.forEach(childNode => {
            convertNodeV12(childNode, ids)
        })
    }
}

const convertDocV13 = function(doc, bibliography) {
    const returnDoc = JSON.parse(JSON.stringify(doc))
    delete returnDoc.settings
    delete returnDoc.metadata
    returnDoc.bibliography = {}
    returnDoc.imageIds = []
    convertNodeV13(returnDoc.contents, returnDoc.bibliography, bibliography, returnDoc.imageIds)
    return returnDoc
}

const convertNodeV13 = function(node, shrunkBib, fullBib, imageIds) {
    switch (node.type) {
        case 'article':
            node.attrs.language = 'en-US'
            break
        case 'authors':
            const authorsText = node.content ? node.content.reduce(
                    (text, item) => item.type === 'text' ? text + item.text : text,
                    ''
                ) : ''
            node.content = authorsText.split(/[,;]/g).map(authorString => {
                const author = authorString.trim()
                if (!author.length) {
                    return false
                }
                const authorParts = author.split(' ')
                return {
                    type: 'author',
                    attrs: {
                        firstname: authorParts.length > 1 ? authorParts.shift() : false,
                        lastname: authorParts.join(' '),
                        institution: false,
                        email: false
                    }
                }
            }).filter(authorObj => authorObj)
            if (!node.content.length) {
                delete node.content
            }
            break
        case 'citation':
            node.attrs.references.forEach(ref => {
                const item = fullBib[ref.id]
                if (!item) {
                    item = {
                        fields: {"title":[{"type":"text","text":"Deleted"}]},
                        bib_type: "misc",
                        entry_key: "FidusWriter"
                    }
                }
                item = Object.assign({}, item)
                delete item.entry_cat
                shrunkBib[ref.id] = item
            })
            break
        case 'keywords':
                const keywordsText = node.content ? node.content.reduce(
                        (text, item) => item.type === 'text' ? text + item.text : text,
                        ''
                    ) : ''
                node.content = keywordsText.split(/[,;]/g).map(keywordString => {
                    const keyword = keywordString.trim()
                    if (!keyword.length) {
                        return false
                    }
                    return {
                        type: 'keyword',
                        attrs: {
                            keyword
                        }
                    }
                }).filter(keywordObj => keywordObj)
                if (!node.content.length) {
                    delete node.content
                }
                break
        case 'figure':
            if (isNaN(parseInt(node.attrs.image))) {
                node.attrs.image = false
            } else {
                imageIds.push(parseInt(node.attrs.image))
            }
            break
    }
    if (node.content) {
        node.content.forEach(childNode => {
            convertNodeV13(childNode, shrunkBib, fullBib, imageIds)
        })
    }
}

const convertDocV20 = function(doc) {
    const returnDoc = JSON.parse(JSON.stringify(doc))
    delete(returnDoc.added)
    delete(returnDoc.is_owner)
    delete(returnDoc.revisions)
    delete(returnDoc.rights)
    delete(returnDoc.updated)
    if(returnDoc.contents.attrs) {
        returnDoc.contents.attrs.tracked = false
    }
    Object.values(returnDoc.comments).forEach(comment => {
        comment.username = comment.userName
        comment.isMajor = comment['review:isMajor']
        delete(comment.userAvatar)
        delete(comment.userName)
        delete(comment['review:isMajor'])
        if (comment.answers) {
            comment.answers.forEach(answer => {
                answer.username = answer.userName
                delete(answer.userAvatar)
                delete(answer.userName)
            })
        }
    })
    return returnDoc
}

const convertNodeV21 = function(node) {
    let commentMark
    if (node.marks && (commentMark = node.marks.find(mark => mark.type==='comment'))) {
        commentMark.attrs.id = String(commentMark.attrs.id)
    }
    if (node.content) {
        node.content.forEach(childNode => convertNodeV21(childNode))
    }
}

const convertDocV21 = function(doc) {
    const returnDoc = JSON.parse(JSON.stringify(doc))
    convertNodeV21(returnDoc.contents)
    Object.entries(returnDoc.comments).forEach(([commentId, comment]) => {
        delete(comment.id)
        comment.assignedUser = false
        comment.assignedUsername = false
        comment.resolved = false
        comment.comment = comment.comment.split('\n').map(text =>
            ({type: 'paragraph', content: [{type: 'text', text}]})
        )
        if (comment.answers) {
            comment.answers.forEach(answer => {
                answer.id = answer.answerId ? String(answer.answerId) :
                    answer.id && String(answer.id) !== String(commentId) ? String(answer.id) :
                    String(Math.floor(Math.random() * 0xffffffff))
                delete(answer.answerId)
                answer.answer = answer.answer.split('\n').map(text =>
                    ({type: 'paragraph', content: [{type: 'text', text}]})
                )
            })
        }
    })
    return returnDoc
}
<commit_msg>revert some let => const<commit_after>/* To convert to and from how the document is stored in the database to how ProseMirror expects it.
 We use the DOM import for ProseMirror as the JSON we store in the database is really jsonized HTML.
*/

import {obj2Node} from "../exporter/tools/json"
import {docSchema} from "./document"
import {randomHeadingId, randomFigureId} from "./common"

export const getSettings = function(pmArticle) {
    const settings = JSON.parse(JSON.stringify(pmArticle.attrs))
    return settings
}

export const updateDoc = function(doc, bibliography, docVersion) {
    /* This is to clean documents taking all the accepted formatting from older
       versions and outputting the current version of the doc format.
       Notice that the docVersion isn't the same as the version of the FW export
       file in Fidus Writer < 3.2 (docVersion/FW file versions versions -1.X).
       While the FW file version also says something about what files could be
       available inside the FW zip, the doc_version refers to how the data is
       stored in those files.
       In general, an update to the doc_version will likely also trigger an
       update to the version of the FW export file, the reverse is not always
       true.
    */

    switch(docVersion) {
        // Import from versions up to 3.0 no longer supported starting with Fidus Writer 3.5
        case 1: // Fidus Writer 3.1 prerelease
            doc = convertDocV1(doc)
            doc = convertDocV11(doc)
            doc = convertDocV12(doc)
            doc = convertDocV13(doc, bibliography)
            doc = convertDocV20(doc)
            doc = convertDocV21(doc)
            break
        case 1.1: // Fidus Writer 3.1
            doc = convertDocV11(doc)
            doc = convertDocV12(doc)
            doc = convertDocV13(doc, bibliography)
            doc = convertDocV20(doc)
            doc = convertDocV21(doc)
            break
        case 1.2: // Fidus Writer 3.2
            doc = convertDocV12(doc)
            doc = convertDocV13(doc, bibliography)
            doc = convertDocV20(doc)
            doc = convertDocV21(doc)
            break
        case 1.3: // Fidus Writer 3.3 prerelease
            doc = convertDocV13(doc, bibliography)
            doc = convertDocV20(doc)
            doc = convertDocV21(doc)
            break
        case 2.0: // Fidus Writer 3.3
            doc = convertDocV20(doc)
            doc = convertDocV21(doc)
            break
        case 2.1: // Fidus Writer 3.4
            doc = convertDocV21(doc)
            break
    }
    return doc
}

const convertDocV1 = function(doc) {
    const returnDoc = JSON.parse(JSON.stringify(doc))
    convertNodeV1(returnDoc.contents)
    return returnDoc
}

const convertNodeV1 = function(node) {
    switch (node.type) {
        case 'citation':
            const prefixes = node.attrs.bibBefore ? node.attrs.bibBefore.split(',,,') : []
            const locators = node.attrs.bibPage ? node.attrs.bibPage.split(',,,') : []
            const ids = node.attrs.bibEntry ? node.attrs.bibEntry.split(',') : []
            const references = ids.map((id, index) => {
                const returnObj = {id: parseInt(id)}
                if (prefixes[index] && prefixes[index] !== '') {
                    returnObj['prefix'] = prefixes[index]
                }
                if (locators[index] && locators[index] !== '') {
                    returnObj['locator'] = locators[index]
                }
                return returnObj
            })
            node.attrs = {
                format: node.attrs.bibFormat,
                references
            }
            break
        case 'footnote':
            if (node.attrs && node.attrs.footnote) {
                node.attrs.footnote.forEach(childNode => {
                    convertNodeV1(childNode)
                })
            }
            break
    }
    if (node.content) {
        node.content.forEach(childNode => {
            convertNodeV1(childNode)
        })
    }
}

const convertDocV11 = function(doc) {
    const returnDoc = JSON.parse(JSON.stringify(doc))
    convertNodeV11(returnDoc.contents)
    return returnDoc
}

const convertNodeV11 = function(node, ids = []) {
    switch (node.type) {
        case 'heading':
            let blockId = node.attrs.id
            while (!blockId || ids.includes(blockId)) {
                blockId = randomHeadingId()
            }
            node.attrs.id = blockId
            ids.push(blockId)
            break
    }
    if (node.content) {
        node.content.forEach(childNode => {
            convertNodeV11(childNode, ids)
        })
    }
}

const convertDocV12 = function(doc) {
    const returnDoc = JSON.parse(JSON.stringify(doc))
    convertNodeV12(returnDoc.contents)
    return returnDoc
}

const convertNodeV12 = function(node, ids = []) {
    switch (node.type) {
        case 'figure':
            let blockId = node.attrs.id
            while (!blockId || ids.includes(blockId)) {
                blockId = randomFigureId()
            }
            node.attrs.id = blockId
            ids.push(blockId)
            break
    }
    if (node.content) {
        node.content.forEach(childNode => {
            convertNodeV12(childNode, ids)
        })
    }
}

const convertDocV13 = function(doc, bibliography) {
    const returnDoc = JSON.parse(JSON.stringify(doc))
    delete returnDoc.settings
    delete returnDoc.metadata
    returnDoc.bibliography = {}
    returnDoc.imageIds = []
    convertNodeV13(returnDoc.contents, returnDoc.bibliography, bibliography, returnDoc.imageIds)
    return returnDoc
}

const convertNodeV13 = function(node, shrunkBib, fullBib, imageIds) {
    switch (node.type) {
        case 'article':
            node.attrs.language = 'en-US'
            break
        case 'authors':
            const authorsText = node.content ? node.content.reduce(
                    (text, item) => item.type === 'text' ? text + item.text : text,
                    ''
                ) : ''
            node.content = authorsText.split(/[,;]/g).map(authorString => {
                const author = authorString.trim()
                if (!author.length) {
                    return false
                }
                const authorParts = author.split(' ')
                return {
                    type: 'author',
                    attrs: {
                        firstname: authorParts.length > 1 ? authorParts.shift() : false,
                        lastname: authorParts.join(' '),
                        institution: false,
                        email: false
                    }
                }
            }).filter(authorObj => authorObj)
            if (!node.content.length) {
                delete node.content
            }
            break
        case 'citation':
            node.attrs.references.forEach(ref => {
                let item = fullBib[ref.id]
                if (!item) {
                    item = {
                        fields: {"title":[{"type":"text","text":"Deleted"}]},
                        bib_type: "misc",
                        entry_key: "FidusWriter"
                    }
                }
                item = Object.assign({}, item)
                delete item.entry_cat
                shrunkBib[ref.id] = item
            })
            break
        case 'keywords':
                const keywordsText = node.content ? node.content.reduce(
                        (text, item) => item.type === 'text' ? text + item.text : text,
                        ''
                    ) : ''
                node.content = keywordsText.split(/[,;]/g).map(keywordString => {
                    const keyword = keywordString.trim()
                    if (!keyword.length) {
                        return false
                    }
                    return {
                        type: 'keyword',
                        attrs: {
                            keyword
                        }
                    }
                }).filter(keywordObj => keywordObj)
                if (!node.content.length) {
                    delete node.content
                }
                break
        case 'figure':
            if (isNaN(parseInt(node.attrs.image))) {
                node.attrs.image = false
            } else {
                imageIds.push(parseInt(node.attrs.image))
            }
            break
    }
    if (node.content) {
        node.content.forEach(childNode => {
            convertNodeV13(childNode, shrunkBib, fullBib, imageIds)
        })
    }
}

const convertDocV20 = function(doc) {
    const returnDoc = JSON.parse(JSON.stringify(doc))
    delete(returnDoc.added)
    delete(returnDoc.is_owner)
    delete(returnDoc.revisions)
    delete(returnDoc.rights)
    delete(returnDoc.updated)
    if(returnDoc.contents.attrs) {
        returnDoc.contents.attrs.tracked = false
    }
    Object.values(returnDoc.comments).forEach(comment => {
        comment.username = comment.userName
        comment.isMajor = comment['review:isMajor']
        delete(comment.userAvatar)
        delete(comment.userName)
        delete(comment['review:isMajor'])
        if (comment.answers) {
            comment.answers.forEach(answer => {
                answer.username = answer.userName
                delete(answer.userAvatar)
                delete(answer.userName)
            })
        }
    })
    return returnDoc
}

const convertNodeV21 = function(node) {
    let commentMark
    if (node.marks && (commentMark = node.marks.find(mark => mark.type==='comment'))) {
        commentMark.attrs.id = String(commentMark.attrs.id)
    }
    if (node.content) {
        node.content.forEach(childNode => convertNodeV21(childNode))
    }
}

const convertDocV21 = function(doc) {
    const returnDoc = JSON.parse(JSON.stringify(doc))
    convertNodeV21(returnDoc.contents)
    Object.entries(returnDoc.comments).forEach(([commentId, comment]) => {
        delete(comment.id)
        comment.assignedUser = false
        comment.assignedUsername = false
        comment.resolved = false
        comment.comment = comment.comment.split('\n').map(text =>
            ({type: 'paragraph', content: [{type: 'text', text}]})
        )
        if (comment.answers) {
            comment.answers.forEach(answer => {
                answer.id = answer.answerId ? String(answer.answerId) :
                    answer.id && String(answer.id) !== String(commentId) ? String(answer.id) :
                    String(Math.floor(Math.random() * 0xffffffff))
                delete(answer.answerId)
                answer.answer = answer.answer.split('\n').map(text =>
                    ({type: 'paragraph', content: [{type: 'text', text}]})
                )
            })
        }
    })
    return returnDoc
}
<|endoftext|>


<commit_before>export default ({minute, second, millisecond}) => (

    {minute}:{second}:{millisecond}

)
<commit_msg>Fix time too long in small mobile device<commit_after>export default ({minute, second, millisecond}) => (

    {minute}:{second}:{millisecond}

)
<|endoftext|>


<commit_before>'use strict';

describe('cards_job', function() {

	var testParent,
			jobData;

	describe('template', function() {

		beforeEach(function() {
			testParent = document.createElement('div');
			testParent.className = 'jobs';

			jobData = {
				fields: {
					organisation: 'Test Organisation',
					role: 'Developer',
					location: 'Somewhere',
					startDate: '2013-08-15',
					endDate: '2014-01-15',
					description: 'A description of this job'
				}
			};
		});

		afterEach(function() {
			jobData = [];
			while(testParent.firstChild) {
				testParent.removeChild(testParent.firstChild);
			}
		});

		it('should render the job correctly given a fixed start and end date', function(done) {
			/**
			 *	Setting up the node references for testing later
			 */
			var title, role_location, dates, description;

			/**
			 *	Spies
			 */
			spyOn(Meteor, 'subscribe').and.returnValue({
				subscriptionId: 1,
				ready: function() {
					return true;
				}
			});

			/**
			 *	Render the template with the data and set up node selectors
			 */
			Blaze.renderWithData(Template.cards_job, jobData, testParent);
			title = testParent.getElementsByClassName('job__title')[0];
			role_location = testParent.getElementsByClassName('job__role_location')[0];
			dates = testParent.getElementsByClassName('job__dates__time');
			description = testParent.getElementsByTagName('p')[0];

			/**
			 *	Then run the tests
			 */
			expect(title.firstChild.wholeText.trim()).toEqual('Test Organisation');
			expect(role_location.firstChild.wholeText).toContain('Developer, Somewhere');
			expect(dates[0].firstChild.wholeText.trim()).toContain('Aug 2013');
			expect(dates[1].firstChild.wholeText.trim()).toEqual('Jan 2014');
			expect(description.firstChild.wholeText.trim()).toEqual('A description of this job');

			/**
			 *	Finished
			 */
			setTimeout(done, 200);
		});

		it('should show "Present" as the job end date in the absence of an end date', function(done) {
			/**
			 *	Setting up the node references for testing later
			 */
			var dates;

			/**
			 *	Removing this from the job data to trigger the appearance of 'Present'
			 */
			delete jobData.fields.endDate;

			/**
			 *	Spies
			 */
			spyOn(Meteor, 'subscribe').and.returnValue({
				subscriptionId: 1,
				ready: function() {
					return true;
				}
			});

			/**
			 *	Render the template with the data and set up node selectors
			 */
			Blaze.renderWithData(Template.cards_job, jobData, testParent);
			dates = testParent.getElementsByClassName('job__dates__time');

			/**
			 *	Then run the tests
			 */
			expect(dates[0].firstChild.wholeText.trim()).toContain('Aug 2013');
			expect(dates[1].firstChild.wholeText.trim()).toEqual('Present');

			/**
			 *	Finished
			 */
			setTimeout(done, 200);
		});

		it('should render the correct number of images', function(done) {

			/**
			 *	Setting up
			 */
			var images;

			jobData.fields.projectLogos = [
				{
					sys: {
						id: 'abcde'
					}
				},
				{
					sys: {
						id: 'fghij'
					}
				}
			];

			/**
			 *	Spies - spying on the collections find
			 *	function and returning dummy data
			 */
			spyOn(Meteor, 'subscribe').and.returnValue({
				subscriptionId: 1,
				ready: function() {
					return true;
				}
			});

			spyOn(Core.app.collections.images, 'find').and.callFake(function(selector) {
				return {
					fetch: function() {
						return [
							{width: 800, density: {multiplier: 1, prefixed: ''}, device: 'desktop', filename: 'abcde-desktop.jpg'},
							{width: 1600, density: {multiplier: 2, prefixed: '@2x'}, device: 'desktop', filename: 'abcde-desktop@2x.jpg'},
							{width: 2400, density: {multiplier: 3, prefixed: '@3x'}, device: 'desktop', filename: 'abcde-desktop@3x.jpg'},
							{width: 400, density: {multiplier: 1, prefixed: ''}, device: 'tablet', filename: 'abcde-tablet.jpg'},
							{width: 800, density: {multiplier: 2, prefixed: '@2x'}, device: 'tablet', filename: 'abcde-tablet@2x.jpg'},
							{width: 1200, density: {multiplier: 3, prefixed: '@3x'}, device: 'tablet', filename: 'abcde-tablet@3x.jpg'},
							{width: 320, density: {multiplier: 1, prefixed: ''}, device: 'mobile', filename: 'abcde-mobile.jpg'},
							{width: 640, density: {multiplier: 2, prefixed: '@2x'}, device: 'mobile', filename: 'abcde-mobile@2x.jpg'},
							{width: 960, density: {multiplier: 3, prefixed: '@3x'}, device: 'mobile', filename: 'abcde-mobile@3x.jpg'},

							{width: 800, density: {multiplier: 1, prefixed: ''}, device: 'desktop', filename: 'fghij-desktop.jpg'},
							{width: 1600, density: {multiplier: 2, prefixed: '@2x'}, device: 'desktop', filename: 'fghij-desktop@2x.jpg'},
							{width: 2400, density: {multiplier: 3, prefixed: '@3x'}, device: 'desktop', filename: 'fghij-desktop@3x.jpg'},
							{width: 400, density: {multiplier: 1, prefixed: ''}, device: 'tablet', filename: 'fghij-tablet.jpg'},
							{width: 800, density: {multiplier: 2, prefixed: '@2x'}, device: 'tablet', filename: 'fghij-tablet@2x.jpg'},
							{width: 1200, density: {multiplier: 3, prefixed: '@3x'}, device: 'tablet', filename: 'fghij-tablet@3x.jpg'},
							{width: 320, density: {multiplier: 1, prefixed: ''}, device: 'mobile', filename: 'fghij-mobile.jpg'},
							{width: 640, density: {multiplier: 2, prefixed: '@2x'}, device: 'mobile', filename: 'fghij-mobile@2x.jpg'},
							{width: 960, density: {multiplier: 3, prefixed: '@3x'}, device: 'mobile', filename: 'fghij-mobile@3x.jpg'}
						]
					}
				};
			});

			/**
			 *	Run the function
			 */
			Blaze.renderWithData(Template.cards_job, jobData, testParent);
			images = testParent.getElementsByTagName('img');

			/**
			 *	Running the tests
			 */
			expect(images.length).toEqual(2);
			expect(images[0].getAttribute('src')).toContain('abcde');
			expect(images[1].getAttribute('src')).toContain('fghij');

			/**
			 *	Finished
			 */
			setTimeout(done, 200);
		});
	});
});<commit_msg>Added helper tests for the job card.<commit_after>'use strict';

describe('cards_job', function() {

	var testParent,
			jobData;

	describe('template', function() {

		beforeEach(function() {
			testParent = document.createElement('div');
			testParent.className = 'jobs';

			jobData = {
				fields: {
					organisation: 'Test Organisation',
					role: 'Developer',
					location: 'Somewhere',
					startDate: '2013-08-15',
					endDate: '2014-01-15',
					description: 'A description of this job'
				}
			};
		});

		afterEach(function() {
			jobData = [];
			while(testParent.firstChild) {
				testParent.removeChild(testParent.firstChild);
			}
		});

		it('should render the job correctly given a fixed start and end date', function(done) {
			/**
			 *	Setting up the node references for testing later
			 */
			var title, role_location, dates, description;

			/**
			 *	Spies
			 */
			spyOn(Meteor, 'subscribe').and.returnValue({
				subscriptionId: 1,
				ready: function() {
					return true;
				}
			});

			/**
			 *	Render the template with the data and set up node selectors
			 */
			Blaze.renderWithData(Template.cards_job, jobData, testParent);
			title = testParent.getElementsByClassName('job__title')[0];
			role_location = testParent.getElementsByClassName('job__role_location')[0];
			dates = testParent.getElementsByClassName('job__dates__time');
			description = testParent.getElementsByTagName('p')[0];

			/**
			 *	Then run the tests
			 */
			expect(title.firstChild.wholeText.trim()).toEqual('Test Organisation');
			expect(role_location.firstChild.wholeText).toContain('Developer, Somewhere');
			expect(dates[0].firstChild.wholeText.trim()).toContain('Aug 2013');
			expect(dates[1].firstChild.wholeText.trim()).toEqual('Jan 2014');
			expect(description.firstChild.wholeText.trim()).toEqual('A description of this job');

			/**
			 *	Finished
			 */
			setTimeout(done, 200);
		});

		it('should show "Present" as the job end date in the absence of an end date', function(done) {
			/**
			 *	Setting up the node references for testing later
			 */
			var dates;

			/**
			 *	Removing this from the job data to trigger the appearance of 'Present'
			 */
			delete jobData.fields.endDate;

			/**
			 *	Spies
			 */
			spyOn(Meteor, 'subscribe').and.returnValue({
				subscriptionId: 1,
				ready: function() {
					return true;
				}
			});

			/**
			 *	Render the template with the data and set up node selectors
			 */
			Blaze.renderWithData(Template.cards_job, jobData, testParent);
			dates = testParent.getElementsByClassName('job__dates__time');

			/**
			 *	Then run the tests
			 */
			expect(dates[0].firstChild.wholeText.trim()).toContain('Aug 2013');
			expect(dates[1].firstChild.wholeText.trim()).toEqual('Present');

			/**
			 *	Finished
			 */
			setTimeout(done, 200);
		});

		it('should render the correct number of images', function(done) {

			/**
			 *	Setting up
			 */
			var images;

			jobData.fields.projectLogos = [
				{
					sys: {
						id: 'abcde'
					}
				},
				{
					sys: {
						id: 'fghij'
					}
				}
			];

			/**
			 *	Spies - spying on the collections find
			 *	function and returning dummy data
			 */
			spyOn(Meteor, 'subscribe').and.returnValue({
				subscriptionId: 1,
				ready: function() {
					return true;
				}
			});

			spyOn(Core.app.collections.images, 'find').and.callFake(function(selector) {
				return {
					fetch: function() {
						return [
							{width: 800, density: {multiplier: 1, prefixed: ''}, device: 'desktop', filename: 'abcde-desktop.jpg'},
							{width: 1600, density: {multiplier: 2, prefixed: '@2x'}, device: 'desktop', filename: 'abcde-desktop@2x.jpg'},
							{width: 2400, density: {multiplier: 3, prefixed: '@3x'}, device: 'desktop', filename: 'abcde-desktop@3x.jpg'},
							{width: 400, density: {multiplier: 1, prefixed: ''}, device: 'tablet', filename: 'abcde-tablet.jpg'},
							{width: 800, density: {multiplier: 2, prefixed: '@2x'}, device: 'tablet', filename: 'abcde-tablet@2x.jpg'},
							{width: 1200, density: {multiplier: 3, prefixed: '@3x'}, device: 'tablet', filename: 'abcde-tablet@3x.jpg'},
							{width: 320, density: {multiplier: 1, prefixed: ''}, device: 'mobile', filename: 'abcde-mobile.jpg'},
							{width: 640, density: {multiplier: 2, prefixed: '@2x'}, device: 'mobile', filename: 'abcde-mobile@2x.jpg'},
							{width: 960, density: {multiplier: 3, prefixed: '@3x'}, device: 'mobile', filename: 'abcde-mobile@3x.jpg'},

							{width: 800, density: {multiplier: 1, prefixed: ''}, device: 'desktop', filename: 'fghij-desktop.jpg'},
							{width: 1600, density: {multiplier: 2, prefixed: '@2x'}, device: 'desktop', filename: 'fghij-desktop@2x.jpg'},
							{width: 2400, density: {multiplier: 3, prefixed: '@3x'}, device: 'desktop', filename: 'fghij-desktop@3x.jpg'},
							{width: 400, density: {multiplier: 1, prefixed: ''}, device: 'tablet', filename: 'fghij-tablet.jpg'},
							{width: 800, density: {multiplier: 2, prefixed: '@2x'}, device: 'tablet', filename: 'fghij-tablet@2x.jpg'},
							{width: 1200, density: {multiplier: 3, prefixed: '@3x'}, device: 'tablet', filename: 'fghij-tablet@3x.jpg'},
							{width: 320, density: {multiplier: 1, prefixed: ''}, device: 'mobile', filename: 'fghij-mobile.jpg'},
							{width: 640, density: {multiplier: 2, prefixed: '@2x'}, device: 'mobile', filename: 'fghij-mobile@2x.jpg'},
							{width: 960, density: {multiplier: 3, prefixed: '@3x'}, device: 'mobile', filename: 'fghij-mobile@3x.jpg'}
						]
					}
				};
			});

			/**
			 *	Run the function
			 */
			Blaze.renderWithData(Template.cards_job, jobData, testParent);
			images = testParent.getElementsByTagName('img');

			/**
			 *	Running the tests
			 */
			expect(images.length).toEqual(2);
			expect(images[0].getAttribute('src')).toContain('abcde');
			expect(images[1].getAttribute('src')).toContain('fghij');

			/**
			 *	Finished
			 */
			setTimeout(done, 200);
		});
	});

	describe('helpers', function() {

		describe('images', function() {

			var job;

			beforeEach(function() {
				/**
				 *	Dummy data
				 */
				job = {
					fields: {
						projectLogos: [
							{
								sys: {
									id: 'dummy-2'
								}
							},
							{
								sys: {
									id: 'dummy-3'
								}
							}
						]
					}
				};
			});

			it('should call the find function with the correct selector on the images collection if the job has images associated with it.', function(done) {

				/**
				 *	Spies
				 */
				spyOn(Core.app.collections.images, 'find').and.callFake(function(selector) {
					return {
						fetch: function(){}
					};
				});

				/**
				 *	Call the function
				 */
				Template.cards_job.__helpers[' images'].call(job);

				/**
				 *	Run the test
				 */
				expect(Core.app.collections.images.find.calls.argsFor(0)).toEqual([{asset_id: {$in: ['dummy-2', 'dummy-3']}}]);

				/**
				 *	Done
				 */
				done();
			});

			it('should return an empty array if the job has no images', function(done) {

				/**
				 *	Setting up the data
				 */
				delete job.fields.projectLogos;

				/**
				 *	Run the function and the test
				 */
				expect(Template.cards_job.__helpers[' images'].call(job).length).toEqual(0);

				done();
			});

		});

	});
});<|endoftext|>


<commit_before>/* global HTMLElement, MutationObserver */
var registerElement = require('./a-register-element').registerElement;
var utils = require('../utils/');

/**
 * Base class for A-Frame that manages loading of objects.
 *
 * Nodes can be modified using mixins.
 * Nodes emit a `loaded` event when they and their children have initialized.
 */
module.exports = registerElement('a-node', {
  prototype: Object.create(HTMLElement.prototype, {
    createdCallback: {
      value: function () {
        this.hasLoaded = false;
        this.isNode = true;
        this.mixinEls = [];
        this.mixinObservers = {};
      },
      writable: window.debug
    },

    attachedCallback: {
      value: function () {
        var mixins = this.getAttribute('mixin');
        this.sceneEl = this.isScene ? this : this.closestScene();
        this.emit('nodeready', {}, false);
        if (mixins) { this.updateMixins(mixins); }
      },
      writable: window.debug
    },

    attributeChangedCallback: {
      value: function (attr, oldVal, newVal) {
        if (attr === 'mixin') { this.updateMixins(newVal, oldVal); }
      }
    },

   /**
    * Returns the first scene by traversing up the tree starting from and
    * including receiver element.
    */
    closestScene: {
      value: function closest () {
        var element = this;
        while (element) {
          if (element.isScene) { break; }
          element = element.parentElement;
        }
        return element;
      }
    },

    /**
     * Returns first element matching a selector by traversing up the tree starting
     * from and including receiver element.
     *
     * @param {string} selector - Selector of element to find.
     */
    closest: {
      value: function closest (selector) {
        var matches = this.matches || this.mozMatchesSelector ||
          this.msMatchesSelector || this.oMatchesSelector || this.webkitMatchesSelector;
        var element = this;
        while (element) {
          if (matches.call(element, selector)) { break; }
          element = element.parentElement;
        }
        return element;
      }
    },

    detachedCallback: {
      value: function () { /* no-op */ }
    },

    /**
     * Wait for children to load, if any.
     * Then emit `loaded` event and set `hasLoaded`.
     */
    load: {
      value: function (cb, childFilter) {
        var children;
        var childrenLoaded;
        var self = this;

        if (this.hasLoaded) { return; }

        // Default to waiting for all nodes.
        childFilter = childFilter || function (el) { return el.isNode; };

        // Wait for children to load (if any), then load.
        children = this.getChildren();
        childrenLoaded = children.filter(childFilter).map(function (child) {
          return new Promise(function waitForLoaded (resolve) {
            if (child.hasLoaded) { return resolve(); }
            child.addEventListener('loaded', resolve);
          });
        });

        Promise.all(childrenLoaded).then(function emitLoaded () {
          self.hasLoaded = true;
          if (cb) { cb(); }
          self.emit('loaded', {}, false);
        });
      },
      writable: true
    },

    getChildren: {
      value: function () {
        var children = [];
        for (var i = 0; i < this.children.length; i++) {
          children.push(this.children[i]);
        }
        return children;
      }
    },

    updateMixins: {
      value: function (newMixins, oldMixins) {
        var newMixinsIds = newMixins.split(' ');
        var oldMixinsIds = oldMixins ? oldMixins.split(' ') : [];
        // To determine what listeners will be removed
        var diff = oldMixinsIds.filter(function (i) { return newMixinsIds.indexOf(i) < 0; });
        this.mixinEls = [];
        diff.forEach(this.unregisterMixin.bind(this));
        newMixinsIds.forEach(this.registerMixin.bind(this));
      }
    },

    registerMixin: {
      value: function (mixinId) {
        if (!this.sceneEl) { return; }
        var mixinEl = this.sceneEl.querySelector('a-mixin#' + mixinId);
        if (!mixinEl) { return; }
        this.attachMixinListener(mixinEl);
        this.mixinEls.push(mixinEl);
      }
    },

    setAttribute: {
      value: function (attr, newValue) {
        if (attr === 'mixin') { this.updateMixins(newValue); }
        HTMLElement.prototype.setAttribute.call(this, attr, newValue);
      }
    },

    unregisterMixin: {
      value: function (mixinId) {
        var mixinEls = this.mixinEls;
        var mixinEl;
        var i;
        for (i = 0; i < mixinEls.length; ++i) {
          mixinEl = mixinEls[i];
          if (mixinId === mixinEl.id) {
            mixinEls.splice(i, 1);
            break;
          }
        }
        this.removeMixinListener(mixinId);
      }
    },

    removeMixinListener: {
      value: function (mixinId) {
        var observer = this.mixinObservers[mixinId];
        if (!observer) { return; }
        observer.disconnect();
        this.mixinObservers[mixinId] = null;
      }
    },

    attachMixinListener: {
      value: function (mixinEl) {
        var self = this;
        var mixinId = mixinEl.id;
        var currentObserver = this.mixinObservers[mixinId];
        if (!mixinEl) { return; }
        if (currentObserver) { return; }
        var observer = new MutationObserver(function (mutations) {
          var attr = mutations[0].attributeName;
          self.applyMixin(attr);
        });
        var config = { attributes: true };
        observer.observe(mixinEl, config);
        this.mixinObservers[mixinId] = observer;
      }
    },

    applyMixin: {
      value: function () { /* no-op */ }
    },

    /**
     * Emits a DOM event.
     *
     * @param {String} name
     *   Name of event (use a space-delimited string for multiple events).
     * @param {Object=} [detail={}]
     *   Custom data to pass as `detail` to the event.
     * @param {Boolean=} [bubbles=true]
     *   Whether the event should bubble.
     */
    emit: {
      value: function (name, detail, bubbles) {
        var self = this;
        detail = detail || {};
        if (bubbles === undefined) { bubbles = true; }
        var data = { bubbles: !!bubbles, detail: detail };
        return name.split(' ').map(function (eventName) {
          return utils.fireEvent(self, eventName, data);
        });
      }
    },

    /**
     * Returns a closure that emits a DOM event.
     *
     * @param {String} name
     *   Name of event (use a space-delimited string for multiple events).
     * @param {Object} detail
     *   Custom data (optional) to pass as `detail` if the event is to
     *   be a `CustomEvent`.
     * @param {Boolean} bubbles
     *   Whether the event should be bubble.
     */
    emitter: {
      value: function (name, detail, bubbles) {
        var self = this;
        return function () {
          self.emit(name, detail, bubbles);
        };
      }
    }
  })
});
<commit_msg>a-node getChildren shorter array syntax (#1706)<commit_after>/* global HTMLElement, MutationObserver */
var registerElement = require('./a-register-element').registerElement;
var utils = require('../utils/');

/**
 * Base class for A-Frame that manages loading of objects.
 *
 * Nodes can be modified using mixins.
 * Nodes emit a `loaded` event when they and their children have initialized.
 */
module.exports = registerElement('a-node', {
  prototype: Object.create(HTMLElement.prototype, {
    createdCallback: {
      value: function () {
        this.hasLoaded = false;
        this.isNode = true;
        this.mixinEls = [];
        this.mixinObservers = {};
      },
      writable: window.debug
    },

    attachedCallback: {
      value: function () {
        var mixins = this.getAttribute('mixin');
        this.sceneEl = this.isScene ? this : this.closestScene();
        this.emit('nodeready', {}, false);
        if (mixins) { this.updateMixins(mixins); }
      },
      writable: window.debug
    },

    attributeChangedCallback: {
      value: function (attr, oldVal, newVal) {
        if (attr === 'mixin') { this.updateMixins(newVal, oldVal); }
      }
    },

   /**
    * Returns the first scene by traversing up the tree starting from and
    * including receiver element.
    */
    closestScene: {
      value: function closest () {
        var element = this;
        while (element) {
          if (element.isScene) { break; }
          element = element.parentElement;
        }
        return element;
      }
    },

    /**
     * Returns first element matching a selector by traversing up the tree starting
     * from and including receiver element.
     *
     * @param {string} selector - Selector of element to find.
     */
    closest: {
      value: function closest (selector) {
        var matches = this.matches || this.mozMatchesSelector ||
          this.msMatchesSelector || this.oMatchesSelector || this.webkitMatchesSelector;
        var element = this;
        while (element) {
          if (matches.call(element, selector)) { break; }
          element = element.parentElement;
        }
        return element;
      }
    },

    detachedCallback: {
      value: function () { /* no-op */ }
    },

    /**
     * Wait for children to load, if any.
     * Then emit `loaded` event and set `hasLoaded`.
     */
    load: {
      value: function (cb, childFilter) {
        var children;
        var childrenLoaded;
        var self = this;

        if (this.hasLoaded) { return; }

        // Default to waiting for all nodes.
        childFilter = childFilter || function (el) { return el.isNode; };

        // Wait for children to load (if any), then load.
        children = this.getChildren();
        childrenLoaded = children.filter(childFilter).map(function (child) {
          return new Promise(function waitForLoaded (resolve) {
            if (child.hasLoaded) { return resolve(); }
            child.addEventListener('loaded', resolve);
          });
        });

        Promise.all(childrenLoaded).then(function emitLoaded () {
          self.hasLoaded = true;
          if (cb) { cb(); }
          self.emit('loaded', {}, false);
        });
      },
      writable: true
    },

    getChildren: {
      value: function () {
        return Array.prototype.slice.call(this.children, 0);
      }
    },

    updateMixins: {
      value: function (newMixins, oldMixins) {
        var newMixinsIds = newMixins.split(' ');
        var oldMixinsIds = oldMixins ? oldMixins.split(' ') : [];
        // To determine what listeners will be removed
        var diff = oldMixinsIds.filter(function (i) { return newMixinsIds.indexOf(i) < 0; });
        this.mixinEls = [];
        diff.forEach(this.unregisterMixin.bind(this));
        newMixinsIds.forEach(this.registerMixin.bind(this));
      }
    },

    registerMixin: {
      value: function (mixinId) {
        if (!this.sceneEl) { return; }
        var mixinEl = this.sceneEl.querySelector('a-mixin#' + mixinId);
        if (!mixinEl) { return; }
        this.attachMixinListener(mixinEl);
        this.mixinEls.push(mixinEl);
      }
    },

    setAttribute: {
      value: function (attr, newValue) {
        if (attr === 'mixin') { this.updateMixins(newValue); }
        HTMLElement.prototype.setAttribute.call(this, attr, newValue);
      }
    },

    unregisterMixin: {
      value: function (mixinId) {
        var mixinEls = this.mixinEls;
        var mixinEl;
        var i;
        for (i = 0; i < mixinEls.length; ++i) {
          mixinEl = mixinEls[i];
          if (mixinId === mixinEl.id) {
            mixinEls.splice(i, 1);
            break;
          }
        }
        this.removeMixinListener(mixinId);
      }
    },

    removeMixinListener: {
      value: function (mixinId) {
        var observer = this.mixinObservers[mixinId];
        if (!observer) { return; }
        observer.disconnect();
        this.mixinObservers[mixinId] = null;
      }
    },

    attachMixinListener: {
      value: function (mixinEl) {
        var self = this;
        var mixinId = mixinEl.id;
        var currentObserver = this.mixinObservers[mixinId];
        if (!mixinEl) { return; }
        if (currentObserver) { return; }
        var observer = new MutationObserver(function (mutations) {
          var attr = mutations[0].attributeName;
          self.applyMixin(attr);
        });
        var config = { attributes: true };
        observer.observe(mixinEl, config);
        this.mixinObservers[mixinId] = observer;
      }
    },

    applyMixin: {
      value: function () { /* no-op */ }
    },

    /**
     * Emits a DOM event.
     *
     * @param {String} name
     *   Name of event (use a space-delimited string for multiple events).
     * @param {Object=} [detail={}]
     *   Custom data to pass as `detail` to the event.
     * @param {Boolean=} [bubbles=true]
     *   Whether the event should bubble.
     */
    emit: {
      value: function (name, detail, bubbles) {
        var self = this;
        detail = detail || {};
        if (bubbles === undefined) { bubbles = true; }
        var data = { bubbles: !!bubbles, detail: detail };
        return name.split(' ').map(function (eventName) {
          return utils.fireEvent(self, eventName, data);
        });
      }
    },

    /**
     * Returns a closure that emits a DOM event.
     *
     * @param {String} name
     *   Name of event (use a space-delimited string for multiple events).
     * @param {Object} detail
     *   Custom data (optional) to pass as `detail` if the event is to
     *   be a `CustomEvent`.
     * @param {Boolean} bubbles
     *   Whether the event should be bubble.
     */
    emitter: {
      value: function (name, detail, bubbles) {
        var self = this;
        return function () {
          self.emit(name, detail, bubbles);
        };
      }
    }
  })
});
<|endoftext|>


<commit_before>const SteamCommunity = require('../index.js');

const Helpers = require('./helpers.js');

/**
 * Restore a previously removed steam package from your steam account.
 * @param {int|string} packageID
 * @param {function} callback
 */
SteamCommunity.prototype.restorePackage = function(packageID, callback) {
	this.httpRequestPost({
		"uri": "https://help.steampowered.com/wizard/AjaxDoPackageRestore",
		"form": {
			"packageid": packageID,
			"sessionid": this.getSessionID('https://help.steampowered.com'),
			"wizard_ajax": 1
		},
		"json": true
	}, (err, res, body) => {
		if (err) {
			callback(err);
			return;
		}

		if (!body.success) {
			callback(body.errorMsg ? new Error(body.errorMsg) : Helpers.eresultError(body.success));
			return;
		}

		callback(null);
	});
};

/**
 * Remove a steam package from your steam account.
 * @param {int|string} packageID
 * @param {function} callback
 */
SteamCommunity.prototype.removePackage = function(packageID, callback) {
	this.httpRequestPost({
		"uri": "https://help.steampowered.com/wizard/AjaxDoPackageRemove",
		"form": {
			"packageid": packageID,
			"sessionid": this.getSessionID('https://help.steampowered.com'),
			"wizard_ajax": 1
		},
		"json": true
	}, (err, res, body) => {
		if (err) {
			callback(err);
			return;
		}

		if (!body.success) {
			callback(body.errorMsg ? new Error(body.errorMsg) : Helpers.eresultError(body.success));
			return;
		}

		callback(null);
	});
};
<commit_msg>Make callback optional in removePackage/restorePackage<commit_after>const SteamCommunity = require('../index.js');

const Helpers = require('./helpers.js');

/**
 * Restore a previously removed steam package from your steam account.
 * @param {int|string} packageID
 * @param {function} callback
 */
SteamCommunity.prototype.restorePackage = function(packageID, callback) {
	this.httpRequestPost({
		"uri": "https://help.steampowered.com/wizard/AjaxDoPackageRestore",
		"form": {
			"packageid": packageID,
			"sessionid": this.getSessionID('https://help.steampowered.com'),
			"wizard_ajax": 1
		},
		"json": true
	}, (err, res, body) => {
		if (!callback) {
			return;
		}

		if (err) {
			callback(err);
			return;
		}

		if (!body.success) {
			callback(body.errorMsg ? new Error(body.errorMsg) : Helpers.eresultError(body.success));
			return;
		}

		callback(null);
	});
};

/**
 * Remove a steam package from your steam account.
 * @param {int|string} packageID
 * @param {function} callback
 */
SteamCommunity.prototype.removePackage = function(packageID, callback) {
	this.httpRequestPost({
		"uri": "https://help.steampowered.com/wizard/AjaxDoPackageRemove",
		"form": {
			"packageid": packageID,
			"sessionid": this.getSessionID('https://help.steampowered.com'),
			"wizard_ajax": 1
		},
		"json": true
	}, (err, res, body) => {
		if (!callback) {
			return;
		}

		if (err) {
			callback(err);
			return;
		}

		if (!body.success) {
			callback(body.errorMsg ? new Error(body.errorMsg) : Helpers.eresultError(body.success));
			return;
		}

		callback(null);
	});
};
<|endoftext|>


<commit_before>console.log( "- easyscan.js START" );

var esyscn = new function() {
  /* namespaces easyscan js
   *
   * See <http://stackoverflow.com/a/881611>.
   * Only check_already_run() can be called publicly, and only via ```esyscn.check_already_run();```.
   */

  var cell_position_map = { "location": 0, "call_number": 1, "barcode": 2, "availability": 3 };

  this.check_already_run = function() {
    /* Checks to see if javascript has already been run.
     * Called by document.ready()
     */
    var all_html = $("body").html().toString();  // jquery already loaded (whew)
    var index = all_html.indexOf( "Request Scan" );
    if (index != -1) {
      console.log( "- aready run" );
    } else {
      console.log( "- not already run" );
    grab_title();
    }
  }

  var grab_title = function() {
    /* Grabs bib title; then continues processing.
     * Called by check_already_run()
     */
    var title = null;
    title_objs = $("tr").find("td").find("strong");
    if ( title_objs.length > 0 ) {
      title_obj = title_objs[0];
      title = title_obj.textContent.trim();
    }
    console.log( "- title, " + title );
    process_item_table( title );
  }

  var process_item_table = function( title ) {
    /* Updates bib-item list to show request-scan button.
     * Called by grab_title()
     */
    rows = $( ".bibItemsEntry" );
    for (var i = 0; i < rows.length; i++) {
      row = rows[i];
      row_dict = extract_row_data( row.getElementsByTagName("td") );
      if ( evaluate_row_data(row_dict)["show_scan_button"] == true ) {
        console.log( "- continuing row procesing" );
        update_row( title, row_dict )
      }
      row.deleteCell( cell_position_map["barcode"] );
    }
    delete_header_cell();
  }

  var extract_row_data = function( cells ) {
    /* Takes row dom-object; extracts and returns fielded data.
     * It runs through the labels of the `var cell_position_map` dict, and builds a row_data dict:
     *   each key is the label; each value is the correct cell's text.
     * Called by process_item_table()
     */
    var row_data = {}
    var map_keys = Object.keys( cell_position_map );  // yeilds [ "location", "call_number", etc. ] - compatible with older browsers?
    for (var i = 0; i < map_keys.length; i++) {
      var key = map_keys[i];
      var value = cells[ cell_position_map[key] ].textContent.trim();
      if ( key == "barcode" ) { value = value.split(" ").join(""); } // removes whitespaces between digits.
      row_data[key] = value;
    }
    console.log( "- row_data, " + JSON.stringify(row_data, null, 4) );
    return row_data;
  }

  var evaluate_row_data = function( row_dict ) {
    /* Evaluates whether 'Request Scan' button should appear; returns boolean.
     * Called by process_item_table()
     */
    var row_evaluation = { "show_scan_button": false };
    if ( (row_dict["location"] == "ANNEX") && (row_dict["availability"] == "AVAILABLE") ) {
        row_evaluation = { "show_scan_button": true };
    }
    console.log( "- row_evaluation, " + row_evaluation );
    return row_evaluation;
  }

  var update_row = function( title, row_dict ) {
    /* Adds `Request Scan` link to row html.
     * Called by process_item_table()
     */
    link_html = build_link_html( title, row_dict )
    last_cell = row.getElementsByTagName("td")[cell_position_map["availability"]];
    $( last_cell ).after( link_html );
    console.log( "- request-scan link added" );
    return;
  }

  var build_link_html = function( title, row_dict ) {
    /* Takes row dict; returns html link.
     * Called by extract_row_data()
     */
    link = 'Request Scan';
    link = link.replace( "THECALLNUMBER", row_dict["call_number"] );
    link = link.replace( "THEBARCODE", row_dict["barcode"] );
    link = link.replace( "THETITLE", title );
    console.log( "- link end, " + link );
    return link;
  }

  var delete_header_cell = function() {
    /* Deletes barcode header cell
     * Called by process_item_table()
     */
    var header_row = $( "tr.bibItemsHeader" )[0];
    console.log( "- header_row, " + header_row );
    if ( typeof header_row == "undefined" ) {
      return; }
    header_row.deleteCell( cell_position_map["barcode"] );
    console.log( "- barcode header cell deleted" );
  }

};  // end namespace esyscn, ```var esyscn = new function() {```

$(document).ready(
  function() {
    esyscn.check_already_run();
  }

);

console.log( "- easyscan.js END" );

// JSON.stringify(obj, null, 4)
<commit_msg>updates table-read to accommodate BB changes.<commit_after>console.log( "- easyscan.js START" );

var esyscn = new function() {
  /* Namespaces this file's function calls.
   *
   * See <http://stackoverflow.com/a/881611> for module-pattern reference.
   * Only check_already_run() can be called publicly, and only via ```esyscn.check_already_run();```.
   */

  var cell_position_map = { "location": 0, "call_number": 1, "availability": 2, "barcode": 3 };

  this.check_already_run = function() {
    /* Checks to see if javascript has already been run.
     * Called by document.ready()
     */
    var all_html = $("body").html().toString();  // jquery already loaded (whew)
    var index = all_html.indexOf( "Request Scan" );
    if (index != -1) {
      console.log( "- aready run" );
    } else {
      console.log( "- not already run" );
    grab_title();
    }
  }

  var grab_title = function() {
    /* Grabs bib title; then continues processing.
     * Called by check_already_run()
     */
    var title = null;
    title_objs = $("tr").find("td").find("strong");
    if ( title_objs.length > 0 ) {
      title_obj = title_objs[0];
      title = title_obj.textContent.trim();
    }
    console.log( "- title, " + title );
    process_item_table( title );
  }

  var process_item_table = function( title ) {
    /* Updates bib-item list to show request-scan button.
     * Called by grab_title()
     */
    rows = $( ".bibItemsEntry" );
    for (var i = 0; i < rows.length; i++) {
      row = rows[i];
      row_dict = extract_row_data( row.getElementsByTagName("td") );
      if ( evaluate_row_data(row_dict)["show_scan_button"] == true ) {
        console.log( "- continuing row procesing" );
        update_row( title, row_dict )
      }
      row.deleteCell( cell_position_map["barcode"] );
    }
    delete_header_cell();
  }

  var extract_row_data = function( cells ) {
    /* Takes row dom-object; extracts and returns fielded data.
     * It runs through the labels of the `var cell_position_map` dict, and builds a row_data dict:
     *   each key is the label; each value is the correct cell's text.
     * Called by process_item_table()
     */
    var row_data = {}
    var map_keys = Object.keys( cell_position_map );  // yeilds [ "location", "call_number", etc. ] - compatible with older browsers?
    for (var i = 0; i < map_keys.length; i++) {
      var key = map_keys[i];
      var value = cells[ cell_position_map[key] ].textContent.trim();
      if ( key == "barcode" ) { value = value.split(" ").join(""); } // removes whitespaces between digits.
      row_data[key] = value;
    }
    console.log( "- row_data, " + JSON.stringify(row_data, null, 4) );
    return row_data;
  }

  var evaluate_row_data = function( row_dict ) {
    /* Evaluates whether 'Request Scan' button should appear; returns boolean.
     * Called by process_item_table()
     */
    var row_evaluation = { "show_scan_button": false };
    if ( (row_dict["location"] == "ANNEX") && (row_dict["availability"] == "AVAILABLE") ) {
        row_evaluation = { "show_scan_button": true };
    }
    console.log( "- row_evaluation, " + row_evaluation );
    return row_evaluation;
  }

  var update_row = function( title, row_dict ) {
    /* Adds `Request Scan` link to row html.
     * Called by process_item_table()
     */
    link_html = build_link_html( title, row_dict )
    last_cell = row.getElementsByTagName("td")[cell_position_map["availability"]];
    $( last_cell ).after( link_html );
    console.log( "- request-scan link added" );
    return;
  }

  var build_link_html = function( title, row_dict ) {
    /* Takes row dict; returns html link.
     * Called by extract_row_data()
     */
    link = 'Request Scan';
    link = link.replace( "THECALLNUMBER", row_dict["call_number"] );
    link = link.replace( "THEBARCODE", row_dict["barcode"] );
    link = link.replace( "THETITLE", title );
    console.log( "- link end, " + link );
    return link;
  }

  var delete_header_cell = function() {
    /* Deletes barcode header cell
     * Called by process_item_table()
     */
    var header_row = $( "tr.bibItemsHeader" )[0];
    console.log( "- header_row, " + header_row );
    if ( typeof header_row == "undefined" ) {
      return; }
    header_row.deleteCell( cell_position_map["barcode"] );
    console.log( "- barcode header cell deleted" );
  }

};  // end namespace esyscn, ```var esyscn = new function() {```

$(document).ready(
  function() {
    esyscn.check_already_run();
  }

);

console.log( "- easyscan.js END" );

// JSON.stringify(obj, null, 4)
<|endoftext|>


<commit_before>ROOT = process.cwd()
HELPERS = require( ROOT + '/helpers/general.js' );
log = HELPERS.log
var config = require( ROOT + '/config.json' );

var fs = require( 'fs' );

var DeviceHandler = require( ROOT + '/Models/devicesHandler' );
var DEVICE_HANDLER = new DeviceHandler();

var ACTION_INSTANCE_DATA_HANDLER = new( require( ROOT + '/Models/actionInstanceData' ) );

this.showIndexView = function( req, res ) {
	res.render( 'index', {
		'locals': {
			'title': config.app_name,
			'marketingMode': config.web_console.marketing_mode,
			'hostName': config.server.host,
			'pubsubPort': config.services.ojsConsole.port,
		}
	} );

};

this.getActionMetadata = function( req, res ) {
	var actionName = req.params.actionName;
	log( 'metadata for: ' + actionName );
	ACTION_INSTANCE_DATA_HANDLER.findactionInstanceData( actionName, function( err, metadata ) {

		if ( err ) {
			res.writeHead( 500, {
				'Content-Type': 'text/plain'
			} );
			res.write( err + '\n' );
			res.end();
		} else if ( metadata == null || metadata == undefined || metadata.args == null || metadata.args == undefined ) {
			res.writeHead( 404, {
				"Content-Type": "application/json"
			} );
			res.write( 'No metadata found for ' + actionName );
			res.end();

		} else {

			var arguments = JSON.stringify( metadata.args );
			arguments = JSON.parse( arguments );

			res.writeHead( 200, {
				"Content-Type": "application/json"
			} );
			res.write(
				JSON.stringify( {
					'args': metadata.args
				} )
			);
			res.end();
		}
	} );
};

this.getActions = function( req, res ) {
	var actionsPath = ROOT + '/resources/actions/';
	fs.readdir( actionsPath, function( err, files ) {
		if ( err ) {
			log( err );
			res.writeHead( 500, {
				'Content-Type': 'text/plain'
			} );
			res.write( err + '\n' );
			res.end();
		} else {

			var fileNames = [];
			for ( var i = 0; i < files.length; i++ ) {
				var file = files[ i ];
				if ( file[ 0 ] == '.' || file.slice( -3 ) != '.js' || file == 'misc.js' ) {
					continue;
				}
				fileNames.push( file.replace( '.js', '' ) );
			}
			res.writeHead( 200, {
				"Content-Type": "application/json"
			} );
			res.write(
				JSON.stringify( {
					//'actions': fileNames.sort()
					'files': fileNames.sort()
				} )
			);
			res.end();
		}
	} );
};

this.getAction = function( req, res ) {
	var actionName = req.params.actionName;
	fs.readFile( ROOT + '/resources/actions/' + actionName + '.js', 'binary', function( error, file ) {

		if ( error ) {
			res.writeHead( 500, {
				'Content-Type': 'text/plain'
			} );
			res.write( error + '\n' );
			res.end();
		} else {
			res.writeHead( 200, {
				'Content-Type': 'text/plain'
			} );
			res.write( file, 'binary' );
			res.end();
		}
	} );

};

this.getCapabilities = function( req, res ) {
	fs.readdir( ROOT + config.resources.capabilities, function( err, files ) {
		if ( err ) {
			log( err );
			res.writeHead( 500, {
				'Content-Type': 'text/plain'
			} );
			res.write( err + '\n' );
			res.end();
		} else {

			var fileNames = [];
			for ( var i = 0; i < files.length; i++ ) {
				var file = files[ i ];
				if ( file[ 0 ] == '.' || file.slice( -3 ) != '.js' || file == 'misc.js' ) {
					continue;
				}
				fileNames.push( file.replace( '.js', '' ) );
			}
			res.writeHead( 200, {
				"Content-Type": "application/json"
			} );
			res.write(
				JSON.stringify( {
					//'capabilities': fileNames.sort()
					files: fileNames.sort(),
					capabilities: fileNames.sort(),
					deviceTypes: config.device_types
				} )
			);
			res.end();
		}
	} );
};

this.getCapability = function( req, res ) {
	var capabilityName = req.params.capabilityName;
	log( capabilityName );

	fs.readFile( ROOT + config.resources.capabilities + capabilityName + '.js', 'binary', function( error, file ) {
		if ( error ) {
			res.writeHead( 500, {
				'Content-Type': 'text/plain'
			} );
			res.write( error + '\n' );
			res.end();
		} else {
			res.writeHead( 200, {
				'Content-Type': 'text/plain'
			} );
			res.write( file, 'binary' );
			res.end();
		}
	} );

};

this.getDownloads = function( req, res ) {
	fs.readdir( ROOT + config.clients_path, function( err, files ) {
		if ( err ) {
			log( err );
			res.writeHead( 500, {
				'Content-Type': 'text/plain'
			} );
			res.write( err + '\n' );
			res.end();
		} else {

			var fileNames = [];
			for ( var i = 0; i < files.length; i++ ) {
				var file = files[ i ];
				if ( file[ 0 ] == '.' || file.slice( -4 ) != '.apk' ) {
					continue;
				}
				fileNames.push( file );
			}
			res.writeHead( 200, {
				"Content-Type": "application/json"
			} );
			res.write(
				JSON.stringify( {
					'client_app_names': fileNames.sort()
				} )
			);
			res.end();
		}
	} );
};

this.getDownload = function( req, res ) {
	var clientName = req.params.clientName;
	fs.readFile( ROOT + config.clients_path + clientName, 'binary', function( error, file ) {
		if ( error ) {
			res.writeHead( 500, {
				'Content-Type': 'text/plain'
			} );
			res.write( error + '\n' );
			res.end();
		} else {
			res.writeHead( 200, {
				'Content-Type': 'text/plain'
			} );
			res.write( file, 'binary' );
			res.end();
		}
	} );
};<commit_msg>hostname issue in console<commit_after>ROOT = process.cwd()
HELPERS = require( ROOT + '/helpers/general.js' );
log = HELPERS.log
var config = require( ROOT + '/config.json' );

var fs = require( 'fs' );

var DeviceHandler = require( ROOT + '/Models/devicesHandler' );
var DEVICE_HANDLER = new DeviceHandler();

var ACTION_INSTANCE_DATA_HANDLER = new( require( ROOT + '/Models/actionInstanceData' ) );

this.showIndexView = function( req, res ) {
	res.render( 'index', {
		'locals': {
			'title': config.app_name,
			'marketingMode': config.web_console.marketing_mode,
			'hostName': req.headers.host, //config.server.host,
			'pubsubPort': config.services.ojsConsole.port,
		}
	} );

};

this.getActionMetadata = function( req, res ) {
	var actionName = req.params.actionName;
	log( 'metadata for: ' + actionName );
	ACTION_INSTANCE_DATA_HANDLER.findactionInstanceData( actionName, function( err, metadata ) {

		if ( err ) {
			res.writeHead( 500, {
				'Content-Type': 'text/plain'
			} );
			res.write( err + '\n' );
			res.end();
		} else if ( metadata == null || metadata == undefined || metadata.args == null || metadata.args == undefined ) {
			res.writeHead( 404, {
				"Content-Type": "application/json"
			} );
			res.write( 'No metadata found for ' + actionName );
			res.end();

		} else {

			var arguments = JSON.stringify( metadata.args );
			arguments = JSON.parse( arguments );

			res.writeHead( 200, {
				"Content-Type": "application/json"
			} );
			res.write(
				JSON.stringify( {
					'args': metadata.args
				} )
			);
			res.end();
		}
	} );
};

this.getActions = function( req, res ) {
	var actionsPath = ROOT + '/resources/actions/';
	fs.readdir( actionsPath, function( err, files ) {
		if ( err ) {
			log( err );
			res.writeHead( 500, {
				'Content-Type': 'text/plain'
			} );
			res.write( err + '\n' );
			res.end();
		} else {

			var fileNames = [];
			for ( var i = 0; i < files.length; i++ ) {
				var file = files[ i ];
				if ( file[ 0 ] == '.' || file.slice( -3 ) != '.js' || file == 'misc.js' ) {
					continue;
				}
				fileNames.push( file.replace( '.js', '' ) );
			}
			res.writeHead( 200, {
				"Content-Type": "application/json"
			} );
			res.write(
				JSON.stringify( {
					//'actions': fileNames.sort()
					'files': fileNames.sort()
				} )
			);
			res.end();
		}
	} );
};

this.getAction = function( req, res ) {
	var actionName = req.params.actionName;
	fs.readFile( ROOT + '/resources/actions/' + actionName + '.js', 'binary', function( error, file ) {

		if ( error ) {
			res.writeHead( 500, {
				'Content-Type': 'text/plain'
			} );
			res.write( error + '\n' );
			res.end();
		} else {
			res.writeHead( 200, {
				'Content-Type': 'text/plain'
			} );
			res.write( file, 'binary' );
			res.end();
		}
	} );

};

this.getCapabilities = function( req, res ) {
	fs.readdir( ROOT + config.resources.capabilities, function( err, files ) {
		if ( err ) {
			log( err );
			res.writeHead( 500, {
				'Content-Type': 'text/plain'
			} );
			res.write( err + '\n' );
			res.end();
		} else {

			var fileNames = [];
			for ( var i = 0; i < files.length; i++ ) {
				var file = files[ i ];
				if ( file[ 0 ] == '.' || file.slice( -3 ) != '.js' || file == 'misc.js' ) {
					continue;
				}
				fileNames.push( file.replace( '.js', '' ) );
			}
			res.writeHead( 200, {
				"Content-Type": "application/json"
			} );
			res.write(
				JSON.stringify( {
					//'capabilities': fileNames.sort()
					files: fileNames.sort(),
					capabilities: fileNames.sort(),
					deviceTypes: config.device_types
				} )
			);
			res.end();
		}
	} );
};

this.getCapability = function( req, res ) {
	var capabilityName = req.params.capabilityName;
	log( capabilityName );

	fs.readFile( ROOT + config.resources.capabilities + capabilityName + '.js', 'binary', function( error, file ) {
		if ( error ) {
			res.writeHead( 500, {
				'Content-Type': 'text/plain'
			} );
			res.write( error + '\n' );
			res.end();
		} else {
			res.writeHead( 200, {
				'Content-Type': 'text/plain'
			} );
			res.write( file, 'binary' );
			res.end();
		}
	} );

};

this.getDownloads = function( req, res ) {
	fs.readdir( ROOT + config.clients_path, function( err, files ) {
		if ( err ) {
			log( err );
			res.writeHead( 500, {
				'Content-Type': 'text/plain'
			} );
			res.write( err + '\n' );
			res.end();
		} else {

			var fileNames = [];
			for ( var i = 0; i < files.length; i++ ) {
				var file = files[ i ];
				if ( file[ 0 ] == '.' || file.slice( -4 ) != '.apk' ) {
					continue;
				}
				fileNames.push( file );
			}
			res.writeHead( 200, {
				"Content-Type": "application/json"
			} );
			res.write(
				JSON.stringify( {
					'client_app_names': fileNames.sort()
				} )
			);
			res.end();
		}
	} );
};

this.getDownload = function( req, res ) {
	var clientName = req.params.clientName;
	fs.readFile( ROOT + config.clients_path + clientName, 'binary', function( error, file ) {
		if ( error ) {
			res.writeHead( 500, {
				'Content-Type': 'text/plain'
			} );
			res.write( error + '\n' );
			res.end();
		} else {
			res.writeHead( 200, {
				'Content-Type': 'text/plain'
			} );
			res.write( file, 'binary' );
			res.end();
		}
	} );
};<|endoftext|>


<commit_before>angular.module('app.controllers').controller('home', function ($scope, $timeout, socialActivity, homeCtrlParams,
        profile, activity, groups, flurry, $ionicScrollDelegate) {

  flurry.log('news feed');

  $scope.filter = homeCtrlParams.filter;

  $scope.isLoadMore = false;

  var activityCollection = activity.getActivities();

  function getActivities() {
    if(homeCtrlParams.filter.selectedGroup)
      $scope.activities = homeCtrlParams.filter.selectedGroup.activities
    else
      $scope.activities = activityCollection.getFilteredModels();
  }

  function getUnansweredCount(activities) {
    return _(activities).reduce(function (memo, activity) {
      return (activity.get('answered') || activity.get('closed') || activity.get('ignore_count')) ? memo : ++memo;
    }, 0);
  }

  function setFiltersData() {
    homeCtrlParams.filter.groups = groups.getGroupsOptions();
    _(homeCtrlParams.filter.groups).each(function (group) {
      group.activities = activityCollection.getFilteredModels(group);
      group.unansweredCount = getUnansweredCount(group.activities);
      group.read = !_.some(group.activities, function (item) {
        return !item.get('read');
      });
      if (homeCtrlParams.filter.selectedGroup && homeCtrlParams.filter.selectedGroup.id === group.id) {
        homeCtrlParams.filter.selectedGroup = group;
      }
    });
    homeCtrlParams.filter.unansweredCount = getUnansweredCount(activityCollection.getFilteredModels());
  }

  function prepare() {
    homeCtrlParams.loaded = true;
    activityCollection.setDeferredRead().sort();
    setFiltersData();

    $scope.loading = false;
    $ionicScrollDelegate.resize();

    activity.saveRead();
  }

  function loadActivities(loadType) {
    var prevSize = activityCollection.size();
    activity.load(loadType).then(function () {
      if (loadType === 'append' && prevSize === activityCollection.size()) {
        $scope.isLoadMore = false;
      } else {
        $scope.isLoadMore = true;
      }
      prepare();
      $scope.$emit('home.activities-reloaded');
      $scope.$broadcast('scroll.refreshComplete');
      $scope.$broadcast('scroll.infiniteScrollComplete');
    }, prepare).finally(socialActivity.load);
  }

  $scope.togglePostWindow = function () {
    $scope.showPostWindow = !$scope.showPostWindow;
    $scope.execApply();
  };

  $scope.newPost = function (type) {
    var types = {
      1: 'long petition',
      2: 'quorum'
    };
    $scope.path('/micro-petitions/add/' + types[type] + '/' +
            (homeCtrlParams.filter.selectedGroup ? homeCtrlParams.filter.selectedGroup.id : ''));
    $scope.showPostWindow = false;
  };

  $scope.filterLineStep = function () {
    return 5;
  };

  $scope.steps = function () {
    var items = homeCtrlParams.filter.groups.length - 3;
    items = items > 0 ? items : 0;
    var step = $scope.filterLineStep();
    var stepsCount = Math.ceil(items / step);
    var steps = [3];
    for (var i = 1; i < stepsCount; i++) {
      steps.push(i * step + 3);
    }
    if (items && 0 === items % step) {
      steps.push(steps[steps.length - 1] + step);
    }
    return steps;
  };

  $scope.loadMoreActivities = function () {
    loadActivities('append');
  };

  $scope.pullToRefresh = function () {
    loadActivities('clearAndLoad');
  };

  $scope.$watch('filter.selectedGroup', getActivities);
  $scope.$watch('loading', function () {
    if ($scope.loading) {
      $scope.showSpinner();
    } else if ($scope.loading === false) {
      $scope.hideSpinner();
    }
  });

  $scope.$on('notification.received', function () {
    loadActivities('refresh');
  });

  $scope.$on('activity.reload', function () {
    loadActivities('refresh');
  });

  //move scroll to top when filter is changed
  $scope.$watch('filter.selectedGroup', function (nVal) {
    if (typeof (nVal) !== undefined) {
      $timeout(function () {
        $ionicScrollDelegate.resize();
        $ionicScrollDelegate.scrollTop();
      });
    }
  });

  //call this when this view is loaded because this view is cached
  $scope.$on('$ionicView.enter', function () {
    if (!profile.get()) {
      profile.load();
    }

    if (!homeCtrlParams.loaded) {
      if (activityCollection.size() === 0) {
        $scope.isLoadMore = true;
      } else {
        loadActivities('refresh');
      }
    } else {
      prepare();
    }

  });
  //call this because cache may be loaded
  setFiltersData();
  getActivities();
});

angular.module('app.controllers').run(function (homeCtrlParams, $document, $rootScope) {
  $document.bind('resume', function () {
    homeCtrlParams.loaded = false;
    $rootScope.$broadcast('activity.reload');
    $rootScope.execApply();
  });
});

angular.module('app.controllers').controller('preload', function (topBar, session, profile, $location) {
  if (session.token) {
    if (session.is_registration_complete) {
      profile.load().then(function () {
        profile.checkRemind();
      });
      if (!$location.path() || '/' === $location.path() || '/preload' === $location.path()) {
        $location.path('/main');
      }
    } else {
      $location.path('/profile');
    }
  } else {
    $location.path('/login');
  }
});

angular.module('app.controllers').directive('iActivity', function ($rootScope, questions, petitions, discussion, elapsedFilter, follows, session, iParse, $sce) {

  function eventCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/event.html';
  }

  function newsCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/news.html';
    $scope.entity = $scope.activity.get('entity');
    $scope.rate = function (action) {
      $scope.sending = true;
      discussion.loadRoot('poll', $scope.entity.id).then(function (comment) {
        discussion.rate(comment, action).then(function (comment) {
          $scope.activity.set('rate_up', comment.rate_up).set('rate_down', comment.rate_down);
          $scope.sending = false;
        }, function () {
          $scope.sending = false;
        });
      });
    };
  }

  function paymentCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/payment.html';
  }

  function postCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/post.html';
    $scope.currentUserIsActivityOwner = $scope.activity.get('owner').id == session.user_id
    $scope.booster = $scope.activity.get('owner').type === 'group' ? 100 : $scope.activity.getQuorumCompletedPercent();
    var follow = follows.getByUserId($scope.activity.get('owner').id);
    $scope.followable = !follow.isFollow();
    if ($scope.followable && follow.isApproved()) {
      $scope.isFollowApproved = true;
    }
    $scope.isFollowShow = follows.loaded && follow.get('user').id !== session.user_id;
    $scope.sign = function (optionId) {
      $scope.sending = true;
      petitions.answer($scope.activity.get('entity').id, optionId).then(function (answer) {
        $scope.activity.set('answer', answer).set('answered', true);
        $scope.sending = false;
        if (optionId === 1) {
          $scope.showToast('Post upvoted!');
        }
        if (optionId === 2) {
          $scope.showToast('Post downvoted!');
        }
      });
    };
    $scope.unsign = function () {
      $scope.sending = true;
      petitions.unsign($scope.activity.get('entity').id, $scope.activity.get('answer').option_id).then(function () {
        $scope.activity.set('answered', false).set('answer', null);
        $scope.sending = false;
      });
    };
    $scope.followOwner = function () {
      $scope.sending = true;
      follow.follow().then(function () {
        $scope.activity.followable = false;
        $scope.sending = false;
        $scope.showToast('Follow request sent!');
      });
    };
    $scope.showToast = $rootScope.showToast;
  }

  function petitionCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/petition.html';
    $scope.answer = function () {
      $scope.sending = true;
      if ($scope.answerAction === 'sign') {
        $scope.sign();
      } else {
        $scope.unsign();
      }
      $scope.answerAction = '';
    };
    $scope.$watch(function () {
      return $scope.activity.get('answer');
    }, function (answer) {
      $scope.answerAction = answer ? 'unsign' : 'sign';
    });
    $scope.sign = function () {
      questions.load($scope.activity.get('entity').id).then(function (question) {
        question.answer({
          privacy: 0,
          comment: '',
          option_id: question.options[0].id
        }).then(function (answer) {
          $scope.activity.set('answer', answer).set('answered', true);
          $scope.sending = false;
        });
      });
    };
    $scope.unsign = function () {
      $scope.answerAction = '';
      var answer = $scope.activity.get('answer');
      questions.unsignFromPetition(answer.question.id, answer.option_id).then(function () {
        $scope.activity.set('answer', null).set('answered', false);
        $scope.sending = false;
      });
    };
  }

  function questionCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/question.html';
  }

  var ctrlByType = {
    'leader-event': eventCtrl,
    'leader-news': newsCtrl,
    'crowdfunding-payment-request': paymentCtrl,
    'payment-request': paymentCtrl,
    'micro-petition': postCtrl,
    'petition': petitionCtrl,
    'question': questionCtrl
  };

  return {
    scope: {
      activity: '='
    },
    restrict: 'E',
    template: '<ng-include src="templateSrc"></ng-include>',
    controller: function ($scope) {
      $scope.navigateTo = $rootScope.navigateTo;
      $scope.navigateToActivity = function (activity, focus, e) {
        activity.setRead();
        if (e && e.target.tagName.toLowerCase() === 'hash-tag') {
          $rootScope.openTag(angular.element(e.target).text());
        } else {
          $rootScope.navigateTo('activity', activity, focus);
        }
      };

      $scope.title = $scope.activity.get('title');
      $scope.description = $sce.trustAsHtml(iParse.wrapHashTags($scope.activity.get('description')));
      $scope.avatar_file_path = $scope.activity.get('owner').avatar_file_path;
      $scope.iconClass = $scope.activity.getIcon();
      $scope.official_title = $scope.activity.get('owner').official_title;
      $scope.full_name = [$scope.activity.get('owner').first_name, $scope.activity.get('owner').last_name].join(' ');
      $scope.owner_info_1 = $scope.activity.get('owner_info_1');
      $scope.sent_at_elapsed = elapsedFilter($scope.activity.get('sent_at'));
      $scope.responses_count = $scope.activity.get('responses_count');
      $scope.isDefaultAvatar = $rootScope.isDefaultAvatar($scope.avatar_file_path);

      var entity = $scope.activity.get('entity');

      if (ctrlByType[entity.type]) {
        ctrlByType[entity.type]($scope);
      } else if (ctrlByType[entity.type_default]) {
        $scope.templateSrc = 'templates/home/activities/default.html';
      }
    }
  };
});<commit_msg>refactor home.js: rename fn name<commit_after>angular.module('app.controllers').controller('home', function ($scope, $timeout, socialActivity, homeCtrlParams,
        profile, activity, groups, flurry, $ionicScrollDelegate) {

  flurry.log('news feed');

  $scope.filter = homeCtrlParams.filter;

  $scope.isLoadMore = false;

  var activityCollection = activity.getActivities();

  function refreshListOfActivities() {
    if(homeCtrlParams.filter.selectedGroup)
      $scope.activities = homeCtrlParams.filter.selectedGroup.activities
    else
      $scope.activities = activityCollection.getFilteredModels();
  }

  function getUnansweredCount(activities) {
    return _(activities).reduce(function (memo, activity) {
      return (activity.get('answered') || activity.get('closed') || activity.get('ignore_count')) ? memo : ++memo;
    }, 0);
  }

  function setFiltersData() {
    homeCtrlParams.filter.groups = groups.getGroupsOptions();
    _(homeCtrlParams.filter.groups).each(function (group) {
      group.activities = activityCollection.getFilteredModels(group);
      group.unansweredCount = getUnansweredCount(group.activities);
      group.read = !_.some(group.activities, function (item) {
        return !item.get('read');
      });
      if (homeCtrlParams.filter.selectedGroup && homeCtrlParams.filter.selectedGroup.id === group.id) {
        homeCtrlParams.filter.selectedGroup = group;
      }
    });
    homeCtrlParams.filter.unansweredCount = getUnansweredCount(activityCollection.getFilteredModels());
  }

  function prepare() {
    homeCtrlParams.loaded = true;
    activityCollection.setDeferredRead().sort();
    setFiltersData();

    $scope.loading = false;
    $ionicScrollDelegate.resize();

    activity.saveRead();
  }

  function loadActivities(loadType) {
    var prevSize = activityCollection.size();
    activity.load(loadType).then(function () {
      if (loadType === 'append' && prevSize === activityCollection.size()) {
        $scope.isLoadMore = false;
      } else {
        $scope.isLoadMore = true;
      }
      prepare();
      $scope.$emit('home.activities-reloaded');
      $scope.$broadcast('scroll.refreshComplete');
      $scope.$broadcast('scroll.infiniteScrollComplete');
    }, prepare).finally(socialActivity.load);
  }

  $scope.togglePostWindow = function () {
    $scope.showPostWindow = !$scope.showPostWindow;
    $scope.execApply();
  };

  $scope.newPost = function (type) {
    var types = {
      1: 'long petition',
      2: 'quorum'
    };
    $scope.path('/micro-petitions/add/' + types[type] + '/' +
            (homeCtrlParams.filter.selectedGroup ? homeCtrlParams.filter.selectedGroup.id : ''));
    $scope.showPostWindow = false;
  };

  $scope.filterLineStep = function () {
    return 5;
  };

  $scope.steps = function () {
    var items = homeCtrlParams.filter.groups.length - 3;
    items = items > 0 ? items : 0;
    var step = $scope.filterLineStep();
    var stepsCount = Math.ceil(items / step);
    var steps = [3];
    for (var i = 1; i < stepsCount; i++) {
      steps.push(i * step + 3);
    }
    if (items && 0 === items % step) {
      steps.push(steps[steps.length - 1] + step);
    }
    return steps;
  };

  $scope.loadMoreActivities = function () {
    loadActivities('append');
  };

  $scope.pullToRefresh = function () {
    loadActivities('clearAndLoad');
  };

  $scope.$watch('filter.selectedGroup', refreshListOfActivities);
  $scope.$watch('loading', function () {
    if ($scope.loading) {
      $scope.showSpinner();
    } else if ($scope.loading === false) {
      $scope.hideSpinner();
    }
  });

  $scope.$on('notification.received', function () {
    loadActivities('refresh');
  });

  $scope.$on('activity.reload', function () {
    loadActivities('refresh');
  });

  //move scroll to top when filter is changed
  $scope.$watch('filter.selectedGroup', function (nVal) {
    if (typeof (nVal) !== undefined) {
      $timeout(function () {
        $ionicScrollDelegate.resize();
        $ionicScrollDelegate.scrollTop();
      });
    }
  });

  //call this when this view is loaded because this view is cached
  $scope.$on('$ionicView.enter', function () {
    if (!profile.get()) {
      profile.load();
    }

    if (!homeCtrlParams.loaded) {
      if (activityCollection.size() === 0) {
        $scope.isLoadMore = true;
      } else {
        loadActivities('refresh');
      }
    } else {
      prepare();
    }

  });
  //call this because cache may be loaded
  setFiltersData();
  refreshListOfActivities();
});

angular.module('app.controllers').run(function (homeCtrlParams, $document, $rootScope) {
  $document.bind('resume', function () {
    homeCtrlParams.loaded = false;
    $rootScope.$broadcast('activity.reload');
    $rootScope.execApply();
  });
});

angular.module('app.controllers').controller('preload', function (topBar, session, profile, $location) {
  if (session.token) {
    if (session.is_registration_complete) {
      profile.load().then(function () {
        profile.checkRemind();
      });
      if (!$location.path() || '/' === $location.path() || '/preload' === $location.path()) {
        $location.path('/main');
      }
    } else {
      $location.path('/profile');
    }
  } else {
    $location.path('/login');
  }
});

angular.module('app.controllers').directive('iActivity', function ($rootScope, questions, petitions, discussion, elapsedFilter, follows, session, iParse, $sce) {

  function eventCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/event.html';
  }

  function newsCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/news.html';
    $scope.entity = $scope.activity.get('entity');
    $scope.rate = function (action) {
      $scope.sending = true;
      discussion.loadRoot('poll', $scope.entity.id).then(function (comment) {
        discussion.rate(comment, action).then(function (comment) {
          $scope.activity.set('rate_up', comment.rate_up).set('rate_down', comment.rate_down);
          $scope.sending = false;
        }, function () {
          $scope.sending = false;
        });
      });
    };
  }

  function paymentCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/payment.html';
  }

  function postCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/post.html';
    $scope.currentUserIsActivityOwner = $scope.activity.get('owner').id == session.user_id
    $scope.booster = $scope.activity.get('owner').type === 'group' ? 100 : $scope.activity.getQuorumCompletedPercent();
    var follow = follows.getByUserId($scope.activity.get('owner').id);
    $scope.followable = !follow.isFollow();
    if ($scope.followable && follow.isApproved()) {
      $scope.isFollowApproved = true;
    }
    $scope.isFollowShow = follows.loaded && follow.get('user').id !== session.user_id;
    $scope.sign = function (optionId) {
      $scope.sending = true;
      petitions.answer($scope.activity.get('entity').id, optionId).then(function (answer) {
        $scope.activity.set('answer', answer).set('answered', true);
        $scope.sending = false;
        if (optionId === 1) {
          $scope.showToast('Post upvoted!');
        }
        if (optionId === 2) {
          $scope.showToast('Post downvoted!');
        }
      });
    };
    $scope.unsign = function () {
      $scope.sending = true;
      petitions.unsign($scope.activity.get('entity').id, $scope.activity.get('answer').option_id).then(function () {
        $scope.activity.set('answered', false).set('answer', null);
        $scope.sending = false;
      });
    };
    $scope.followOwner = function () {
      $scope.sending = true;
      follow.follow().then(function () {
        $scope.activity.followable = false;
        $scope.sending = false;
        $scope.showToast('Follow request sent!');
      });
    };
    $scope.showToast = $rootScope.showToast;
  }

  function petitionCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/petition.html';
    $scope.answer = function () {
      $scope.sending = true;
      if ($scope.answerAction === 'sign') {
        $scope.sign();
      } else {
        $scope.unsign();
      }
      $scope.answerAction = '';
    };
    $scope.$watch(function () {
      return $scope.activity.get('answer');
    }, function (answer) {
      $scope.answerAction = answer ? 'unsign' : 'sign';
    });
    $scope.sign = function () {
      questions.load($scope.activity.get('entity').id).then(function (question) {
        question.answer({
          privacy: 0,
          comment: '',
          option_id: question.options[0].id
        }).then(function (answer) {
          $scope.activity.set('answer', answer).set('answered', true);
          $scope.sending = false;
        });
      });
    };
    $scope.unsign = function () {
      $scope.answerAction = '';
      var answer = $scope.activity.get('answer');
      questions.unsignFromPetition(answer.question.id, answer.option_id).then(function () {
        $scope.activity.set('answer', null).set('answered', false);
        $scope.sending = false;
      });
    };
  }

  function questionCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/question.html';
  }

  var ctrlByType = {
    'leader-event': eventCtrl,
    'leader-news': newsCtrl,
    'crowdfunding-payment-request': paymentCtrl,
    'payment-request': paymentCtrl,
    'micro-petition': postCtrl,
    'petition': petitionCtrl,
    'question': questionCtrl
  };

  return {
    scope: {
      activity: '='
    },
    restrict: 'E',
    template: '<ng-include src="templateSrc"></ng-include>',
    controller: function ($scope) {
      $scope.navigateTo = $rootScope.navigateTo;
      $scope.navigateToActivity = function (activity, focus, e) {
        activity.setRead();
        if (e && e.target.tagName.toLowerCase() === 'hash-tag') {
          $rootScope.openTag(angular.element(e.target).text());
        } else {
          $rootScope.navigateTo('activity', activity, focus);
        }
      };

      $scope.title = $scope.activity.get('title');
      $scope.description = $sce.trustAsHtml(iParse.wrapHashTags($scope.activity.get('description')));
      $scope.avatar_file_path = $scope.activity.get('owner').avatar_file_path;
      $scope.iconClass = $scope.activity.getIcon();
      $scope.official_title = $scope.activity.get('owner').official_title;
      $scope.full_name = [$scope.activity.get('owner').first_name, $scope.activity.get('owner').last_name].join(' ');
      $scope.owner_info_1 = $scope.activity.get('owner_info_1');
      $scope.sent_at_elapsed = elapsedFilter($scope.activity.get('sent_at'));
      $scope.responses_count = $scope.activity.get('responses_count');
      $scope.isDefaultAvatar = $rootScope.isDefaultAvatar($scope.avatar_file_path);

      var entity = $scope.activity.get('entity');

      if (ctrlByType[entity.type]) {
        ctrlByType[entity.type]($scope);
      } else if (ctrlByType[entity.type_default]) {
        $scope.templateSrc = 'templates/home/activities/default.html';
      }
    }
  };
});<|endoftext|>