<commit_before>const axios = require('axios')
const getDocumentTemplate = require('document-templates')
const config = require('../config')
const prepareDocument = require('../lib/prepare-document')
const prepareDocumentPreview = require('../lib/prepare-document-preview')
const documents = require('../lib/data/documents.json')
const courseCategory = documents.courses
const order = documents.order
const behaviour = documents.behaviour
const documentTypes = documents.categories
const samtale = documents.samtale
const warningPeriods = documents.period
const generateSystemJwt = require('../lib/generate-system-jwt')
const createViewOptions = require('../lib/create-view-options')
const createPreview = require('../lib/create-preview')
const datePadding = require('../lib/date-padding')
const getTemplateType = require('../lib/get-template-type')
const getProfilePicture = require('../lib/get-profile-picture')
const logger = require('../lib/logger')

function filterDocumentTypes (contactTeacher) {
  const filteredList = []
  documentTypes.forEach(type => {
    if (type.id === 'fag' || contactTeacher) {
      filteredList.push(type)
    }
  })
  return filteredList
}

module.exports.write = async (request, h) => {
  const yar = request.yar
  const myContactClasses = yar.get('myContactClasses') || []
  const studentUserName = request.params.studentUserName
  const userId = request.auth.credentials.data.userId
  const token = generateSystemJwt(userId)
  const url = `${config.BUDDY_SERVICE_URL}/students/${studentUserName}`
  const urlContactTeachers = `${config.BUDDY_SERVICE_URL}/students/${studentUserName}/contactteachers`
  let mainGroupName = false

  const viewOptions = createViewOptions({ credentials: request.auth.credentials, myContactClasses: myContactClasses, order: order, behaviour: behaviour, courseCategory: courseCategory, samtale: samtale, documentTypes: documentTypes, warningPeriods: warningPeriods })

  logger('info', ['documents', 'write', 'userId', userId, 'studentUserName', studentUserName, 'start'])

  axios.defaults.headers.common.Authorization = token
  // Retrieves student and students contactTeachers
  const [results, contactTeachersResult, profilePicture] = await Promise.all([axios.get(url), axios.get(urlContactTeachers), getProfilePicture(studentUserName)])
  const payload = results.data
  const contactTeachers = contactTeachersResult.data
  if (contactTeachers.length > 0) {
    mainGroupName = contactTeachers[0].groupId
    logger('info', ['documents', 'write', 'userId', userId, 'studentUserName', studentUserName, 'mainGroupName', mainGroupName])
  } else {
    logger('error', ['documents', 'write', 'userId', userId, 'studentUserName', studentUserName, 'contactTeachers not found'])
  }
  if (!payload.statusKode) {
    const student = payload[0]
    const today = new Date()
    student.mainGroupName = mainGroupName
    viewOptions.student = student
    viewOptions.warningTypes = filterDocumentTypes(student.contactTeacher)
    viewOptions.skjemaUtfyllingStart = today.getTime()
    viewOptions.thisDay = `${today.getFullYear()}-${datePadding(today.getMonth() + 1)}-${datePadding(today.getDate())}`
    if (profilePicture !== false) {
      logger('info', ['documents', 'write', 'userId', userId, 'studentUserName', studentUserName, 'retrieved profile picture'])
      viewOptions.profilePicture = profilePicture.data
    }

    logger('info', ['documents', 'write', 'userId', userId, 'studentUserName', studentUserName, 'student data retrieved'])
    if (mainGroupName !== false) {
      return h.view('document', viewOptions)
    } else {
      return h.view('error-missing-contact-teacher', viewOptions)
    }
  }
  if (payload.statusKode === 401) {
    logger('info', ['documents', 'write', 'userId', userId, 'studentUserName', studentUserName, '401'])
    return h.redirect('/signout')
  }
}

module.exports.generatePreview = async (request, h) => {
  const user = request.auth.credentials.data
  const data = request.payload
  data.userId = user.userId
  data.userName = user.userName
  data.userAgent = request.headers['user-agent']
  const postData = prepareDocument(data)
  const previewData = prepareDocumentPreview(postData)
  const documentTemplate = getDocumentTemplate({ domain: 'minelev', templateId: getTemplateType(postData) })
  const template = documentTemplate.filePath

  logger('info', ['documents', 'generatePreview', 'userId', data.userId, 'studentUserName', data.studentUserName, 'start'])

  const prewiewDocument = await createPreview(template, previewData)
  return prewiewDocument
}

module.exports.submit = async (request, h) => {
  const yar = request.yar
  const user = request.auth.credentials.data
  const token = generateSystemJwt(user.userId)
  const url = `${config.QUEUE_SERVICE_URL}`
  const data = request.payload
  data.userId = user.userId
  data.userName = user.userName
  data.userAgent = request.headers['user-agent']
  const postData = prepareDocument(data)

  postData.documentStatus = [
    {
      timeStamp: new Date().getTime(),
      status: 'I kø'
    }
  ]

  axios.defaults.headers.common.Authorization = token

  logger('info', ['documents', 'submit', 'userId', data.userId, 'studentUserName', data.studentUserName, 'start'])

  try {
    await axios.put(url, postData)
    logger('info', ['documents', 'submit', 'userId', data.userId, 'studentUserName', data.studentUserName, 'submitted'])
    yar.set('documentAdded', true)
    return h.redirect('/')
  } catch (error) {
    logger('error', ['documents', 'submit', 'userId', data.userId, 'studentUserName', data.studentUserName, error])
    yar.set('documentAdded', false)
    return h.redirect('/')
  }
}
<commit_msg>Removes removal of studentMainGroupName<commit_after>const axios = require('axios')
const getDocumentTemplate = require('document-templates')
const config = require('../config')
const prepareDocument = require('../lib/prepare-document')
const prepareDocumentPreview = require('../lib/prepare-document-preview')
const documents = require('../lib/data/documents.json')
const courseCategory = documents.courses
const order = documents.order
const behaviour = documents.behaviour
const documentTypes = documents.categories
const samtale = documents.samtale
const warningPeriods = documents.period
const generateSystemJwt = require('../lib/generate-system-jwt')
const createViewOptions = require('../lib/create-view-options')
const createPreview = require('../lib/create-preview')
const datePadding = require('../lib/date-padding')
const getTemplateType = require('../lib/get-template-type')
const getProfilePicture = require('../lib/get-profile-picture')
const logger = require('../lib/logger')

function filterDocumentTypes (contactTeacher) {
  const filteredList = []
  documentTypes.forEach(type => {
    if (type.id === 'fag' || contactTeacher) {
      filteredList.push(type)
    }
  })
  return filteredList
}

module.exports.write = async (request, h) => {
  const yar = request.yar
  const myContactClasses = yar.get('myContactClasses') || []
  const studentUserName = request.params.studentUserName
  const userId = request.auth.credentials.data.userId
  const token = generateSystemJwt(userId)
  const url = `${config.BUDDY_SERVICE_URL}/students/${studentUserName}`
  const urlContactTeachers = `${config.BUDDY_SERVICE_URL}/students/${studentUserName}/contactteachers`
  let mainGroupName = false

  const viewOptions = createViewOptions({ credentials: request.auth.credentials, myContactClasses: myContactClasses, order: order, behaviour: behaviour, courseCategory: courseCategory, samtale: samtale, documentTypes: documentTypes, warningPeriods: warningPeriods })

  logger('info', ['documents', 'write', 'userId', userId, 'studentUserName', studentUserName, 'start'])

  axios.defaults.headers.common.Authorization = token
  // Retrieves student and students contactTeachers
  const [results, contactTeachersResult, profilePicture] = await Promise.all([axios.get(url), axios.get(urlContactTeachers), getProfilePicture(studentUserName)])
  const payload = results.data
  const contactTeachers = contactTeachersResult.data
  if (contactTeachers.length > 0) {
    mainGroupName = contactTeachers[0].groupId
    logger('info', ['documents', 'write', 'userId', userId, 'studentUserName', studentUserName, 'mainGroupName', mainGroupName])
  } else {
    logger('error', ['documents', 'write', 'userId', userId, 'studentUserName', studentUserName, 'contactTeachers not found'])
  }
  if (!payload.statusKode) {
    const student = payload[0]
    const today = new Date()
    viewOptions.student = student
    viewOptions.warningTypes = filterDocumentTypes(student.contactTeacher)
    viewOptions.skjemaUtfyllingStart = today.getTime()
    viewOptions.thisDay = `${today.getFullYear()}-${datePadding(today.getMonth() + 1)}-${datePadding(today.getDate())}`
    if (profilePicture !== false) {
      logger('info', ['documents', 'write', 'userId', userId, 'studentUserName', studentUserName, 'retrieved profile picture'])
      viewOptions.profilePicture = profilePicture.data
    }

    logger('info', ['documents', 'write', 'userId', userId, 'studentUserName', studentUserName, 'student data retrieved'])
    if (mainGroupName !== false) {
      return h.view('document', viewOptions)
    } else {
      return h.view('error-missing-contact-teacher', viewOptions)
    }
  }
  if (payload.statusKode === 401) {
    logger('info', ['documents', 'write', 'userId', userId, 'studentUserName', studentUserName, '401'])
    return h.redirect('/signout')
  }
}

module.exports.generatePreview = async (request, h) => {
  const user = request.auth.credentials.data
  const data = request.payload
  data.userId = user.userId
  data.userName = user.userName
  data.userAgent = request.headers['user-agent']
  const postData = prepareDocument(data)
  const previewData = prepareDocumentPreview(postData)
  const documentTemplate = getDocumentTemplate({ domain: 'minelev', templateId: getTemplateType(postData) })
  const template = documentTemplate.filePath

  logger('info', ['documents', 'generatePreview', 'userId', data.userId, 'studentUserName', data.studentUserName, 'start'])

  const prewiewDocument = await createPreview(template, previewData)
  return prewiewDocument
}

module.exports.submit = async (request, h) => {
  const yar = request.yar
  const user = request.auth.credentials.data
  const token = generateSystemJwt(user.userId)
  const url = `${config.QUEUE_SERVICE_URL}`
  const data = request.payload
  data.userId = user.userId
  data.userName = user.userName
  data.userAgent = request.headers['user-agent']
  const postData = prepareDocument(data)

  postData.documentStatus = [
    {
      timeStamp: new Date().getTime(),
      status: 'I kø'
    }
  ]

  axios.defaults.headers.common.Authorization = token

  logger('info', ['documents', 'submit', 'userId', data.userId, 'studentUserName', data.studentUserName, 'start'])

  try {
    await axios.put(url, postData)
    logger('info', ['documents', 'submit', 'userId', data.userId, 'studentUserName', data.studentUserName, 'submitted'])
    yar.set('documentAdded', true)
    return h.redirect('/')
  } catch (error) {
    logger('error', ['documents', 'submit', 'userId', data.userId, 'studentUserName', data.studentUserName, error])
    yar.set('documentAdded', false)
    return h.redirect('/')
  }
}
<|endoftext|>


<commit_before>describe('tote', function() {
    'use strict';

    function getItemFromLocalStorage( key ) {
        return localStorage.getItem( key );
    }

    describe('when creating', function() {
        var ls;

        afterEach(function() {
            ls.clear();
        });

        it('initializes with a namespace', function() {
            ls = tote('mystash');

            expect( ls ).not.toBeNull();
        });

        it('throws exception, given a null namespace', function() {
            function createWithNullNamespace() {
                ls = tote(null);
            }

            expect( createWithNullNamespace ).toThrow();
        });

        it('throws exception, given an empty string namespace', function() {
            function createWithEmptyStringNamespace() {
                ls = tote('');
            }

            expect( createWithEmptyStringNamespace ).toThrow();
        });
    });

    describe('api', function() {
        var ls;

        beforeEach(function() {
            ls = tote('myarea');
        });

        afterEach(function() {
            ls.clear();
        });

        it('#set, writes serialized values with namespaced key', function() {
            var key = 'foo',
                value = 'FOO!!!';

            ls.set(key, value);

            expect( getItemFromLocalStorage('myarea-foo') ).toEqual( '"' + value + '"' );
        });

        it('#get, retrieves values with namespaced key', function() {
            var key = 'bar',
                value = '!!!BAR';

            ls.set(key, value);

            expect( ls.get(key) ).toEqual( value );
        });

        it('#remove, deletes value associated with namespaced key', function() {
            var key1 = 'getridofthis',
                key2 = 'leavealone',
                value = 'Will be gone soon';
            ls.set(key1, value);
            ls.set(key2, value);

            ls.remove(key1);

            expect( ls.get(key1) ).toBeNull();
            expect( ls.get(key2) ).toEqual( value );
        });

        it('#all, returns an array of all values', function() {
            var key1 = 'x',
                value1 = 'The simple things',
                key2 = 'y',
                value2 = 'Are free?';
            ls.set(key1, value1);
            ls.set(key2, value2);

            var all = ls.all();

            expect( all[0] ).toEqual( value1 );
            expect( all[1] ).toEqual( value2 );
        });

        it('#all({compact:true}), returns an array of a compact representation of all items', function() {
            var item1 = {'a':'Me'},
                item2 = {'b':'You'};
            ls.set('a', item1.a);
            ls.set('b', item2.b);

            var all = ls.all({compact:true});

            expect( all[0] ).toEqual( item1 );
            expect( all[1] ).toEqual( item2 );
        });

        it('#all({kvp:true}), returns an array key-value pairs stored in namespace', function() {
            var item1 = {key:'x', value:'Me'},
                item2 = {key:'y', value:'You'};
            ls.set(item1.key, item1.value);
            ls.set(item2.key, item2.value);

            var all = ls.all({kvp:true});

            expect( all[0] ).toEqual( item1 );
            expect( all[1] ).toEqual( item2 );
        });

        it('#length, returns the number of items stored', function() {
            var key1 = 'item1',
                key2 = 'item2',
                value = 'Just counting';
            ls.set(key1, value);
            ls.set(key2, value);

            expect( ls.length() ).toEqual( 2 );

            ls.remove(key1);
            expect( ls.length() ).toEqual( 1 );
        });

        it('#key(n), returns the name of the nth key in storage', function() {
            var key1 = 'n1',
                key2 = 'n2',
                value = 'access by position';
            ls.set(key1, value);
            ls.set(key2, value);

            expect( ls.key(0) ).toEqual( key1 );
            expect( ls.key(1) ).toEqual( key2 );
        });
    });

    describe('handling different datatypes', function() {
        var ls;

        beforeEach(function() {
            ls = tote('datatypes');
        });

        afterEach(function() {
            ls.clear();
        });

        it('persists and retrieves a simple string', function() {
            var key = 'string-simple',
                value = 'Simple String';

            ls.set(key, value);

            expect( ls.get(key) ).toEqual( value );
        });

        it('persists and retrieves a string with crazy characters', function() {
            var key = 'string-complex',
                value = 'Some overly c\'omplex string/ with "quoted" and *strred* and & etc...';

            ls.set(key, value);

            expect( ls.get(key) ).toEqual( value );
        });

        it('persists and retrieves a positive number', function() {
            var key = 'number-positive',
                value = 23434;

            ls.set(key, value);

            expect( ls.get(key) ).toEqual( value );
        });

        it('persists and retrieves a negative number', function() {
            var key = 'number-negative',
                value = -4747;

            ls.set(key, value);

            expect( ls.get(key) ).toEqual( value );
        });

        it('persists and retrieves a float', function() {
            var key = 'number-float',
                value = 3700.894;

            ls.set(key, value);

            expect( ls.get(key) ).toEqual( value );
        });

        it('persists and retrieves a boolean true', function() {
            var key = 'bool-true',
                value = true;

            ls.set(key, value);

            expect( ls.get(key) ).toEqual( value );
        });

        it('persists and retrieves a boolean false', function() {
            var key = 'bool-false',
                value = false;

            ls.set(key, value);

            expect( ls.get(key) ).toEqual( value );
        });

        it('persists and retrieves an object', function() {
            var key = 'obj',
                value = {id:2, name:'Jolted Zoo', age:45, 'here':true};

            ls.set(key, value);

            expect( ls.get(key) ).toEqual( value );
        });

        it('persists and retrieves an array', function() {
            var key = 'array',
                value = [
                    {id:1, name:'Yellow Pellet', age:23, 'here':false},
                    {id:2, name:'Jolted Zoo', age:45, 'here':true}
                ];

            ls.set(key, value);

            expect( ls.get(key) ).toEqual( value );
        });

    });

    describe('multiple instances with different namespace', function() {
        var ls1,
            ls2;

        beforeEach(function() {
            ls1 = tote('area51');
            ls2 = tote('districtB13');
        });

        afterEach(function() {
            ls1.clear();
            ls2.clear();
        });

        it('#clear, removes only values associated with self', function() {
            var key1 = 'one',
                key2 = 'two',
                value1 = 'First value',
                value2 = 'Second value';
            ls1.set(key1, value1);
            ls1.set(key2, value2);
            ls2.set(key1, value1);
            ls2.set(key2, value2);

            ls1.clear();

            expect( ls1.get(key1) ).toBeNull();
            expect( ls1.get(key2) ).toBeNull();

            expect( ls2.get(key1) ).toEqual( value1 );
            expect( ls2.get(key2) ).toEqual( value2 );
        });
    });

    describe('multiple instances with the same namespaces', function() {
        var ls1,
            ls2;

        beforeEach(function() {
            ls1 = tote('mySpace');
            ls2 = tote('mySpace');
        });

        afterEach(function() {
            ls1.clear();
            ls2.clear();
        });

        it('#set, values persisted in one will be available in the other', function() {
            var key1 = 'one',
                key2 = 'two',
                value1 = 'First value',
                value2 = 'Second value';

            ls1.set(key1, value1);
            ls1.set(key2, value2);

            expect( ls2.get(key1) ).toEqual( value1 );
            expect( ls2.get(key2) ).toEqual( value2 );
        });

        it('#remove, values removed in one will be removed in the other', function() {
            var key1 = 'one',
                key2 = 'two',
                value1 = 'First value',
                value2 = 'Second value';
            ls1.set(key1, value1);
            ls1.set(key2, value2);

            ls1.remove(key2);

            expect( ls2.get(key1) ).toEqual( value1 );
            expect( ls2.get(key2) ).toBeNull();
        });

        it('#clear, removes values associated with the current namespace', function() {
            var key1 = 'one',
                key2 = 'two',
                value1 = 'First value',
                value2 = 'Second value';
            ls1.set(key1, value1);
            ls1.set(key2, value2);
            ls2.set(key1, value1);
            ls2.set(key2, value2);

            ls1.clear();

            expect( ls1.get(key1) ).toBeNull();
            expect( ls1.get(key2) ).toBeNull();

            expect( ls2.get(key1) ).toBeNull();
            expect( ls2.get(key2) ).toBeNull();
        });

        it('#all, values persisted in one will be available in the other', function() {
            var key1 = 'one',
                key2 = 'two',
                value1 = 'First value',
                value2 = 'Second value';

            ls1.set(key1, value1);
            ls1.set(key2, value2);

            expect( ls2.all()[0] ).toEqual( value1 );
            expect( ls2.all()[1] ).toEqual( value2 );
        });
    });
});<commit_msg>Update website<commit_after>describe('tote', function() {
    'use strict';

    function getItemFromLocalStorage( key ) {
        return localStorage.getItem( key );
    }

    describe('when creating', function() {
        var ls;

        afterEach(function() {
            ls.clear();
        });

        it('initializes with a namespace', function() {
            ls = tote('mystash');

            expect( ls ).not.toBeNull();
        });

        it('throws exception, given a null namespace', function() {
            function createWithNullNamespace() {
                ls = tote(null);
            }

            expect( createWithNullNamespace ).toThrow();
        });

        it('throws exception, given an empty string namespace', function() {
            function createWithEmptyStringNamespace() {
                ls = tote('');
            }

            expect( createWithEmptyStringNamespace ).toThrow();
        });
    });

    describe('api', function() {
        var ls;

        beforeEach(function() {
            ls = tote('myarea');
        });

        afterEach(function() {
            ls.clear();
        });

        it('#set, writes serialized values with namespaced key', function() {
            var key = 'foo',
                value = 'FOO!!!';

            ls.set(key, value);

            expect( getItemFromLocalStorage('myarea-foo') ).toEqual( '"' + value + '"' );
        });

        it('#get, retrieves values with namespaced key', function() {
            var key = 'bar',
                value = '!!!BAR';

            ls.set(key, value);

            expect( ls.get(key) ).toEqual( value );
        });

        it('#remove, deletes value associated with namespaced key', function() {
            var key1 = 'getridofthis',
                key2 = 'leavealone',
                value = 'Will be gone soon';
            ls.set(key1, value);
            ls.set(key2, value);

            ls.remove(key1);

            expect( ls.get(key1) ).toBeNull();
            expect( ls.get(key2) ).toEqual( value );
        });

        it('#all, returns an array of all values', function() {
            var key1 = 'x',
                value1 = 'The simple things',
                key2 = 'y',
                value2 = 'Are free?';
            ls.set(key1, value1);
            ls.set(key2, value2);

            var all = ls.all();

            expect( all[0] ).toEqual( value1 );
            expect( all[1] ).toEqual( value2 );
        });

        it('#all({compact:true}), returns an array of a compact representation of all items', function() {
            var item1 = {'a':'Me'},
                item2 = {'b':'You'};
            ls.set('a', item1.a);
            ls.set('b', item2.b);

            var all = ls.all({compact:true});

            expect( all[0] ).toEqual( item1 );
            expect( all[1] ).toEqual( item2 );
        });

        it('#all({kvp:true}), returns an array key-value pairs stored in namespace', function() {
            var item1 = {key:'x', value:'Me'},
                item2 = {key:'y', value:'You'};
            ls.set(item1.key, item1.value);
            ls.set(item2.key, item2.value);

            var all = ls.all({kvp:true});

            expect( all[0] ).toEqual( item1 );
            expect( all[1] ).toEqual( item2 );
        });

        it('#length, returns the number of items stored', function() {
            var key1 = 'item1',
                key2 = 'item2',
                value = 'Just counting';
            ls.set(key1, value);
            ls.set(key2, value);

            expect( ls.length() ).toEqual( 2 );

            ls.remove(key1);
            expect( ls.length() ).toEqual( 1 );
        });

        it('#key(n), returns the name of the nth key in storage', function() {
            var key1 = 'n1',
                key2 = 'n2',
                value = 'access by position';
            ls.set(key1, value);
            ls.set(key2, value);

            expect( ls.key(0) ).toEqual( key1 );
            expect( ls.key(1) ).toEqual( key2 );
        });
    });

    describe('handling different datatypes', function() {
        var ls;

        beforeEach(function() {
            ls = tote('datatypes');
        });

        afterEach(function() {
            ls.clear();
        });

        it('persists and retrieves a simple string', function() {
            var key = 'string-simple',
                value = 'Simple String';

            ls.set(key, value);

            expect( ls.get(key) ).toEqual( value );
        });

        it('persists and retrieves a string with crazy characters', function() {
            var key = 'string-complex',
                value = 'Some overly c\'omplex string/ with "quoted" and *strred* and & etc...';

            ls.set(key, value);

            expect( ls.get(key) ).toEqual( value );
        });

        it('persists and retrieves a positive number', function() {
            var key = 'number-positive',
                value = 23434;

            ls.set(key, value);

            expect( ls.get(key) ).toEqual( value );
        });

        it('persists and retrieves a negative number', function() {
            var key = 'number-negative',
                value = -4747;

            ls.set(key, value);

            expect( ls.get(key) ).toEqual( value );
        });

        it('persists and retrieves a float', function() {
            var key = 'number-float',
                value = 3700.894;

            ls.set(key, value);

            expect( ls.get(key) ).toEqual( value );
        });

        it('persists and retrieves a boolean true', function() {
            var key = 'bool-true',
                value = true;

            ls.set(key, value);

            expect( ls.get(key) ).toEqual( value );
        });

        it('persists and retrieves a boolean false', function() {
            var key = 'bool-false',
                value = false;

            ls.set(key, value);

            expect( ls.get(key) ).toEqual( value );
        });

        it('persists and retrieves an object', function() {
            var key = 'obj',
                value = {id:2, name:'Jolted Zoo', age:45, 'here':true};

            ls.set(key, value);

            expect( ls.get(key) ).toEqual( value );
        });

        it('persists and retrieves an array', function() {
            var key = 'array',
                value = [
                    {id:1, name:'Yellow Pellet', age:23, 'here':false},
                    {id:2, name:'Jolted Zoo', age:45, 'here':true}
                ];

            ls.set(key, value);

            expect( ls.get(key) ).toEqual( value );
        });

    });

    describe('multiple instances with different namespaces', function() {
        var ls1,
            ls2;

        beforeEach(function() {
            ls1 = tote('area51');
            ls2 = tote('districtB13');
        });

        afterEach(function() {
            ls1.clear();
            ls2.clear();
        });

        it('#clear, removes only values associated with self', function() {
            var key1 = 'one',
                key2 = 'two',
                value1 = 'First value',
                value2 = 'Second value';
            ls1.set(key1, value1);
            ls1.set(key2, value2);
            ls2.set(key1, value1);
            ls2.set(key2, value2);

            ls1.clear();

            expect( ls1.get(key1) ).toBeNull();
            expect( ls1.get(key2) ).toBeNull();

            expect( ls2.get(key1) ).toEqual( value1 );
            expect( ls2.get(key2) ).toEqual( value2 );
        });
    });

    describe('multiple instances with the same namespace', function() {
        var ls1,
            ls2;

        beforeEach(function() {
            ls1 = tote('mySpace');
            ls2 = tote('mySpace');
        });

        afterEach(function() {
            ls1.clear();
            ls2.clear();
        });

        it('#set, values persisted in one will be available in the other', function() {
            var key1 = 'one',
                key2 = 'two',
                value1 = 'First value',
                value2 = 'Second value';

            ls1.set(key1, value1);
            ls1.set(key2, value2);

            expect( ls2.get(key1) ).toEqual( value1 );
            expect( ls2.get(key2) ).toEqual( value2 );
        });

        it('#remove, values removed in one will be removed in the other', function() {
            var key1 = 'one',
                key2 = 'two',
                value1 = 'First value',
                value2 = 'Second value';
            ls1.set(key1, value1);
            ls1.set(key2, value2);

            ls1.remove(key2);

            expect( ls2.get(key1) ).toEqual( value1 );
            expect( ls2.get(key2) ).toBeNull();
        });

        it('#clear, removes values associated with the current namespace', function() {
            var key1 = 'one',
                key2 = 'two',
                value1 = 'First value',
                value2 = 'Second value';
            ls1.set(key1, value1);
            ls1.set(key2, value2);
            ls2.set(key1, value1);
            ls2.set(key2, value2);

            ls1.clear();

            expect( ls1.get(key1) ).toBeNull();
            expect( ls1.get(key2) ).toBeNull();

            expect( ls2.get(key1) ).toBeNull();
            expect( ls2.get(key2) ).toBeNull();
        });

        it('#all, values persisted in one will be available in the other', function() {
            var key1 = 'one',
                key2 = 'two',
                value1 = 'First value',
                value2 = 'Second value';

            ls1.set(key1, value1);
            ls1.set(key2, value2);

            expect( ls2.all()[0] ).toEqual( value1 );
            expect( ls2.all()[1] ).toEqual( value2 );
        });
    });
});<|endoftext|>


<commit_before>// A function that builds the __new__ method for the factory function
__BRYTHON__.$__new__ = function(factory){
    return function(cls){
        /*
        if(cls===undefined){
            throw __BRYTHON__.builtins.TypeError(factory.$dict.__name__+'.__new__(): not enough arguments')
        }
        */
        var res = factory.apply(null,[])
        res.__class__ = cls.$dict
        var init_func = null
        try{init_func = __BRYTHON__.builtins.getattr(res,'__init__')}
        catch(err){}
        if(init_func!==null){
            var args = [], pos=0
            for(var i=1, _len_i = arguments.length; i < _len_i;i++){args[pos++]=arguments[i]}
            init_func.apply(null,args)
            res.__initialized__ = true
        }
        return res
    }
}

__BRYTHON__.builtins.object = (function($B){

var _b_=$B.builtins

// class object for the built-in class 'object'
var $ObjectDict = {
    //__class__:$type, : not here, added in py_type.js after $type is defined
    // __bases__ : set to an empty tuple in py_list.js after tuple is defined
    __name__:'object',
    $native:true
}

// function used to generate the methods that return 'unorderable types'
var $ObjectNI = function(name,op){
    return function(other){
        throw _b_.TypeError('unorderable types: object() '+op+
            ' '+ _b_.str($B.get_class(other).__name__)+'()')
    }
}

// Name of special methods : if they are not found as attributes, try
// the "reflected" attribute on the argument
// For instance, for "getattr(x,'__mul__')", if object x has no attribute
// "__mul__", try a function using the attribute "__rmul__" of its
// first argument

var opnames = ['add','sub','mul','truediv','floordiv','mod','pow',
    'lshift','rshift','and','xor','or']
var opsigns = ['+','-','*','/','//','%','**','<<','>>','&','^', '|']

$ObjectDict.__delattr__ = function(self,attr){delete self[attr]}

$ObjectDict.__dir__ = function(self) {
    var objects = [self], pos=1
    var mro = $B.get_class(self).__mro__
    for (var i=0, _len_i = mro.length; i < _len_i; i++) {
        objects[pos++]=mro[i]
    }

    var res = [], pos=0
    for (var i=0, _len_i = objects.length; i < _len_i; i++) {
        for(var attr in objects[i]){
            //if(attr.charAt(0)=='$' && attr.substr(0,2)!=='$$'){
            if(attr.charAt(0)=='$' && attr.charAt(1) != '$') {
                // exclude internal attributes set by Brython
                continue
            }
            if(!isNaN(parseInt(attr.charAt(0)))){
                // Exclude numerical attributes
                // '0', '1' are in attributes of string 'ab'
                continue
            }
            res[pos++]=attr
        }
    }
    res = _b_.list(_b_.set(res))
    _b_.list.$dict.sort(res)
    return res
}

$ObjectDict.__eq__ = function(self,other){
    // equality test defaults to identity of objects
    //test_issue_1393
    var _class=$B.get_class(self)
    if (_class.$native || _class.__name__ == 'function') {
       var _class1=$B.get_class(other)
       if (!_class1.$native && _class1.__name__ != 'function') {
          return _b_.getattr(other, '__eq__')(self)
       }
    }
    return self===other
}

$ObjectDict.__ge__ = $ObjectNI('__ge__','>=')

$ObjectDict.__getattribute__ = function(obj,attr){

    var klass = $B.get_class(obj)
    if(attr==='__class__'){
        return klass.$factory
    }
    var res = obj[attr],args=[]

    if(res===undefined){
        // search in classes hierarchy, following method resolution order
        //if(attr=='show'){console.log('object getattr '+attr+' of obj '+obj)}
        var mro = klass.__mro__
        for(var i=0, _len_i = mro.length; i < _len_i;i++){
            if(mro[i].$methods){
                var method = mro[i].$methods[attr]
                if(method!==undefined){return method(obj)}
            }
            var v=mro[i][attr]
            if(v!==undefined){
                res = v
                break
            }else if(attr=='__str__' && mro[i]['__repr__']!==undefined){
                // If the class doesn't define __str__ but defines __repr__,
                // use __repr__
                res = mro[i]['repr']
                break
            }
        }
    }else{
        if(res.__set__===undefined){
            // For non-data descriptors, the attribute found in object
            // dictionary takes precedence
            return res
        }
    }

    if(res!==undefined){
        var get_func = res.__get__

        if(get_func===undefined && (typeof res=='object')){
            var __get__ = _b_.getattr(res,'__get__',null);
            if(__get__ && (typeof __get__=='function')){
                get_func = function(x,y){return __get__.apply(x,[y,klass])}
            }
        }

        if(get_func===undefined && (typeof res=='function')){
            get_func = function(x){return x}
        }
        if(get_func!==undefined){ // descriptor
            res.__name__ = attr
            // __new__ is a static method
            if(attr=='__new__'){res.$type='staticmethod'}
            var res1 = get_func.apply(null,[res,obj,klass])
            if(typeof res1=='function'){
                // If attribute is a class then return it unchanged
                //
                // Example :
                // ===============
                // class A:
                //    def __init__(self,x):
                //        self.x = x
                //
                // class B:
                //    foo = A
                //    def __init__(self):
                //        self.info = self.foo(18)
                //
                // B()
                // ===============
                // In class B, when we call self.foo(18), self.foo is the
                // class A, its method __init__ must be called without B's
                // self as first argument

                if(res1.__class__===$B.$factory) return res

                // Same thing if the attribute is a method of an instance
                // =================
                // class myRepr:
                //     def repr(self, a):
                //         return a
                //
                // class myclass:
                //     _repr=myRepr()
                //     repr= _repr.repr
                //
                //     def myfunc(self):
                //         return self.repr('test')
                // =================
                // In function myfunc, self.repr is an instance of MyRepr,
                // it must be used as is, not transformed into a method

                else if(res1.__class__===$B.$MethodDict) return res

                // instance method object
                return $B.make_method(attr, klass, res, res1)(obj)

            }else{
                // result of __get__ is not a function
                return res1
            }
        }
        // attribute is not a descriptor : return it unchanged
        return res
    }else{
        // search __getattr__
        var _ga = obj['__getattr__']
        if(_ga===undefined){
            var mro = klass.__mro__
            if(mro===undefined){console.log('in getattr mro undefined for '+obj)}
            for(var i=0, _len_i = mro.length; i < _len_i;i++){
                var v=mro[i]['__getattr__']
                if(v!==undefined){
                    _ga = v
                    break
                }
            }
        }
        if(_ga!==undefined){
            try{return _ga(obj,attr)}
            catch(err){}
        }
        // for special methods such as __mul__, look for __rmul__ on operand
        if(attr.substr(0,2)=='__' && attr.substr(attr.length-2)=='__'){
            var attr1 = attr.substr(2,attr.length-4) // stripped of __
            var rank = opnames.indexOf(attr1)
            if(rank > -1){
                var rop = '__r'+opnames[rank]+'__' // name of reflected operator
                return function(){
                    try{
                        // Operands must be of different types
                        if($B.$get_class(arguments[0])===klass){throw Error('')}
                        return _b_.getattr(arguments[0],rop)(obj)
                    }catch(err){
                        var msg = "unsupported operand types for "+opsigns[rank]+": '"
                        msg += klass.__name__+"' and '" //+arguments[0].__class__.__name__+"'"
                        throw _b_.TypeError(msg)
                    }
                }
            }
        }
        //throw AttributeError('object '+obj.__class__.__name__+" has no attribute '"+attr+"'")
    }
}

$ObjectDict.__gt__ = $ObjectNI('__gt__','>')

$ObjectDict.__hash__ = function (self) {
    $B.$py_next_hash+=1;
    return $B.$py_next_hash;
}

$ObjectDict.__init__ = function(){}

$ObjectDict.__le__ = $ObjectNI('__le__','<=')

$ObjectDict.__lt__ = $ObjectNI('__lt__','<')

$ObjectDict.__mro__ = [$ObjectDict]

$ObjectDict.__new__ = function(cls){
    if(cls===undefined){throw _b_.TypeError('object.__new__(): not enough arguments')}
    return {__class__ : cls.$dict}
}

$ObjectDict.__ne__ = function(self,other){
   // see if any parent classes contain a __ne__ function
   var _f= $ObjectDict.__getattribute__(self, '__ne__')
   if (_f.__class__ !== self.__class__) return _b_.getattr(_f,'__ne__')(self, other)

   // check to see if any parent classes contain a __eq__ function
   var _f= $ObjectDict.__getattribute__(self, '__eq__')
   return _b_.bool(!_b_.getattr(_f,'__eq__')(self, other))
}

$ObjectDict.__or__ = function(self,other){
    if(_b_.bool(self)) return self
    return other
}

$ObjectDict.__repr__ = function(self){
    if(self===object) return "<class 'object'>"
    if(self.__class__===$B.$factory) return "<class '"+self.$dict.__name__+"'>"
    if(self.__class__.__module__!==undefined){
        return "<"+self.__class__.__module__+"."+self.__class__.__name__+" object>"
    }else{
        return "<"+self.__class__.__name__+" object>"
    }
}

$ObjectDict.__setattr__ = function(self,attr,val){
    if(val===undefined){ // setting an attribute to 'object' type is not allowed
        throw _b_.TypeError("can't set attributes of built-in/extension type 'object'")
    }else if(self.__class__===$ObjectDict){
        // setting an attribute to object() is not allowed
        if($ObjectDict[attr]===undefined){
            throw _b_.AttributeError("'object' object has no attribute '"+attr+"'")
        }else{
            throw _b_.AttributeError("'object' object attribute '"+attr+"' is read-only")
        }
    }
    self[attr] = val
}
$ObjectDict.__setattr__.__str__ = function(){return 'method object.setattr'}

$ObjectDict.__str__ = $ObjectDict.__repr__

$ObjectDict.__subclasshook__ = function(){return _b_.NotImplemented}

// constructor of the built-in class 'object'
function object(){return {__class__:$ObjectDict}}

object.$dict = $ObjectDict
// object.__class__ = $factory : this is done in py_types
$ObjectDict.$factory = object
object.__repr__ = object.__str__ = function(){return "<class 'object'>"}

return object

})(__BRYTHON__)
<commit_msg>Change implementation of object.__ne__()<commit_after>// A function that builds the __new__ method for the factory function
__BRYTHON__.$__new__ = function(factory){
    return function(cls){
        /*
        if(cls===undefined){
            throw __BRYTHON__.builtins.TypeError(factory.$dict.__name__+'.__new__(): not enough arguments')
        }
        */
        var res = factory.apply(null,[])
        res.__class__ = cls.$dict
        var init_func = null
        try{init_func = __BRYTHON__.builtins.getattr(res,'__init__')}
        catch(err){}
        if(init_func!==null){
            var args = [], pos=0
            for(var i=1, _len_i = arguments.length; i < _len_i;i++){args[pos++]=arguments[i]}
            init_func.apply(null,args)
            res.__initialized__ = true
        }
        return res
    }
}

__BRYTHON__.builtins.object = (function($B){

var _b_=$B.builtins

// class object for the built-in class 'object'
var $ObjectDict = {
    //__class__:$type, : not here, added in py_type.js after $type is defined
    // __bases__ : set to an empty tuple in py_list.js after tuple is defined
    __name__:'object',
    $native:true
}

// function used to generate the methods that return 'unorderable types'
var $ObjectNI = function(name,op){
    return function(other){
        throw _b_.TypeError('unorderable types: object() '+op+
            ' '+ _b_.str($B.get_class(other).__name__)+'()')
    }
}

// Name of special methods : if they are not found as attributes, try
// the "reflected" attribute on the argument
// For instance, for "getattr(x,'__mul__')", if object x has no attribute
// "__mul__", try a function using the attribute "__rmul__" of its
// first argument

var opnames = ['add','sub','mul','truediv','floordiv','mod','pow',
    'lshift','rshift','and','xor','or']
var opsigns = ['+','-','*','/','//','%','**','<<','>>','&','^', '|']

$ObjectDict.__delattr__ = function(self,attr){delete self[attr]}

$ObjectDict.__dir__ = function(self) {
    var objects = [self], pos=1
    var mro = $B.get_class(self).__mro__
    for (var i=0, _len_i = mro.length; i < _len_i; i++) {
        objects[pos++]=mro[i]
    }

    var res = [], pos=0
    for (var i=0, _len_i = objects.length; i < _len_i; i++) {
        for(var attr in objects[i]){
            //if(attr.charAt(0)=='$' && attr.substr(0,2)!=='$$'){
            if(attr.charAt(0)=='$' && attr.charAt(1) != '$') {
                // exclude internal attributes set by Brython
                continue
            }
            if(!isNaN(parseInt(attr.charAt(0)))){
                // Exclude numerical attributes
                // '0', '1' are in attributes of string 'ab'
                continue
            }
            res[pos++]=attr
        }
    }
    res = _b_.list(_b_.set(res))
    _b_.list.$dict.sort(res)
    return res
}

$ObjectDict.__eq__ = function(self,other){
    // equality test defaults to identity of objects
    //test_issue_1393
    var _class=$B.get_class(self)
    if (_class.$native || _class.__name__ == 'function') {
       var _class1=$B.get_class(other)
       if (!_class1.$native && _class1.__name__ != 'function') {
          return _b_.getattr(other, '__eq__')(self)
       }
    }
    return self===other
}

$ObjectDict.__ge__ = $ObjectNI('__ge__','>=')

$ObjectDict.__getattribute__ = function(obj,attr){

    var klass = $B.get_class(obj)
    if(attr==='__class__'){
        return klass.$factory
    }
    var res = obj[attr],args=[]

    if(res===undefined){
        // search in classes hierarchy, following method resolution order
        //if(attr=='show'){console.log('object getattr '+attr+' of obj '+obj)}
        var mro = klass.__mro__
        for(var i=0, _len_i = mro.length; i < _len_i;i++){
            if(mro[i].$methods){
                var method = mro[i].$methods[attr]
                if(method!==undefined){return method(obj)}
            }
            var v=mro[i][attr]
            if(v!==undefined){
                res = v
                break
            }else if(attr=='__str__' && mro[i]['__repr__']!==undefined){
                // If the class doesn't define __str__ but defines __repr__,
                // use __repr__
                res = mro[i]['repr']
                break
            }
        }
    }else{
        if(res.__set__===undefined){
            // For non-data descriptors, the attribute found in object
            // dictionary takes precedence
            return res
        }
    }

    if(res!==undefined){
        var get_func = res.__get__

        if(get_func===undefined && (typeof res=='object')){
            var __get__ = _b_.getattr(res,'__get__',null);
            if(__get__ && (typeof __get__=='function')){
                get_func = function(x,y){return __get__.apply(x,[y,klass])}
            }
        }

        if(get_func===undefined && (typeof res=='function')){
            get_func = function(x){return x}
        }
        if(get_func!==undefined){ // descriptor
            res.__name__ = attr
            // __new__ is a static method
            if(attr=='__new__'){res.$type='staticmethod'}
            var res1 = get_func.apply(null,[res,obj,klass])
            if(typeof res1=='function'){
                // If attribute is a class then return it unchanged
                //
                // Example :
                // ===============
                // class A:
                //    def __init__(self,x):
                //        self.x = x
                //
                // class B:
                //    foo = A
                //    def __init__(self):
                //        self.info = self.foo(18)
                //
                // B()
                // ===============
                // In class B, when we call self.foo(18), self.foo is the
                // class A, its method __init__ must be called without B's
                // self as first argument

                if(res1.__class__===$B.$factory) return res

                // Same thing if the attribute is a method of an instance
                // =================
                // class myRepr:
                //     def repr(self, a):
                //         return a
                //
                // class myclass:
                //     _repr=myRepr()
                //     repr= _repr.repr
                //
                //     def myfunc(self):
                //         return self.repr('test')
                // =================
                // In function myfunc, self.repr is an instance of MyRepr,
                // it must be used as is, not transformed into a method

                else if(res1.__class__===$B.$MethodDict) return res

                // instance method object
                return $B.make_method(attr, klass, res, res1)(obj)

            }else{
                // result of __get__ is not a function
                return res1
            }
        }
        // attribute is not a descriptor : return it unchanged
        return res
    }else{
        // search __getattr__
        var _ga = obj['__getattr__']
        if(_ga===undefined){
            var mro = klass.__mro__
            if(mro===undefined){console.log('in getattr mro undefined for '+obj)}
            for(var i=0, _len_i = mro.length; i < _len_i;i++){
                var v=mro[i]['__getattr__']
                if(v!==undefined){
                    _ga = v
                    break
                }
            }
        }
        if(_ga!==undefined){
            try{return _ga(obj,attr)}
            catch(err){}
        }
        // for special methods such as __mul__, look for __rmul__ on operand
        if(attr.substr(0,2)=='__' && attr.substr(attr.length-2)=='__'){
            var attr1 = attr.substr(2,attr.length-4) // stripped of __
            var rank = opnames.indexOf(attr1)
            if(rank > -1){
                var rop = '__r'+opnames[rank]+'__' // name of reflected operator
                return function(){
                    try{
                        // Operands must be of different types
                        if($B.$get_class(arguments[0])===klass){throw Error('')}
                        return _b_.getattr(arguments[0],rop)(obj)
                    }catch(err){
                        var msg = "unsupported operand types for "+opsigns[rank]+": '"
                        msg += klass.__name__+"' and '" //+arguments[0].__class__.__name__+"'"
                        throw _b_.TypeError(msg)
                    }
                }
            }
        }
        //throw AttributeError('object '+obj.__class__.__name__+" has no attribute '"+attr+"'")
    }
}

$ObjectDict.__gt__ = $ObjectNI('__gt__','>')

$ObjectDict.__hash__ = function (self) {
    $B.$py_next_hash+=1;
    return $B.$py_next_hash;
}

$ObjectDict.__init__ = function(){}

$ObjectDict.__le__ = $ObjectNI('__le__','<=')

$ObjectDict.__lt__ = $ObjectNI('__lt__','<')

$ObjectDict.__mro__ = [$ObjectDict]

$ObjectDict.__new__ = function(cls){
    if(cls===undefined){throw _b_.TypeError('object.__new__(): not enough arguments')}
    return {__class__ : cls.$dict}
}

$ObjectDict.__ne__ = function(self,other){
    return !_b_.getattr(self, '__eq__')(other)
}

$ObjectDict.__or__ = function(self,other){
    if(_b_.bool(self)) return self
    return other
}

$ObjectDict.__repr__ = function(self){
    if(self===object) return "<class 'object'>"
    if(self.__class__===$B.$factory) return "<class '"+self.$dict.__name__+"'>"
    if(self.__class__.__module__!==undefined){
        return "<"+self.__class__.__module__+"."+self.__class__.__name__+" object>"
    }else{
        return "<"+self.__class__.__name__+" object>"
    }
}

$ObjectDict.__setattr__ = function(self,attr,val){
    if(val===undefined){ // setting an attribute to 'object' type is not allowed
        throw _b_.TypeError("can't set attributes of built-in/extension type 'object'")
    }else if(self.__class__===$ObjectDict){
        // setting an attribute to object() is not allowed
        if($ObjectDict[attr]===undefined){
            throw _b_.AttributeError("'object' object has no attribute '"+attr+"'")
        }else{
            throw _b_.AttributeError("'object' object attribute '"+attr+"' is read-only")
        }
    }
    self[attr] = val
}
$ObjectDict.__setattr__.__str__ = function(){return 'method object.setattr'}

$ObjectDict.__str__ = $ObjectDict.__repr__

$ObjectDict.__subclasshook__ = function(){return _b_.NotImplemented}

// constructor of the built-in class 'object'
function object(){return {__class__:$ObjectDict}}

object.$dict = $ObjectDict
// object.__class__ = $factory : this is done in py_types
$ObjectDict.$factory = object
object.__repr__ = object.__str__ = function(){return "<class 'object'>"}

return object

})(__BRYTHON__)
<|endoftext|>


<commit_before>import extractContent from 'app/utils/ExtractContent';
import {objAccessor} from 'app/utils/Accessors';
import { APP_NAME, APP_URL, SITE_DESCRIPTION, TWITTER_HANDLE, SHARE_IMAGE, TWITTER_SHARE_IMAGE } from 'config/client_config';

function addSiteMeta(metas) {
    metas.push({title: APP_NAME});
    metas.push({property: 'og:type', content: 'website'});
    metas.push({property: 'og:site_name', content: APP_NAME});
    metas.push({property: 'og:title', content: APP_NAME});
    metas.push({property: 'og:description', content: SITE_DESCRIPTION});
    metas.push({property: 'og:image', content: SHARE_IMAGE});
    metas.push({property: 'fb:app_id', content: $STM_Config.fb_app});
    metas.push({name: 'twitter:card', content: 'summary'});
    metas.push({name: 'twitter:site', content: TWITTER_HANDLE});
    metas.push({name: 'twitter:title', content: APP_NAME});
    metas.push({name: 'twitter:description', site_desc: SITE_DESCRIPTION});
    metas.push({name: 'twitter:image', content: SHARE_IMAGE});
}

export default function extractMeta(chain_data, rp) {
    const metas = [];
    if (rp.username && rp.slug) { // post
        const post = `${rp.username}/${rp.slug}`;
        const content = chain_data.content[post];
        if (content && content.id !== '0.0.0') { // API currently returns 'false' data with id 0.0.0 for posts that do not exist
            const d = extractContent(objAccessor, content, false);

            const url   = 'https://' + APP_URL + d.link;
            const title = d.title + ' — ' + APP_NAME;
            const desc  = d.desc + ' ' + translate('by') + ' ' + d.author;
            const image = d.image_link
            const {category, created} = d

            // Standard meta
            metas.push({title});
            metas.push({canonical: url});
            metas.push({name: 'description',         content: desc});

            // Open Graph data
            metas.push({property: 'og:title',        content: title});
            metas.push({property: 'og:type',         content: 'article'});
            metas.push({property: 'og:url',          content: url});
            metas.push({property: 'og:image',        content: image || SHARE_IMAGE});
            metas.push({property: 'og:description',  content: desc});
            metas.push({property: 'og:site_name',    content: APP_NAME});
            metas.push({property: 'fb:app_id',       content: $STM_Config.fb_app});
            metas.push({property: 'article:tag',     content: category});
            metas.push({property: 'article:published_time', content: created});

            // Twitter card data
            metas.push({name: 'twitter:card',        content: 'summary'});
            metas.push({name: 'twitter:site',        content: TWITTER_HANDLE});
            metas.push({name: 'twitter:title',       content: title});
            metas.push({name: 'twitter:description', content: desc});
            metas.push({name: 'twitter:image',       content: image || TWITTER_SHARE_IMAGE});
        } else {
            addSiteMeta(metas);
        }
    } else { // site
        addSiteMeta(metas);
    }
    return metas;
}
<commit_msg>Revert "Merge branch 'develop'"<commit_after>import extractContent from 'app/utils/ExtractContent';
import {objAccessor} from 'app/utils/Accessors';
import { APP_NAME, APP_URL, SITE_DESCRIPTION, TWITTER_HANDLE, SHARE_IMAGE, TWITTER_SHARE_IMAGE } from 'config/client_config';

function addSiteMeta(metas) {
    metas.push({title: APP_NAME});
    metas.push({property: 'og:type', content: 'website'});
    metas.push({property: 'og:site_name', content: APP_NAME});
    metas.push({property: 'og:title', content: APP_NAME});
    metas.push({property: 'og:description', content: SITE_DESCRIPTION});
    metas.push({property: 'og:image', content: SHARE_IMAGE});
    metas.push({property: 'fb:app_id', content: $STM_Config.fb_app});
    metas.push({name: 'twitter:card', content: 'summary'});
    metas.push({name: 'twitter:site', content: TWITTER_HANDLE});
    metas.push({name: 'twitter:title', content: APP_NAME});
    metas.push({name: 'twitter:description', site_desc: SITE_DESCRIPTION});
    metas.push({name: 'twitter:image', content: SHARE_IMAGE});
}

export default function extractMeta(chain_data, rp) {
    const metas = [];
    if (rp.username && rp.slug) { // post
        const post = `${rp.username}/${rp.slug}`;
        const content = chain_data.content[post];
        if (content) {
            const d = extractContent(objAccessor, content, false);
            const url = 'https://' + APP_URL + d.link;
            const title = d.title + ' — ' + APP_NAME;
            const image = d.image_link ? d.image_link : SHARE_IMAGE;
            const twimage = d.image_link ? d.image_link : TWITTER_SHARE_IMAGE;
            metas.push({title});
            metas.push({canonical: url});
            metas.push({name: 'description', content: d.desc});
            metas.push({property: 'og:type', content: 'article'});
            metas.push({property: 'og:url', content: url});
            metas.push({property: 'og:site_name', content: APP_NAME});
            metas.push({property: 'og:title', content: title});
            metas.push({property: 'og:description', content: d.desc});
            metas.push({property: 'og:image', content: image});
            metas.push({property: 'fb:app_id', content: $STM_Config.fb_app});
            metas.push({name: 'twitter:card', content: 'summary'});
            metas.push({name: 'twitter:site', content: TWITTER_HANDLE});
            metas.push({name: 'twitter:title', content: title});
            metas.push({name: 'twitter:description', content: d.desc});
            metas.push({name: 'twitter:image', content: twimage});
        } else {
            addSiteMeta(metas);
        }
    } else { // site
        addSiteMeta(metas);
    }
    return metas;
}
<|endoftext|>


<commit_before>// ==UserScript==
// @name         Aria2 Helper for 115
// @name:zh-CN   115 网盘 Aria2 助手
// @namespace    Watsilla
// @version      0.1.3
// @description  Add 115 download links to Aria2 via RPC
// @description:zh-CN 直接将所选 115 下载链接发送至 Aria2
// @author       Chao QU
// @match        *://115.com/?ct=file*
// @encoding     utf-8
// @grant        GM_setClipboard
// @grant        GM_xmlhttpRequest
// @grant        GM_log
// @grant        GM_notification
// @grant        unsafeWindow
// @run-at       document-end
// @license      MIT
// @homepage     https://quchao.com/entry/aira2-helper-for-115
// downloadURL   https://github.com/QuChao/Watsilla/raw/master/userScripts/aria2HelperFor115.user.js
// ==/UserScript==
// @version      0.1.0 @ 2017-04-21: Initialize release.
// @version      0.1.1 @ 2017-04-22: One could still copy download link when failed to send it to Aria2.
// @version      0.1.2 @ 2017-04-24: Add comments on configuration items.
// @version      0.1.3 @ 2017-07-28: Fix download link fetching & copying issue on non-Chrome explorers.
// @inspiredBy   https://greasyfork.org/en/scripts/7749-115-download-helper
// @inspiredBy   https://github.com/robbielj/chrome-aria2-integration
/* jshint -W097 */
'use strict';

// Configs
let Configs = {
    'debug_mode'    : false, // 是否开启调试模式
    "sync_clipboard": true,  // 是否将下载链接同步到剪贴板，部分浏览器（如 Safari ）不支持
    'use_http'      : false, // 115 下载链接是否从 https 转换为 http （老版本 Aria2 需要）
    "rpc_path"      : 'http://localhost:6800/jsonrpc', // RPC 地址
    "rpc_user"      : '',    // RPC 用户名（若设置密码，请填写至 token 项）
    "rpc_token"     : ''     // RPC Token ，v1.18.4+ 支持，与用户名认证方式互斥
};

// Debug Func
let debug = Configs.debug_mode ? GM_log : function () {};

// Aria2RPC
let Aria2RPC = (function ($win, $doc) {
    // privates

    // send
    function _addTask() {
        let rpcHeaders = {
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
        };
        let reqParams = {
            'jsonrpc': '2.0',
            'method' : 'aria2.addUri',
            'id'     : (+new Date()).toString(),
            'params' : []
        };

        // auth method
        if ('' !== Configs.rpc_user) {
            // user/password
            rpcHeaders['Authorization'] = 'Basic ' + $win.btoa(Configs.rpc_user + ':' + Configs.rpc_token);
        } else if ('' !== Configs.rpc_token) {
            // secret, since v1.18.4
            reqParams.params.unshift('token:' + Configs.rpc_token);
        }

        return function (link, options, loadHandler, errorHandler) {
            // download link
            if ('undefined' !== typeof options) {
                reqParams.params.push([link]);
            } else {
                // link is required
                //errorHandler({});
                return;
            }

            // options
            if ('undefined' !== typeof options) {
                reqParams.params.push(options);
            }

            // send to aria2, @todo: support metalink?
            GM_xmlhttpRequest({
                method : 'POST',
                url    : Configs.rpc_path,
                headers: rpcHeaders,
                data   : JSON.stringify(reqParams),
                onload : loadHandler,
                onerror: errorHandler
            });
        };
    }

    return {
        // public
        add: _addTask()
    };
})(unsafeWindow, unsafeWindow.document);

// Queue Manager
let QueueManager = (function ($win, $doc) {
    // constants
    const STATUS_SENT_TO_ARIA2 = 1;
    const STATUS_UNFINISHED = 0;
    const STATUS_DOWNLOAD_FAILURE = -1;
    const STATUS_LINK_FETCH_FAILURE = -2;
    const STATUS_UNDOWNLOADABLE = -3;

    // constructor
    function Queue(options) {
        // options
        this.options = Queue.validateOptions(options);

        // err msgs
        this.errMsgs = [];

        // get selected ones
        let selectedNodes = $doc.getElementById('js_cantain_box').querySelectorAll('li.selected');

        // build the queue
        this.queue = Array.from(selectedNodes).map(function (node) {
            return {
                'name'  : node.getAttribute('title'),
                'code'  : node.getAttribute('pick_code'),
                'link'  : null,
                // -3: , -2: failed to fetch link, -1: failed to download, 0: unfinished, 1: sent to aria2
                'status': '1' === node.getAttribute('file_type') ? STATUS_UNFINISHED : STATUS_UNDOWNLOADABLE
            };
        }, this);
    }

    // static
    Queue.defaultOptions = {
        'copyOnly': false
    };
    Queue.validateOptions = function (options) {
        // validation
        for (let key in options) {
            // skip the inherit ones
            if (!options.hasOwnProperty(key)) {
                continue;
            }
            if (!(key in Queue.defaultOptions)) {
                // check existence
                throw Error('Invalid option: ' + key);
            } else if (typeof options[key] !== typeof Queue.defaultOptions[key]) {
                // check type
                throw Error('Invalid option type: ' + key);
            }
        }

        // merge the options
        return Object.assign({}, Queue.defaultOptions, options);
    };

    // methods
    Queue.prototype.errorHandler = function (errCode, idx, resp) {
        debug(resp);

        this.errMsgs.push('File #' + idx + ': ');
        this.errMsgs.push("\t" + 'File Info: ' + JSON.stringify(this.queue[idx]));
        this.errMsgs.push("\t" + 'HTTP Status: ' + resp.status + ' - ' + resp.statusText);

        let errMsg = 'Unknown';
        if ('responseText' in resp) {
            try {
                let err = JSON.parse(resp.responseText);
                errMsg = err.error.message;
            } catch (e) {
                errMsg = e;
            }
        } else if ('msg' in resp) {
            errMsg = resp.msg;
        }

        this.errMsgs.push("\t" + 'Err Msg:' + errMsg);

        // update the status
        this.queue[idx].status = errCode;
        this.next();
    };
    Queue.prototype.downloadHandler = function (idx, resp) {
        debug(resp);

        if (200 === resp.status && 'responseText' in resp) {
            // update the status
            this.queue[idx].status = STATUS_SENT_TO_ARIA2;
            this.next();
        } else {
            // failed
            this.errorHandler.call(this, STATUS_DOWNLOAD_FAILURE, idx, resp);
        }
    };
    Queue.prototype.download = function (idx) {
        // send to aria2
        if (!this.options.copyOnly) {
            Aria2RPC.add(this.queue[idx].link,
                {
                    'referer': $doc.URL,
                    'header' : 'Cookie:' + $doc.cookie // @todo: http cookie?
                },
                this.downloadHandler.bind(this, idx),
                this.errorHandler.bind(this, STATUS_DOWNLOAD_FAILURE, idx)
            );
        } else {
            // update the status, @todo: another status code?
            this.queue[idx].status = STATUS_SENT_TO_ARIA2;
            this.next();
        }
    };
    Queue.prototype.fetchLinkHandler = function (idx, resp) {
        debug(resp);

        if ('file_url' in resp) {
            // update the link
            this.queue[idx].link = Configs.use_http
                ? resp.file_url.replace('https://', 'http://') // http only?
                : resp.file_url;
            this.next();
        } else {
            this.errorHandler.call(this, STATUS_LINK_FETCH_FAILURE, idx, resp);
        }
    };
    Queue.prototype.fetchLink = function (idx) {
        // get the download link first
        $win.top.UA$.ajax({
            url      : 'files/download?pickcode=' + this.queue[idx].code,
            type     : 'GET',
            dataType : 'json',
            cache    : false,
            success  : this.fetchLinkHandler.bind(this, idx),
            error    : this.errorHandler.bind(this, STATUS_LINK_FETCH_FAILURE, idx)
        })
    };
    Queue.prototype.next = function () {
        // check if it's the queue is empty
        let nextIdx = this.queue.findIndex(function (file) {
            return STATUS_UNFINISHED === file.status;
        });

        // handle the next file
        if (-1 === nextIdx) {
            let report = this.queue.reduce(function (accumulator, file) {
                switch (file.status) {
                    // task finished
                    case STATUS_SENT_TO_ARIA2:
                        accumulator.finished += 1;
                    case STATUS_DOWNLOAD_FAILURE:
                        accumulator.links.push(file.link);
                        break;
                    // task finished
                    case STATUS_UNDOWNLOADABLE:
                        accumulator.undownloadable += 1;
                        break;
                }

                return accumulator;
            }, {
                'links'         : [],
                'finished'      : 0,
                'undownloadable': 0
            });

            let queueSize = this.queue.length;
            let msg = [];
            if (queueSize === report.undownloadable) {
                msg.push('所选 ' + queueSize + ' 项类型均为目录，暂不支持。');
            } else {
                msg.push('所选 ' + queueSize + ' 项已处理完毕：');

                if (!this.options.copyOnly) {
                    if (0 < report.finished) {
                        msg.push((queueSize === report.finished ? '全部' : '其中 ' + report.finished + ' 项') + '成功发送至 Aria2 进行下载。');
                    } else {
                        msg.push((0 === report.undownloadable ? '全部' : '其中 ' + (queueSize - report.undownloadable) + ' 项') + '发送至 Aria2 失败。');
                    }
                }

                if (this.options.copyOnly || Configs.sync_clipboard) {
                    let downloadLinks = report.links.join("\n");
                    if (false === /\sSafari\/\d+\.\d+\.\d+/.test($win.navigator.userAgent)) {
                        // sync to clipboard
                        GM_setClipboard(downloadLinks, 'text');
                        msg.push('下载地址已同步至剪贴板。');
                    } else if (this.options.copyOnly) {
                        prompt('本浏览器不支持访问剪贴板，请手动全选复制', downloadLinks);
                    }
                }

                if (0 < report.undownloadable) {
                    msg.push('另有 ' + report.undownloadable + ' 项类型为目录，暂不支持。');
                }
            }

            // notify the user
            GM_notification(msg.join("\n"));

            if (this.errMsgs.length) {
                throw Error(this.errMsgs.join("\n"));
            }
        } else if (null === this.queue[nextIdx].link) {
            // fetch link with the code, then download it
            this.fetchLink(nextIdx);
        } else {
            // fetch link with the code, then download it
            this.download(nextIdx);
        }
    };

    return Queue;
})(unsafeWindow, unsafeWindow.document);

// UI Helper
let UiHelper = (function ($win, $doc) {
    // privates
    let _triggerId = 'aria2Trigger';

    function _clickHandler(evt) {
        (new QueueManager({
            'copyOnly': evt.ctrlKey || evt.metaKey // press ctrl/cmd key while clicking to copy the download link only
        })).next();

        // kill the listener
        evt.target.removeEventListener('click', _clickHandler, false);
    }

    function _recordHandler(record) {
        //debug(record);

        // place the trigger
        let ariaTrigger = $doc.createElement('li');
        ariaTrigger.id = _triggerId;
        ariaTrigger.title = '按住 Ctrl 点击可仅复制下载链接';
        ariaTrigger.innerHTML = '发送至 Aria2';
        record.target.firstChild.appendChild(ariaTrigger);

        // make it clickable
        ariaTrigger.addEventListener('click', _clickHandler, false);

        // stop the observation
        //_observer.disconnect();

        return true;
    }

    // initialization
    function _init() {
        let container = $doc.getElementById('js_operate_box');

        // create a observer on the container
        new MutationObserver(function (records) {
            records.filter(function () {
                return null === $doc.getElementById(_triggerId);
            }).some(_recordHandler);
        }).observe(container, {
            'childList': true,
        });
    }

    return {
        // public
        init: _init
    };
})(unsafeWindow, unsafeWindow.document);

// fire
UiHelper.init();
<commit_msg>Upgrade Aria2 Helper for 115.com to v0.1.4.<commit_after>// ==UserScript==
// @name         Aria2 Helper for 115
// @name:zh-CN   115 网盘 Aria2 助手
// @namespace    Watsilla
// @version      0.1.3
// @description  Add 115 download links to Aria2 via RPC
// @description:zh-CN 直接将所选 115 下载链接发送至 Aria2
// @author       Chao QU
// @match        *://115.com/?ct=file*
// @encoding     utf-8
// @grant        GM_setClipboard
// @grant        GM_xmlhttpRequest
// @grant        GM_log
// @grant        GM_notification
// @grant        unsafeWindow
// @run-at       document-end
// @license      MIT
// @homepage     https://quchao.com/entry/aira2-helper-for-115
// downloadURL   https://github.com/QuChao/Watsilla/raw/master/userScripts/aria2HelperFor115.user.js
// ==/UserScript==
// @version      0.1.0 @ 2017-04-21: Initialize release.
// @version      0.1.1 @ 2017-04-22: One could still copy download link when failed to send it to Aria2.
// @version      0.1.2 @ 2017-04-24: Add comments on configuration items.
// @version      0.1.3 @ 2017-07-28: Fix download link fetching & copying issue on non-Chrome explorers.
// @version      0.1.4 @ 2017-10-18: Fix an issue that a batch task only sends the first file to Aria2.
// @inspiredBy   https://greasyfork.org/en/scripts/7749-115-download-helper
// @inspiredBy   https://github.com/robbielj/chrome-aria2-integration
/* jshint -W097 */
'use strict';

// Configs
let Configs = {
    'debug_mode'    : false, // 是否开启调试模式
    "sync_clipboard": true,  // 是否将下载链接同步到剪贴板，部分浏览器（如 Safari ）不支持
    'use_http'      : false, // 115 下载链接是否从 https 转换为 http （老版本 Aria2 需要）
    "rpc_path"      : 'http://localhost:6800/jsonrpc', // RPC 地址
    "rpc_user"      : '',    // RPC 用户名（若设置密码，请填写至 token 项）
    "rpc_token"     : ''     // RPC Token ，v1.18.4+ 支持，与用户名认证方式互斥
};

// Debug Func
let debug = Configs.debug_mode ? GM_log : function () {};
let emptyFunc = function () {};

// Aria2RPC
let Aria2RPC = (function ($win, $doc) {
    // privates

    // send
    function _addTask() {
        let rpcHeaders = {
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
        };

        // auth method, pt.1
        if ('' !== Configs.rpc_user) {
            // user/password
            rpcHeaders['Authorization'] = 'Basic ' + $win.btoa(Configs.rpc_user + ':' + Configs.rpc_token);
        }

        return function (link, options, loadHandler, errorHandler) {
            // new task
            let reqParams = {
                'jsonrpc': '2.0',
                'method' : 'aria2.addUri',
                'id'     : (+new Date()).toString(),
                'params' : []
            };

            // auth method, pt.2
            if ('' === Configs.rpc_user && '' !== Configs.rpc_token) {
                // secret, since v1.18.4
                reqParams.params.unshift('token:' + Configs.rpc_token);
            }

            // download link
            if ('undefined' !== typeof link) {
                // @todo: multiple sources?
                reqParams.params.push([link]);
            } else {
                // link is required
                //errorHandler({});
                return;
            }

            // options
            if ('undefined' !== typeof options) {
                reqParams.params.push(options);
            }

            // send to aria2, @todo: support metalink?
            GM_xmlhttpRequest({
                method : 'POST',
                url    : Configs.rpc_path,
                headers: rpcHeaders,
                data   : JSON.stringify(reqParams),
                onload : loadHandler || emptyFunc,
                onerror: errorHandler || emptyFunc
            });
        };
    }

    return {
        // public
        add: _addTask()
    };
})(unsafeWindow, unsafeWindow.document);

// Queue Manager
let QueueManager = (function ($win, $doc) {
    // constants
    const STATUS_SENT_TO_ARIA2 = 1;
    const STATUS_UNFINISHED = 0;
    const STATUS_DOWNLOAD_FAILURE = -1;
    const STATUS_LINK_FETCH_FAILURE = -2;
    const STATUS_UNDOWNLOADABLE = -3;

    // constructor
    function Mgr(options) {
        // options
        this.options = Mgr.validateOptions(options);

        // err msgs
        this.errMsgs = [];

        // get selected ones
        let selectedNodes = $doc.getElementById('js_cantain_box').querySelectorAll('li.selected');

        // build the queue
        this.queue = Array.from(selectedNodes).map(function (node) {
            return {
                'name'  : node.getAttribute('title'),
                'code'  : node.getAttribute('pick_code'),
                'link'  : null,
                // -3: , -2: failed to fetch link, -1: failed to download, 0: unfinished, 1: sent to aria2
                'status': '1' === node.getAttribute('file_type') ? STATUS_UNFINISHED : STATUS_UNDOWNLOADABLE
            };
        }, this);
    }

    // static
    Mgr.defaultOptions = {
        'copyOnly': false
    };
    Mgr.validateOptions = function (options) {
        // validation
        for (let key in options) {
            // skip the inherit ones
            if (!options.hasOwnProperty(key)) {
                continue;
            }
            if (!(key in Mgr.defaultOptions)) {
                // check existence
                throw Error('Invalid option: ' + key);
            } else if (typeof options[key] !== typeof Mgr.defaultOptions[key]) {
                // check type
                throw Error('Invalid option type: ' + key);
            }
        }

        // merge the options
        return Object.assign({}, Mgr.defaultOptions, options);
    };

    // methods
    Mgr.prototype.errorHandler = function (errCode, idx, resp) {
        debug(resp);

        this.errMsgs.push('File #' + idx + ': ');
        this.errMsgs.push("\t" + 'File Info: ' + JSON.stringify(this.queue[idx]));
        this.errMsgs.push("\t" + 'HTTP Status: ' + resp.status + ' - ' + resp.statusText);

        let errMsg = 'Unknown';
        if ('responseText' in resp) {
            try {
                let err = JSON.parse(resp.responseText);
                errMsg = err.error.message;
            } catch (e) {
                errMsg = e;
            }
        } else if ('msg' in resp) {
            errMsg = resp.msg;
        }

        this.errMsgs.push("\t" + 'Err Msg:' + errMsg);

        // update the status
        this.queue[idx].status = errCode;
        this.next();
    };
    Mgr.prototype.downloadHandler = function (idx, resp) {
        debug(resp);

        if (200 === resp.status && 'responseText' in resp) {
            // update the status
            this.queue[idx].status = STATUS_SENT_TO_ARIA2;
            this.next();
        } else {
            // failed
            this.errorHandler.call(this, STATUS_DOWNLOAD_FAILURE, idx, resp);
        }
    };
    Mgr.prototype.download = function (idx) {
        // send to aria2
        if (!this.options.copyOnly) {
            Aria2RPC.add(this.queue[idx].link,
                {
                    'referer': $doc.URL,
                    'header' : 'Cookie:' + $doc.cookie // @todo: http cookie?
                },
                this.downloadHandler.bind(this, idx),
                this.errorHandler.bind(this, STATUS_DOWNLOAD_FAILURE, idx)
            );
        } else {
            // update the status, @todo: another status code?
            this.queue[idx].status = STATUS_SENT_TO_ARIA2;
            this.next();
        }
    };
    Mgr.prototype.fetchLinkHandler = function (idx, resp) {
        debug(resp);

        if ('file_url' in resp) {
            // update the link
            this.queue[idx].link = Configs.use_http
                ? resp.file_url.replace('https://', 'http://') // http only?
                : resp.file_url;
            this.next();
        } else {
            this.errorHandler.call(this, STATUS_LINK_FETCH_FAILURE, idx, resp);
        }
    };
    Mgr.prototype.fetchLink = function (idx) {
        // get the download link first
        $win.top.UA$.ajax({
            url      : 'files/download?pickcode=' + this.queue[idx].code,
            type     : 'GET',
            dataType : 'json',
            cache    : false,
            success  : this.fetchLinkHandler.bind(this, idx),
            error    : this.errorHandler.bind(this, STATUS_LINK_FETCH_FAILURE, idx)
        })
    };
    Mgr.prototype.next = function () {
        // check if it's the queue is empty
        let nextIdx = this.queue.findIndex(function (file) {
            return STATUS_UNFINISHED === file.status;
        });

        // handle the next file
        if (-1 === nextIdx) {
            let report = this.queue.reduce(function (accumulator, file) {
                switch (file.status) {
                    // task finished
                    case STATUS_SENT_TO_ARIA2:
                        accumulator.finished += 1;
                    case STATUS_DOWNLOAD_FAILURE:
                        accumulator.links.push(file.link);
                        break;
                    // task finished
                    case STATUS_UNDOWNLOADABLE:
                        accumulator.undownloadable += 1;
                        break;
                }

                return accumulator;
            }, {
                'links'         : [],
                'finished'      : 0,
                'undownloadable': 0
            });

            let queueSize = this.queue.length;
            let msg = [];
            if (queueSize === report.undownloadable) {
                msg.push('所选 ' + queueSize + ' 项类型均为目录，暂不支持。');
            } else {
                msg.push('所选 ' + queueSize + ' 项已处理完毕：');

                if (!this.options.copyOnly) {
                    if (0 < report.finished) {
                        msg.push((queueSize === report.finished ? '全部' : '其中 ' + report.finished + ' 项') + '成功发送至 Aria2 进行下载。');
                    } else {
                        msg.push((0 === report.undownloadable ? '全部' : '其中 ' + (queueSize - report.undownloadable) + ' 项') + '发送至 Aria2 失败。');
                    }
                }

                if (this.options.copyOnly || Configs.sync_clipboard) {
                    let downloadLinks = report.links.join("\n");
                    if (false === /\sSafari\/\d+\.\d+\.\d+/.test($win.navigator.userAgent)) {
                        // sync to clipboard
                        GM_setClipboard(downloadLinks, 'text');
                        msg.push('下载地址已同步至剪贴板。');
                    } else if (this.options.copyOnly) {
                        prompt('本浏览器不支持访问剪贴板，请手动全选复制', downloadLinks);
                    }
                }

                if (0 < report.undownloadable) {
                    msg.push('另有 ' + report.undownloadable + ' 项类型为目录，暂不支持。');
                }
            }

            // notify the user
            GM_notification(msg.join("\n"));

            if (this.errMsgs.length) {
                throw Error(this.errMsgs.join("\n"));
            }
        } else if (null === this.queue[nextIdx].link) {
            // fetch link with the code
            this.fetchLink(nextIdx);
        } else {
            // download it
            this.download(nextIdx);
        }
    };

    return Mgr;
})(unsafeWindow, unsafeWindow.document);

// UI Helper
let UiHelper = (function ($win, $doc) {
    // privates
    let _triggerId = 'aria2Trigger';

    function _clickHandler(evt) {
        (new QueueManager({
            'copyOnly': evt.ctrlKey || evt.metaKey // press ctrl/cmd key while clicking to copy the download link only
        })).next();

        // kill the listener
        evt.target.removeEventListener('click', _clickHandler, false);
    }

    function _recordHandler(record) {
        //debug(record);

        // place the trigger
        let ariaTrigger = $doc.createElement('li');
        ariaTrigger.id = _triggerId;
        ariaTrigger.title = '按住 Ctrl 点击可仅复制下载链接';
        ariaTrigger.innerHTML = '发送至 Aria2';
        record.target.firstChild.appendChild(ariaTrigger);

        // make it clickable
        ariaTrigger.addEventListener('click', _clickHandler, false);

        // stop the observation
        //_observer.disconnect();

        return true;
    }

    // initialization
    function _init() {
        let container = $doc.getElementById('js_operate_box');

        // create a observer on the container
        new MutationObserver(function (records) {
            records.filter(function () {
                return null === $doc.getElementById(_triggerId);
            }).some(_recordHandler);
        }).observe(container, {
            'childList': true,
        });
    }

    return {
        // public
        init: _init
    };
})(unsafeWindow, unsafeWindow.document);

// fire
UiHelper.init();
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import types from '../../utils/types';
import h from '../../utils/helpers';
import renderOption from '../../utils/renderOption';
import Page from '../../containers/Page';
import TitleBar from '../../components/TitleBar';
import Button from '../../components/Button';
import Input from '../../components/Input';

export default class Entry extends Component {
  static propTypes = {
    ...types.entries,
    params: PropTypes.shape({
      id: PropTypes.string.isRequired,
    }).isRequired,
  }

  static defaultProps = {
    title: '',
  }

  constructor(props) {
    super(props);

    const { entries, params } = props;
    const { id } = params;
    this.renderFields = this.renderFields.bind(this);
    this.entry = entries.entries.find(e => e._id === id);
  }

  renderFields(field) {
    const foundField = this.props.fields.fields.find(f => f._id === field.fieldId);
    return renderOption(foundField, field.value);
  }

  render() {
    const { section, title, _id, fields } = this.entry;
    const { sections } = this.props;
    const sectionSlug = h.getSlugFromId(sections.sections, section);
    const sectionName = h.getPropFromProp(sections.sections, { _id: section }, 'title');

    const links = [
      { label: sectionName, path: `/admin/entries/${sectionSlug}` },
      { label: title, path: `/admin/entries/${sectionSlug}/${_id}` },
    ];

    return (
      <Page name="entry" links={links}>
        <TitleBar title={title}>
          <Button>Save Entry</Button>
        </TitleBar>

            <Input label="Title" defaultValue={title} name={title} full />
            {fields.map(field => this.renderFields(field))}

      </Page>
    );
  }
}
<commit_msg>:scroll: Require Title in Entry and Secion<commit_after>import React, { Component, PropTypes } from 'react';
import types from '../../utils/types';
import h from '../../utils/helpers';
import renderOption from '../../utils/renderOption';
import Page from '../../containers/Page';
import TitleBar from '../../components/TitleBar';
import Button from '../../components/Button';
import Input from '../../components/Input';

export default class Entry extends Component {
  static propTypes = {
    ...types.entries,
    params: PropTypes.shape({
      id: PropTypes.string.isRequired,
    }).isRequired,
  }

  static defaultProps = {
    title: '',
  }

  constructor(props) {
    super(props);

    const { entries, params } = props;
    const { id } = params;
    this.renderFields = this.renderFields.bind(this);
    this.entry = entries.entries.find(e => e._id === id);
  }

  renderFields(field) {
    const foundField = this.props.fields.fields.find(f => f._id === field.fieldId);
    return renderOption(foundField, field.value);
  }

  render() {
    const { section, title, _id, fields } = this.entry;
    const { sections } = this.props;
    const sectionSlug = h.getSlugFromId(sections.sections, section);
    const sectionName = h.getPropFromProp(sections.sections, { _id: section }, 'title');

    const links = [
      { label: sectionName, path: `/admin/entries/${sectionSlug}` },
      { label: title, path: `/admin/entries/${sectionSlug}/${_id}` },
    ];

    return (
      <Page name="entry" links={links}>
        <TitleBar title={title}>
          <Button>Save Entry</Button>
        </TitleBar>

            <Input label="Title" defaultValue={title} name={title} full required />
            {fields.map(field => this.renderFields(field))}

      </Page>
    );
  }
}
<|endoftext|>


<commit_before>var Post = {
    init: function() {
        if(localStorage.getItem('share_url')) {
            Post_ajaxCreate();
            MovimTpl.showPanel();
        }
    },

    setEmbed: function() {
        if(localStorage.getItem('share_url')) {
            var embed = document.querySelector('input[name=embed]');
            embed.value = localStorage.getItem('share_url');
            embed.onpaste();
        }
    }
}

MovimWebsocket.attach(function() {
    Post.init();
});
<commit_msg>- Clear the shared url<commit_after>var Post = {
    init: function() {
        if(localStorage.getItem('share_url')) {
            Post_ajaxCreate();
            MovimTpl.showPanel();
        }
    },

    setEmbed: function() {
        if(localStorage.getItem('share_url')) {
            var embed = document.querySelector('input[name=embed]');
            embed.value = localStorage.getItem('share_url');
            embed.onpaste();
            localStorage.removeItem('share_url');
        }
    }
}

MovimWebsocket.attach(function() {
    Post.init();
});
<|endoftext|>