<commit_before>import React, {Component} from 'react'
import { connect } from 'react-redux'
import Checkbox from '../inspector/fields/Checkbox'
import Assets from './Assets'
import { warn, inProgress } from '../../redux/status'
import Importer from '../ui/Importer'

import './importer.css'

import { getNewAssetID } from '../../redux/project/assets/reducers'
import { loadAssets } from '../../redux/project/loader'

const fs = window.require('fs-extra')
const path = require('path')
const ipcRenderer = window.require('electron').ipcRenderer

export const TYPE_MAP = {
    json: 'particles',
    gif: 'animated',
    png: 'sprite'
}

class AssetImporter extends Component {
    static id = 0

    constructor(props) {
        super(props)

        this.state = {
            assets: {},
            folders: [],
            assetsPaths: {},
            duplicate: false
        }

        this.toggleDuplicate = this.toggleDuplicate.bind(this)
        this.createElements = this.createElements.bind(this)
        this.resetImporter = this.resetImporter.bind(this)
        this.readImage = this.readImage.bind(this)
        this.readProject = this.readProject.bind(this)
        this.readFile = this.readFile.bind(this)
        this.import = this.import.bind(this)
    }

    toggleDuplicate() {
        this.setState({ duplicate: !this.state.duplicate })
    }

    createElements({ items, selected, toggleItem }) {
        const CustomAsset = ({asset, id, small}) => {
            const filepath = Object.keys(this.state.assetsPaths).find(filepath =>
                this.state.assetsPaths[filepath].assets.includes(id))
            const assetsPath = this.state.assetsPaths[filepath].assetsPath
            return

                {small &&

{asset.name}

}
                {small || }
                {small ||
{asset.name}
}

        }

        const CustomFolder = ({tab, row, jumpToFolder}) => {
            const assets = Object.keys(items).filter(id => items[id].tab === tab)
            const allSelected = assets.every(a => selected.includes(a))
            return

 jumpToFolder(row)}>

{tab}

 allSelected === selected.includes(id)))}>

        }

        const CustomTitle = ({tab}) =>

{tab}

        return

            <Assets
                isAssetImporter={true}
                assets={items}
                folders={this.state.folders}
                selected={selected}
                rect={{
                    width: Math.min(900, .9 * window.innerWidth),
                    // Magic number alert: height of the footer and bar
                    height: Math.min(1000, .8 * window.innerHeight) - 67
                }}
                CustomAsset={CustomAsset}
                CustomFolder={CustomFolder}
                CustomTitle={CustomTitle} />

    }

    resetImporter() {
        this.setState({
            assets: {},
            folders: [],
            assetsPath: {},
            duplicate: false
        })
    }

    readProject(filepath) {
        const project = fs.readJsonSync(filepath)
        const assetsPath = path.join(filepath, project.assetsPath || '../assets')
        const { assets, folders, error, errors } = loadAssets(project, assetsPath, [])
        if (error)
            this.props.dispatch(warn(error))
        if (errors)
            errors.forEach(e => this.props.dispatch(warn(e)))

        this.setState({
            assets: {...this.state.assets, ...assets},
            folders: [...new Set([...this.state.folders, ...folders])],
            assetsPaths: {
                ...this.state.assetsPaths,
                [filepath]: {
                    assetsPath,
                    assets: Object.keys(assets)
                }
            }
        })

        return assets
    }

    readImage(filepath, fileType) {
        const assetId = getNewAssetID()
        const id = `${this.props.self}:${assetId}`
        const assetsPath = ''
        const asset = {
            name: path.basename(filepath, `.${fileType}`),
            type: TYPE_MAP[fileType],
            tab: 'unsorted',
            location: filepath,
            thumbnail: fileType === 'json' ? 'temp' : null,
            version: 0,
            panning: []
        }

        this.setState({
            assets: { ...this.state.assets, [id]: asset },
            folders: [...new Set([...this.state.folders, 'unsorted'])],
            assetsPaths: {
                ...this.state.assetsPaths,
                [filepath]: {
                    assetsPath,
                    assets: [ id ]
                }
            }
        })

        return {
            [id]: asset
        }
    }

    readFile(filepath) {
        switch (path.extname(filepath)) {
            case '.png':
            case '.gif':
            case '.json':
                return this.readImage(filepath, path.extname(filepath).slice(1))
            default: case '.babble':
                return this.readProject(filepath)
        }
    }

    import(filepath, items) {
        const statusId = `import-${AssetImporter.id++}`

        const assets = Object.keys(items).reduce((acc, curr) => {
            const id = this.state.duplicate ? `${this.props.self}:${getNewAssetID()}` : curr
            if (id in acc) return acc
            acc[id] = this.state.assets[curr]
            return acc
        }, {})

        // Create status message for showing progress on importing assets
        this.props.dispatch(inProgress(statusId, Object.keys(items).length, 'Importing assets...'))

        // Make the background window copy all the necessary files over
        ipcRenderer.send('background', 'import',
            this.state.duplicate,
            // Assets to add
            assets,
            // Assets path to copy assets from
            this.state.assetsPaths[filepath].assetsPath,
            // Assets path to move assets to
            path.join(this.props.project, this.props.assetsPath),
            // ID for sending status updates as new assets get added
            statusId
        )
    }

    shouldComponentUpdate(nextProps, nextState) {
        return this.state.duplicate !== nextState.duplicate
    }

    render() {
        return <Importer
            title="Import Assets"
            createElements={this.createElements}
            readFile={this.readFile}
            import={this.import}
            onOpen={this.resetImporter}
            filters={[
                {name: 'Image', extensions: ['png']},
                {name: 'Animated Image', extensions: ['gif']},
                {name: 'Particle Effect', extensions: ['json']}]}
            footers={[<Checkbox
                inline={true}
                title="Duplicate Assets"
                key="2.5"
                value={this.state.duplicate}
                onChange={this.toggleDuplicate}/>]} />
    }
}

function mapStateToProps(state) {
    return {
        assets: Object.keys(state.project.assets),
        project: state.project.project,
        assetsPath: state.project.settings.assetsPath,
        self: state.self
    }
}

export default connect(mapStateToProps)(AssetImporter)
<commit_msg>Fixed importing images not working on windows<commit_after>import React, {Component} from 'react'
import { connect } from 'react-redux'
import Checkbox from '../inspector/fields/Checkbox'
import Assets from './Assets'
import { warn, inProgress } from '../../redux/status'
import Importer from '../ui/Importer'

import './importer.css'

import { getNewAssetID } from '../../redux/project/assets/reducers'
import { loadAssets } from '../../redux/project/loader'

const fs = window.require('fs-extra')
const path = require('path')
const ipcRenderer = window.require('electron').ipcRenderer

export const TYPE_MAP = {
    json: 'particles',
    gif: 'animated',
    png: 'sprite'
}

class AssetImporter extends Component {
    static id = 0

    constructor(props) {
        super(props)

        this.state = {
            assets: {},
            folders: [],
            assetsPaths: {},
            duplicate: false
        }

        this.toggleDuplicate = this.toggleDuplicate.bind(this)
        this.createElements = this.createElements.bind(this)
        this.resetImporter = this.resetImporter.bind(this)
        this.readImage = this.readImage.bind(this)
        this.readProject = this.readProject.bind(this)
        this.readFile = this.readFile.bind(this)
        this.import = this.import.bind(this)
    }

    toggleDuplicate() {
        this.setState({ duplicate: !this.state.duplicate })
    }

    createElements({ items, selected, toggleItem }) {
        const CustomAsset = ({asset, id, small}) => {
            const filepath = Object.keys(this.state.assetsPaths).find(filepath =>
                this.state.assetsPaths[filepath].assets.includes(id))
            const assetsPath = this.state.assetsPaths[filepath].assetsPath
            return

                {small &&

{asset.name}

}
                {small || }
                {small ||
{asset.name}
}

        }

        const CustomFolder = ({tab, row, jumpToFolder}) => {
            const assets = Object.keys(items).filter(id => items[id].tab === tab)
            const allSelected = assets.every(a => selected.includes(a))
            return

 jumpToFolder(row)}>

{tab}

 allSelected === selected.includes(id)))}>

        }

        const CustomTitle = ({tab}) =>

{tab}

        return

            <Assets
                isAssetImporter={true}
                assets={items}
                folders={this.state.folders}
                selected={selected}
                rect={{
                    width: Math.min(900, .9 * window.innerWidth),
                    // Magic number alert: height of the footer and bar
                    height: Math.min(1000, .8 * window.innerHeight) - 67
                }}
                CustomAsset={CustomAsset}
                CustomFolder={CustomFolder}
                CustomTitle={CustomTitle} />

    }

    resetImporter() {
        this.setState({
            assets: {},
            folders: [],
            assetsPath: {},
            duplicate: false
        })
    }

    readProject(filepath) {
        const project = fs.readJsonSync(filepath)
        const assetsPath = path.join(filepath, project.assetsPath || '../assets')
        const { assets, folders, error, errors } = loadAssets(project, assetsPath, [])
        if (error)
            this.props.dispatch(warn(error))
        if (errors)
            errors.forEach(e => this.props.dispatch(warn(e)))

        this.setState({
            assets: {...this.state.assets, ...assets},
            folders: [...new Set([...this.state.folders, ...folders])],
            assetsPaths: {
                ...this.state.assetsPaths,
                [filepath]: {
                    assetsPath,
                    assets: Object.keys(assets)
                }
            }
        })

        return assets
    }

    readImage(filepath, fileType) {
        const assetId = getNewAssetID()
        const id = `${this.props.self}:${assetId}`
        const assetsPath = ''
        const asset = {
            name: path.basename(filepath, `.${fileType}`),
            type: TYPE_MAP[fileType],
            tab: 'unsorted',
            location: filepath,
            thumbnail: fileType === 'json' ? 'temp' : null,
            version: 0,
            panning: []
        }

        this.setState({
            assets: { ...this.state.assets, [id]: asset },
            folders: [...new Set([...this.state.folders, 'unsorted'])],
            assetsPaths: {
                ...this.state.assetsPaths,
                [filepath]: {
                    assetsPath,
                    assets: [ id ]
                }
            }
        })

        return {
            [id]: asset
        }
    }

    readFile(filepath) {
        switch (path.extname(filepath)) {
            case '.png':
            case '.gif':
            case '.json':
                return this.readImage(filepath, path.extname(filepath).slice(1))
            default: case '.babble':
                return this.readProject(filepath)
        }
    }

    import(filepath, items) {
        const statusId = `import-${AssetImporter.id++}`

        const assets = Object.keys(items).reduce((acc, curr) => {
            const id = this.state.duplicate ? `${this.props.self}:${getNewAssetID()}` : curr
            if (id in acc) return acc
            acc[id] = this.state.assets[curr]
            return acc
        }, {})

        // Create status message for showing progress on importing assets
        this.props.dispatch(inProgress(statusId, Object.keys(items).length, 'Importing assets...'))

        // Make the background window copy all the necessary files over
        ipcRenderer.send('background', 'import',
            this.state.duplicate,
            // Assets to add
            assets,
            // Assets path to copy assets from
            this.state.assetsPaths[filepath].assetsPath,
            // Assets path to move assets to
            path.join(...this.props.project.split('\\'), this.props.assetsPath),
            // ID for sending status updates as new assets get added
            statusId
        )
    }

    shouldComponentUpdate(nextProps, nextState) {
        return this.state.duplicate !== nextState.duplicate
    }

    render() {
        return <Importer
            title="Import Assets"
            createElements={this.createElements}
            readFile={this.readFile}
            import={this.import}
            onOpen={this.resetImporter}
            filters={[
                {name: 'Image', extensions: ['png']},
                {name: 'Animated Image', extensions: ['gif']},
                {name: 'Particle Effect', extensions: ['json']}]}
            footers={[<Checkbox
                inline={true}
                title="Duplicate Assets"
                key="2.5"
                value={this.state.duplicate}
                onChange={this.toggleDuplicate}/>]} />
    }
}

function mapStateToProps(state) {
    return {
        assets: Object.keys(state.project.assets),
        project: state.project.project,
        assetsPath: state.project.settings.assetsPath,
        self: state.self
    }
}

export default connect(mapStateToProps)(AssetImporter)
<|endoftext|>


<commit_before>import React from 'react';
import './SubComponent.scss';

const SubComponent = () => (

    I'm blue

);

export default SubComponent;
<commit_msg>Message<commit_after>import React from 'react';
import './SubComponent.scss';

var Message = React.createClass({
  render() {
      return (

              {this.props.user}:
              {this.props.text}

      );
  }
});

export default SubComponent;
<|endoftext|>


<commit_before>(() => {
  const $title = document.querySelector('.js-issue-title');
  if (!$title) {
    return;
  }

  chrome.storage.local.get('inlineLinks', (options) => {
    let title = $title.innerHTML.replace(/(<a[^>]+>|⬆|<\/a>)/g, '');

    title.match(/#\d+(?=[\],\s\d#]*\])/g).forEach((tag) => {
      const url = `https://www.pivotaltracker.com/story/show/${tag.match(/\d+/)}`;
      const attrs = `href="${url}" target="_blank"`;

      const replacement = options.inlineLinks === false ?
        `${tag}<a ${attrs}>⬆</a>` :
        `<a ${attrs}>${tag}</a>`;

      title = title.replace(tag, replacement);
    });

    $title.innerHTML = title;
  });
})();
<commit_msg>update regex and external path<commit_after>(() => {
  const $title = document.querySelector('.js-issue-title');
  if (!$title) {
    return;
  }

  chrome.storage.local.get('inlineLinks', (options) => {
    let title = $title.innerHTML.replace(/(<a[^>]+>|⬆|<\/a>)/g, '');

    title.match(/[a-zA-Z0-9-]+(?=[\],\s\d#]*\])/g).forEach((tag) => {
      const url = `https://nextcapital.atlassian.net/browse/${tag}`;
      const attrs = `href="${url}" target="_blank"`;

      const replacement = options.inlineLinks === false ?
        `${tag}<a ${attrs}>⬆</a>` :
        `<a ${attrs}>${tag}</a>`;

      title = title.replace(tag, replacement);
    });

    $title.innerHTML = title;
  });
})();
<|endoftext|>


<commit_before>var config = {
  job_basename: require('path').basename(process.cwd()) + '_' + process.env.TRAVIS_JOB_ID,

  sauceSeleniumAddress: 'localhost:4445/wd/hub',
  sauceUser: process.env.SAUCE_USERNAME,
  sauceKey: process.env.SAUCE_ACCESS_KEY,

  browsers: [
    {browserName: 'chrome'},
    {browserName: 'firefox'},
    {browserName: 'safari', version: 7, platform: 'OS X 10.9'},
    {browserName: 'safari', version: 6, platform: 'OS X 10.8'},
    {browserName: 'internet explorer', version: 11, platform: 'Windows 8.1'},
    {browserName: 'internet explorer', version: 10, platform: 'Windows 8'},
    {browserName: 'internet explorer', version: 9, platform: 'Windows 7'},
    {browserName: 'Android', version:'4.4', platform: 'Linux'},
    {browserName: 'Android', version:'4.1', platform: 'Linux'},
    {browserName: 'Android', version:'4.0', platform: 'Linux'},
    {browserName: 'iphone', deviceName: 'iPhone Simulator', version:'8.1', platform: 'OS X 10.9'},
    {browserName: 'iphone', deviceName: 'iPhone Simulator', version:'8.0', platform: 'OS X 10.9'},
    {browserName: 'iphone', deviceName: 'iPhone Simulator', version:'7.1', platform: 'OS X 10.9'},
    {browserName: 'iphone', deviceName: 'Phone Simulator', version:'6.1', platform: 'OS X 10.8'},
    {browserName: 'ipad', version:'8.1', platform: 'OS X 10.9'},
    {browserName: 'ipad', version:'8.0', platform: 'OS X 10.9'},
    {browserName: 'ipad', version:'7.1', platform: 'OS X 10.9'},
    {browserName: 'ipad', version:'6.1', platform: 'OS X 10.8'}
  ],

  specs: [process.cwd() + '/spec.js'],

  baseUrl: 'http://localhost:3000/',

  // This enable testing on none angular pages without test code change.
  onPrepare: function () {
    element = browser.element;
    browser.ignoreSynchronization = true;
  },

  jasmineNodeOpts: {
    showColors: true,
    defaultTimeoutInterval: 30000
  }
};

config.multiCapabilities = config.browsers.map(function (cfg) {
  cfg.build = config.job_basename;
  cfg['tunnel-identifier'] = process.env.TRAVIS_JOB_NUMBER;
  cfg.name = 'FluxEx browser test for example: ' + config.job_basename;
  cfg.public = 'public';
  cfg.tags = [process.env.TRAVIS_JOB_ID, process.env.TRAVIS_COMMIT, 'fluxex'];
  return cfg;
});

module.exports.config = config;<commit_msg>fix 1 typo<commit_after>var config = {
  job_basename: require('path').basename(process.cwd()) + '_' + process.env.TRAVIS_JOB_ID,

  sauceSeleniumAddress: 'localhost:4445/wd/hub',
  sauceUser: process.env.SAUCE_USERNAME,
  sauceKey: process.env.SAUCE_ACCESS_KEY,

  browsers: [
    {browserName: 'chrome'},
    {browserName: 'firefox'},
    {browserName: 'safari', version: 7, platform: 'OS X 10.9'},
    {browserName: 'safari', version: 6, platform: 'OS X 10.8'},
    {browserName: 'internet explorer', version: 11, platform: 'Windows 8.1'},
    {browserName: 'internet explorer', version: 10, platform: 'Windows 8'},
    {browserName: 'internet explorer', version: 9, platform: 'Windows 7'},
    {browserName: 'Android', version:'4.4', platform: 'Linux'},
    {browserName: 'Android', version:'4.1', platform: 'Linux'},
    {browserName: 'Android', version:'4.0', platform: 'Linux'},
    {browserName: 'iphone', deviceName: 'iPhone Simulator', version:'8.1', platform: 'OS X 10.9'},
    {browserName: 'iphone', deviceName: 'iPhone Simulator', version:'8.0', platform: 'OS X 10.9'},
    {browserName: 'iphone', deviceName: 'iPhone Simulator', version:'7.1', platform: 'OS X 10.9'},
    {browserName: 'iphone', deviceName: 'iPhone Simulator', version:'6.1', platform: 'OS X 10.8'},
    {browserName: 'ipad', version:'8.1', platform: 'OS X 10.9'},
    {browserName: 'ipad', version:'8.0', platform: 'OS X 10.9'},
    {browserName: 'ipad', version:'7.1', platform: 'OS X 10.9'},
    {browserName: 'ipad', version:'6.1', platform: 'OS X 10.8'}
  ],

  specs: [process.cwd() + '/spec.js'],

  baseUrl: 'http://localhost:3000/',

  // This enable testing on none angular pages without test code change.
  onPrepare: function () {
    element = browser.element;
    browser.ignoreSynchronization = true;
  },

  jasmineNodeOpts: {
    showColors: true,
    defaultTimeoutInterval: 30000
  }
};

config.multiCapabilities = config.browsers.map(function (cfg) {
  cfg.build = config.job_basename;
  cfg['tunnel-identifier'] = process.env.TRAVIS_JOB_NUMBER;
  cfg.name = 'FluxEx browser test for example: ' + config.job_basename;
  cfg.public = 'public';
  cfg.tags = [process.env.TRAVIS_JOB_ID, process.env.TRAVIS_COMMIT, 'fluxex'];
  return cfg;
});

module.exports.config = config;<|endoftext|>


<commit_before>var api = require('express').Router()
  , config = require('../lib/config')
  , db = require('ezseed-database').db.user
  , jwt = require('express-jwt')
  , prettyBytes = require('pretty-bytes')
  , debug = require('debug')('ezseed:api')
  , logger = require('ezseed-logger')('api')

jwt.sign = require('jsonwebtoken').sign

api

.use(
  jwt({ secret: config.secret }).unless({ path: ['/api/login'] })
)

.use(function(err, req, res, next) {
  if(err.name === 'UnauthorizedError') {
    logger.error(err)
    return res.status(401).end()
  }

  next()
})

.post('/login', function(req, res) {

  db.login(req.body, function(err, user) {
    if(err) {
      return res.send({error: err}).status(401).end()
    } else {
      user.prettySize = prettyBytes(user.spaceLeft)
      user.token = jwt.sign(user, config.secret)
      return res.json(user)
    }
  })

})

/** Protected methods **/

.get('/-', function(req, res) {

  //get user paths
  db.paths(req.user.id, function(err, user) {
    user.prettySize = prettyBytes(user.spaceLeft)
    return res.json(user)
  })

})

.get('/-/files', function(req, res) {
  var types = ['others', 'movies', 'albums']

  if(req.query.type && types.indexOf(req.query.type) === -1)
    return res.status(500).send({error: req.params.type+' is not a valid type'}).end()

  if(req.query.match)
    req.query.match = JSON.parse(req.query.match)

  debug("Files", req.query)

  //need params, limit, dateUpdate, paths, type !
  db.files(req.user.id, req.query, function(err, paths) {

    if(req.query.type) {
      var files = {}
      files[req.query.type] = []
    } else {
      var files = {movies: [], albums: [], others: []}
    }

    paths.paths.forEach(function(path) {
      for(var i in files) {
        for(var j in path[i]) {
          files[i].push(path[i][j])
        }
      }
    })

    return res.json(files)
  })
})

.get('/-/refresh', function(req, res) {
  db.paths(req.user.id, function(err, user) {
    process.watcher.client.call('refresh', user.paths, function(err, update) {
      if(err) {
        res.status(500).send({error: err})
      } else {
        res.json(update)
      }
    })
  })
})

// Mainly used for debugging
.get('/-/reset', function(req,res) {
  db.reset(req.user.id, function(err, paths) {
    if(err) {
      logger.error(err)
    }
    process.watcher.client.call('refresh', paths.paths, function(err, update) {
      if(err) {
        res.status(500).send({error: err})
      } else {
        res.json(update)
      }
    })
  })
})

.get('/-/size', function(req, res) {

  var size = {
    movies: 0,
    albums: 0,
    others: 0
  }, total = 0

  var opts = req.params.default ? {default: req.user.default_path} : {}

  db.files(req.user.id, opts, function(err, files) {

    if(!files)
      return res.status(500).end()

    files.paths.forEach(function(p) {

      if(!req.params.path || req.params.paths.indexOf(p._id) !== -1 || req.params.paths.indexOf(p._id) !== -1) {
        for(var i in size) {
          p[i].forEach(function(files) {
            files[require('ezseed-database').helpers.filename(i)].forEach(function(file) {
              size[i] += file.size
            })
          })

          total += size[i]

        }
      }
    })

    size.total = total

    var pretty = {}

    //converting bytes to pretty version
    for(var i in size) {
      pretty[i] = {
        percent: size[i] / total * 100,
        bytes: size[i],
        pretty: prettyBytes(size[i])
      }
    }

    return res.json(pretty)
  })
})

module.exports = api
<commit_msg>fix(transmission): Add rpc-port to the user login token<commit_after>var api = require('express').Router()
  , config = require('../lib/config')
  , db = require('ezseed-database').db.user
  , p = require('path')
  , fs = require('fs')
  , jwt = require('express-jwt')
  , prettyBytes = require('pretty-bytes')
  , debug = require('debug')('ezseed:api')
  , logger = require('ezseed-logger')('api')

jwt.sign = require('jsonwebtoken').sign

api

.use(
  jwt({ secret: config.secret }).unless({ path: ['/api/login'] })
)

.use(function(err, req, res, next) {
  if(err.name === 'UnauthorizedError') {
    logger.error(err)
    return res.status(401).end()
  }

  next()
})

.post('/login', function(req, res) {

  db.login(req.body, function(err, user) {
    if(err) {
      return res.send({error: err}).status(401).end()
    } else {
      user.prettySize = prettyBytes(user.spaceLeft)
      user.token = jwt.sign(user, config.secret)

      if(user.client == 'transmission') {
        var transmission = JSON.parse(fs.readFileSync(p.join(config.home, user.username, '.settings.'+user.username+'.json')))
        user.port = transmission['rpc-port']
      }

      return res.json(user)
    }
  })

})

/** Protected methods **/

.get('/-', function(req, res) {

  //get user paths
  db.paths(req.user.id, function(err, user) {
    user.prettySize = prettyBytes(user.spaceLeft)
    return res.json(user)
  })

})

.get('/-/files', function(req, res) {
  var types = ['others', 'movies', 'albums']

  if(req.query.type && types.indexOf(req.query.type) === -1)
    return res.status(500).send({error: req.params.type+' is not a valid type'}).end()

  if(req.query.match)
    req.query.match = JSON.parse(req.query.match)

  debug("Files", req.query)

  //need params, limit, dateUpdate, paths, type !
  db.files(req.user.id, req.query, function(err, paths) {

    if(req.query.type) {
      var files = {}
      files[req.query.type] = []
    } else {
      var files = {movies: [], albums: [], others: []}
    }

    paths.paths.forEach(function(path) {
      for(var i in files) {
        for(var j in path[i]) {
          files[i].push(path[i][j])
        }
      }
    })

    return res.json(files)
  })
})

.get('/-/refresh', function(req, res) {
  db.paths(req.user.id, function(err, user) {
    process.watcher.client.call('refresh', user.paths, function(err, update) {
      if(err) {
        res.status(500).send({error: err})
      } else {
        res.json(update)
      }
    })
  })
})

// Mainly used for debugging
.get('/-/reset', function(req,res) {
  db.reset(req.user.id, function(err, paths) {
    if(err) {
      logger.error(err)
    }
    process.watcher.client.call('refresh', paths.paths, function(err, update) {
      if(err) {
        res.status(500).send({error: err})
      } else {
        res.json(update)
      }
    })
  })
})

.get('/-/size', function(req, res) {

  var size = {
    movies: 0,
    albums: 0,
    others: 0
  }, total = 0

  var opts = req.params.default ? {default: req.user.default_path} : {}

  db.files(req.user.id, opts, function(err, files) {

    if(!files)
      return res.status(500).end()

    files.paths.forEach(function(p) {

      if(!req.params.path || req.params.paths.indexOf(p._id) !== -1 || req.params.paths.indexOf(p._id) !== -1) {
        for(var i in size) {
          p[i].forEach(function(files) {
            files[require('ezseed-database').helpers.filename(i)].forEach(function(file) {
              size[i] += file.size
            })
          })

          total += size[i]

        }
      }
    })

    size.total = total

    var pretty = {}

    //converting bytes to pretty version
    for(var i in size) {
      pretty[i] = {
        percent: size[i] / total * 100,
        bytes: size[i],
        pretty: prettyBytes(size[i])
      }
    }

    return res.json(pretty)
  })
})

module.exports = api
<|endoftext|>


<commit_before>var mbc            = require('mbc-common'),
    search_options = mbc.config.Search,
    collections    = mbc.config.Common.Collections,
    backboneio     = require('backbone.io'),
    middleware     = new mbc.iobackends().get_middleware()
;

module.exports = function (db) {
    var backends = {
        app: {
            use: [backboneio.middleware.configStore()]
        },
        transform: {
            use: [middleware.uuid],
            mongo: {
                db: db,
                collection: collections.Transforms,
                opts: { search: search_options.Transforms },
            }},
        media: {
            redis: true,
            mongo: {
                db: db,
                collection: collections.Medias,
                opts: { search: search_options.Medias },
            }},
        piece: {
            use: [middleware.uuid],
            mongo: {
                db: db,
                collection: collections.Pieces,
                opts: { search: search_options.Pieces },
            }},
        list: {
            use: [middleware.uuid, middleware.tmpId],
            mongo: {
                db: db,
                collection: collections.Lists,
                opts: { search: search_options.Lists },
            }},
        sched: {
            use: [middleware.uuid, middleware.publishJSON],
            mongo: {
                db: db,
                collection: collections.Scheds,
                opts: { search: search_options.Scheds },
            }},
        status: {
            use: [middleware.uuid],
            mongo: {
                db: db,
                collection: collections.Status,
                opts: { search: search_options.Status },
            }},
        frame: {
            use: [backboneio.middleware.memoryStore(db, 'progress', {})],
        },
        mostomessages: {
            mongo: {
                db: db,
                collection: collections.Mostomessages,
                opts: { search: search_options.Mostomessages },
            }},
        sketch: {
            use: [middleware.uuid],
            mongo: {
                db: db,
                collection: collections.Sketchs,
                opts: { search: search_options.Sketchs },
            }},
        user: {
            use: [middleware.uuid],
            mongo: {
                db: db,
                collection: collections.Auth,
            }},
        transcodestatus: {
            use: [middleware.debug],
            redis: true,
            store: backboneio.middleware.memoryStore(db, 'transcode_status', {}),
        },
        transcode: {
            use: [middleware.debug],
            redis: true,
            mongo: {
                db: db,
                collection: collections.Transcoding,
            }},
    };

    return backends;
};

<commit_msg>update backends of mostomessages, status and frame<commit_after>var mbc            = require('mbc-common'),
    search_options = mbc.config.Search,
    collections    = mbc.config.Common.Collections,
    backboneio     = require('backbone.io'),
    middleware     = new mbc.iobackends().get_middleware()
;

module.exports = function (db) {
    var backends = {
        app: {
            use: [backboneio.middleware.configStore()]
        },
        transform: {
            use: [middleware.uuid],
            mongo: {
                db: db,
                collection: collections.Transforms,
                opts: { search: search_options.Transforms },
            }},
        media: {
            redis: true,
            mongo: {
                db: db,
                collection: collections.Medias,
                opts: { search: search_options.Medias },
            }},
        piece: {
            use: [middleware.uuid],
            mongo: {
                db: db,
                collection: collections.Pieces,
                opts: { search: search_options.Pieces },
            }},
        list: {
            use: [middleware.uuid, middleware.tmpId],
            mongo: {
                db: db,
                collection: collections.Lists,
                opts: { search: search_options.Lists },
            }},
        sched: {
            use: [middleware.uuid, middleware.publishJSON],
            mongo: {
                db: db,
                collection: collections.Scheds,
                opts: { search: search_options.Scheds },
            }},
        status: {
            use: [middleware.uuid],
            redis: true,
            mongo: {
                db: db,
                collection: collections.Status,
                opts: { search: search_options.Status },
            }},
        frame: {
            redis: true,
            store: backboneio.middleware.memoryStore(db, 'progress', {}),
        },
        mostomessages: {
            redis: true,
            mongo: {
                db: db,
                collection: collections.Mostomessages,
                opts: { search: search_options.Mostomessages },
            }},
        sketch: {
            use: [middleware.uuid],
            mongo: {
                db: db,
                collection: collections.Sketchs,
                opts: { search: search_options.Sketchs },
            }},
        user: {
            use: [middleware.uuid],
            mongo: {
                db: db,
                collection: collections.Auth,
            }},
        transcodestatus: {
            use: [middleware.debug],
            redis: true,
            store: backboneio.middleware.memoryStore(db, 'transcode_status', {}),
        },
        transcode: {
            use: [middleware.debug],
            redis: true,
            mongo: {
                db: db,
                collection: collections.Transcoding,
            }},
    };

    return backends;
};

<|endoftext|>


<commit_before>/* global document, localStorage */
'use strict';
var globals = require('../settings/globals');

module.exports = function (casperjs) {
    return {
        /**
         * Logs in with the given parameters
         *
         * @public
         * @param {Object} [credentials=globals.credentials]
         * @param {String} credentials.username
         * @param {String} credentials.password
         */
        login: function (credentials) {
            return function () {
                return this.thenOpen(globals.adminUrl).then(function () {
                    this.fill('#login-form', credentials ||  globals.credentials, true);
                }).waitForResource(/login/).thenEvaluate(function () {
                    localStorage.clear();
                });
            };
        },

        logout: function () {
            return function () {
                return this.thenOpen(globals.adminLogoutUrl)
                    .waitForSelector('#content');
            };
        },

        /**
         * removes a page by name, or the first encountered one
         *
         * @public
         * @param {Object} opts
         * @param {String} [opts.title] Name of the page to delete
         */
        removePage: function (opts) {
            return function () {
                return this.thenOpen(globals.adminPagesUrl)
                    .waitUntilVisible('.tree .deletelink')
                    .then(function () {
                        var pageId;
                        if (opts && opts.title) {
                            // important to pass single param, because casper acts
                            // weirdly with single key objects https://github.com/n1k0/casperjs/issues/353
                            pageId = this.evaluate(function (title) {
                                return CMS.$('.col1 a > span').map(function () {
                                    var span = $(this);
                                    if (span.text().trim() === title) {
                                        return span.closest('li').attr('id').split('_')[1];
                                    }
                                }).get()[0];
                            }, opts.title);
                        }
                        if (pageId) {
                            this.click('.tree .deletelink[href*="' + pageId + '"]');
                        } else {
                            this.click('.tree .deletelink'); // first one
                        }
                    })
                    .waitUntilVisible('input[type=submit]')
                    .then(function () {
                        this.click('input[type=submit]');
                    });
            };
        },

        /**
        * Adds the page
        *
        * @public
        * @param {Object} opts
        * @param {String} opts.title name of the page
        */
        addPage: function (opts) {
            return function () {
                return this.thenOpen(globals.adminPagesUrl + 'add/')
                    .waitUntilVisible('#id_title')
                    .then(function () {
                        this.sendKeys('#id_title', opts.title);
                        this.click('input[name="_save"]');
                    });
            };
        },

        /**
         * Adds the plugin (currently to the first placeholder)
         *
         * @param {Object} opts
         * @param {String} opts.type type of the plugin to add
         * @param {Object} opts.content object containing fields and values
         * @param {Object} [opts.parent] selector of the parent cms-draggable
         * @example
         *
         *     cms.addPlugin({
         *         type: 'TextPlugin',
         *         content: {
         *             id_body: 'Some text'
         *         }
         *     });
         */
        addPlugin: function (opts) {
            var xPath = casperjs.selectXPath;

            return function () {
                return this.thenOpen(globals.editUrl)
                    .waitUntilVisible('.cms-toolbar-expanded', function () {
                        this.click('.cms-toolbar-item-cms-mode-switcher .cms-btn[href="?build"]');
                    })
                    // only add to placeholder if no parent specified
                    .thenBypassIf(opts.parent, 1)
                    .waitUntilVisible('.cms-structure', function () {
                        this.click('.cms-structure .cms-submenu-add [data-tooltip="Add plugin"]');
                    })
                    // if parent specified - try to add to it
                    .thenBypassUnless(opts.parent, 1)
                    .then(function () {
                        // if the parent is expanded - click on "Add plugin"
                        if (this.visible(opts.parent)) {
                            this.click(opts.parent + ' [data-tooltip="Add plugin"]');
                        } else {
                            // get full selector (css3, not jquery) of the closest placeholder
                            var parentSelector = this.evaluate(function (selector) {
                                return $(selector).closest('.cms-dragarea').parentsUntil('body')
                                    .andSelf()
                                    .map(function () {
                                        return this.nodeName + ':nth-child(' + ($(this).index() + 1) + ')';
                                    }).get().join('>');
                            }, opts.parent);

                            // check if "Expand all" is visible
                            if (this.visible(parentSelector + ' .cms-dragbar-expand-all')) {
                                this.click(parentSelector + ' .cms-dragbar-expand-all');
                            } else {
                                // if not visible, then first "Collapse all"
                                this.click(parentSelector + ' .cms-dragbar-collapse-all');
                                this.wait(100);
                                this.click(parentSelector + ' .cms-dragbar-expand-all');
                            }

                            this.wait(100);
                            this.click(opts.parent + ' [data-tooltip="Add plugin"]');
                        }
                    })
                    .waitUntilVisible('.cms-plugin-picker .cms-submenu-item [data-rel="add"]', function () {
                        this.then(function () {
                            this.click(xPath('//a[@href="' + opts.type + '"]'));
                        });
                        // ensure previous content has been changed
                        return this.waitWhileVisible('.cms-plugin-picker .cms-submenu-item [data-rel="add"]');
                    })
                    .withFrame(0, function () {
                        // we cannot pass the options as object because casper js
                        // treats objects/arrays in a funny way, so we stringify it
                        this.waitUntilVisible('#content', function () {
                            this.evaluate(function (content) {
                                if (!content.length) {
                                    return;
                                }

                                content = JSON.parse(content);

                                Object.keys(content).forEach(function (key) {
                                    document.querySelector('#' + key).value = content[key];
                                });
                            }, JSON.stringify(opts.content));
                        });
                    })
                    .then(function () {
                        // djangocms-text-ckeditor is special
                        if (opts.type === 'TextPlugin') {
                            this.withFrame(0, function () {
                                casper.waitUntilVisible('.cke_inner', function () {
                                    // explicitly put text to ckeditor
                                    this.evaluate(function (contentData) {
                                        CMS.CKEditor.editor.setData(contentData);
                                    }, opts.content.id_body);
                                });
                            });
                        }
                    }).then(function () {
                        this.click('.cms-modal-buttons .cms-btn-action.default');
                    });
            };
        },

        clearClipboard: function () {
            return function () {
                return this.thenOpen(globals.editUrl)
                    .then(function () {
                        this.click('.cms-clipboard-empty a');
                    })
                    .waitForResource(/clear/);
            };
        }
    };
};
<commit_msg>actually wait until page creation is complete<commit_after>/* global document, localStorage */
'use strict';
var globals = require('../settings/globals');

module.exports = function (casperjs) {
    return {
        /**
         * Logs in with the given parameters
         *
         * @public
         * @param {Object} [credentials=globals.credentials]
         * @param {String} credentials.username
         * @param {String} credentials.password
         */
        login: function (credentials) {
            return function () {
                return this.thenOpen(globals.adminUrl).then(function () {
                    this.fill('#login-form', credentials ||  globals.credentials, true);
                }).waitForResource(/login/).thenEvaluate(function () {
                    localStorage.clear();
                });
            };
        },

        logout: function () {
            return function () {
                return this.thenOpen(globals.adminLogoutUrl)
                    .waitForSelector('#content');
            };
        },

        /**
         * removes a page by name, or the first encountered one
         *
         * @public
         * @param {Object} opts
         * @param {String} [opts.title] Name of the page to delete
         */
        removePage: function (opts) {
            return function () {
                return this.thenOpen(globals.adminPagesUrl)
                    .waitUntilVisible('.tree .deletelink')
                    .then(function () {
                        var pageId;
                        if (opts && opts.title) {
                            // important to pass single param, because casper acts
                            // weirdly with single key objects https://github.com/n1k0/casperjs/issues/353
                            pageId = this.evaluate(function (title) {
                                return CMS.$('.col1 a > span').map(function () {
                                    var span = $(this);
                                    if (span.text().trim() === title) {
                                        return span.closest('li').attr('id').split('_')[1];
                                    }
                                }).get()[0];
                            }, opts.title);
                        }
                        if (pageId) {
                            this.click('.tree .deletelink[href*="' + pageId + '"]');
                        } else {
                            this.click('.tree .deletelink'); // first one
                        }
                    })
                    .waitUntilVisible('input[type=submit]')
                    .then(function () {
                        this.click('input[type=submit]');
                    });
            };
        },

        /**
        * Adds the page
        *
        * @public
        * @param {Object} opts
        * @param {String} opts.title name of the page
        */
        addPage: function (opts) {
            return function () {
                return this.thenOpen(globals.adminPagesUrl + 'add/')
                    .waitUntilVisible('#id_title')
                    .then(function () {
                        this.sendKeys('#id_title', opts.title);
                        this.click('input[name="_save"]');
                    })
                    .waitUntilVisible('.success');
            };
        },

        /**
         * Adds the plugin (currently to the first placeholder)
         *
         * @param {Object} opts
         * @param {String} opts.type type of the plugin to add
         * @param {Object} opts.content object containing fields and values
         * @param {Object} [opts.parent] selector of the parent cms-draggable
         * @example
         *
         *     cms.addPlugin({
         *         type: 'TextPlugin',
         *         content: {
         *             id_body: 'Some text'
         *         }
         *     });
         */
        addPlugin: function (opts) {
            var xPath = casperjs.selectXPath;

            return function () {
                return this.thenOpen(globals.editUrl)
                    .waitUntilVisible('.cms-toolbar-expanded', function () {
                        this.click('.cms-toolbar-item-cms-mode-switcher .cms-btn[href="?build"]');
                    })
                    // only add to placeholder if no parent specified
                    .thenBypassIf(opts.parent, 1)
                    .waitUntilVisible('.cms-structure', function () {
                        this.click('.cms-structure .cms-submenu-add [data-tooltip="Add plugin"]');
                    })
                    // if parent specified - try to add to it
                    .thenBypassUnless(opts.parent, 1)
                    .then(function () {
                        // if the parent is expanded - click on "Add plugin"
                        if (this.visible(opts.parent)) {
                            this.click(opts.parent + ' [data-tooltip="Add plugin"]');
                        } else {
                            // get full selector (css3, not jquery) of the closest placeholder
                            var parentSelector = this.evaluate(function (selector) {
                                return $(selector).closest('.cms-dragarea').parentsUntil('body')
                                    .andSelf()
                                    .map(function () {
                                        return this.nodeName + ':nth-child(' + ($(this).index() + 1) + ')';
                                    }).get().join('>');
                            }, opts.parent);

                            // check if "Expand all" is visible
                            if (this.visible(parentSelector + ' .cms-dragbar-expand-all')) {
                                this.click(parentSelector + ' .cms-dragbar-expand-all');
                            } else {
                                // if not visible, then first "Collapse all"
                                this.click(parentSelector + ' .cms-dragbar-collapse-all');
                                this.wait(100);
                                this.click(parentSelector + ' .cms-dragbar-expand-all');
                            }

                            this.wait(100);
                            this.click(opts.parent + ' [data-tooltip="Add plugin"]');
                        }
                    })
                    .waitUntilVisible('.cms-plugin-picker .cms-submenu-item [data-rel="add"]', function () {
                        this.then(function () {
                            this.click(xPath('//a[@href="' + opts.type + '"]'));
                        });
                        // ensure previous content has been changed
                        return this.waitWhileVisible('.cms-plugin-picker .cms-submenu-item [data-rel="add"]');
                    })
                    .withFrame(0, function () {
                        // we cannot pass the options as object because casper js
                        // treats objects/arrays in a funny way, so we stringify it
                        this.waitUntilVisible('#content', function () {
                            this.evaluate(function (content) {
                                if (!content.length) {
                                    return;
                                }

                                content = JSON.parse(content);

                                Object.keys(content).forEach(function (key) {
                                    document.querySelector('#' + key).value = content[key];
                                });
                            }, JSON.stringify(opts.content));
                        });
                    })
                    .then(function () {
                        // djangocms-text-ckeditor is special
                        if (opts.type === 'TextPlugin') {
                            this.withFrame(0, function () {
                                casper.waitUntilVisible('.cke_inner', function () {
                                    // explicitly put text to ckeditor
                                    this.evaluate(function (contentData) {
                                        CMS.CKEditor.editor.setData(contentData);
                                    }, opts.content.id_body);
                                });
                            });
                        }
                    }).then(function () {
                        this.click('.cms-modal-buttons .cms-btn-action.default');
                    });
            };
        },

        clearClipboard: function () {
            return function () {
                return this.thenOpen(globals.editUrl)
                    .then(function () {
                        this.click('.cms-clipboard-empty a');
                    })
                    .waitForResource(/clear/);
            };
        }
    };
};
<|endoftext|>


<commit_before>module.exports = function convert (keys, values) {
  var reverse = { }
  var full = { }
  var i

  for (i = keys.length; i--;) {
    full[keys[i].toUpperCase()] = values[i].toUpperCase()
    full[keys[i]] = values[i]
  }

  for (i in full) {
    reverse[full[i]] = i
  }

  return {
    fromEn: function (str) {
      return str.replace(/./g, function (ch) {
        return full[ch] || ch
      })
    },
    toEn: function (str) {
      return str.replace(/./g, function (ch) {
        return reverse[ch] || ch
      })
    }
  }
}
<commit_msg>Clean up convert.js code style<commit_after>module.exports = function convert(keys, values) {
  var reverse = {}
  var full = {}
  var i

  for (i = keys.length; i--; ) {
    full[keys[i].toUpperCase()] = values[i].toUpperCase()
    full[keys[i]] = values[i]
  }

  for (i in full) {
    reverse[full[i]] = i
  }

  return {
    fromEn: function (str) {
      return str.replace(/./g, function (ch) {
        return full[ch] || ch
      })
    },
    toEn: function (str) {
      return str.replace(/./g, function (ch) {
        return reverse[ch] || ch
      })
    }
  }
}
<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');
var sax = require("sax");
var AdmZip = require('adm-zip');
var _ = require('lodash');

var argv = require('minimist')(process.argv.slice(2));

if (!argv['d'] && !argv['f']) {
    console.log("no file or dir, use -d or -f argument");
    process.exit(1);
}

var outDir = argv['o'] || 'target';
var imgDir = 'ressources/images';

var inDir = argv["d"];
var inFile = argv["f"];

if (inDir && (!fs.existsSync(inDir) || !fs.statSync(inDir).isDirectory())) {
    console.log("directory not found", inDir);
    process.exit(1);
}
if (!inDir && !fs.existsSync(inFile)) {
    console.log("file not found", inFile);
    process.exit(1);
}

function MDWriter() {
    this.text = "";
    this.inCodeBlock = false;
    this.inNotes = false;
    this.listLevel = 0;
    this.titreAdded = false;
    this.mainTitleAdded = false;
    this.firstPage = false;

    this.addPage = function () {
        this.text += "\n\n\n\n";
        this.titreAdded = false;
        this.firstPage = true;
    };

    this.addParagraph = function () {
        if (this.inCodeBlock || this.inNotes) {
            this.text += "\n";
        }
    };

    this.addText = function (text) {
        if(this.firstPage && !this.mainTitleAdded) {
            this.text = '#' + text + '\n\n<!-- .slide: class="page-title" -->\n\n\n\n';
            this.mainTitleAdded = true;
        }
        else if (!this.titreAdded) {
            this.text += "## " + text + "\n";
            this.titreAdded = true;
        }
        else {
            this.text += text;
        }
    };

    this.addImage = function (path) {
        this.text += "\n\n![](ressources/images/" + path.replace("Pictures/", "") + ")\n";
    };

    this.startList = function () {
        this.listLevel++;
        if (this.listLevel == 1 && this.text[this.text.length-1] !== '\n') {
            this.text += '\n';
        }
    };

    this.endList = function () {
        this.listLevel--;
    };

    this.addBullet = function () {
        if (!this.listLevel) {
            console.warning("not in a list");
        }
        this.text += "\n" + new Array(this.listLevel).join("\t") + "- ";
    };

    this.addEnd = function () {
        this.text += "\n\n\n\n<!-- .slide: class=\"page-questions\" -->\n\n\n\n<!-- .slide: class=\"page-tp1\" -->";
    };

    this.startCodeBlock = function () {
        this.text += "\n```";
        this.inCodeBlock = true;
    };

    this.endCodeBlock = function () {
        this.text += "\n```\n";
        this.inCodeBlock = false;
    };

    this.startNotes = function () {
        this.text += "\nNotes :\n";
        this.inNotes = true;
    };

    this.endNotes = function () {
        this.inNotes = false;
    };

    this.startTable = function() {
        this.inTable = true;
        this.rowNumber = 0;
        this.colNumber = 0;
    }

    this.endTable = function() {
        this.inTable = false;
    }

    this.startRow = function() {
        this.text += "\n|";
        if (this.rowNumber == 1) {
            //If header was written we write separation line
            this.text += Array(this.colNumber + 1).join("---|") + "\n|";
        }
        this.rowNumber++;
    }

    this.closeCell = function() {
        if (this.rowNumber == 1) {
            this.colNumber++;
        }
        this.text += "|";
    }

}

function OdpConverter(filepath, outDir) {

    this.filePath = filepath;
    this.name = path.basename(this.filePath, '.odp').replace(/\s+/g, '_');
    this.contentName = path.join(outDir, this.name + '-content.xml');

    this.extract = function () {
        console.log('extract : ' + this.filePath);
        var zip = new AdmZip(this.filePath);
        var zipEntries = zip.getEntries();
        zipEntries.forEach(function (val) {
            if (val.entryName.indexOf("content.xml") >= 0) {
                //console.log(val.entryName);
                zip.extractEntryTo(val, outDir, true, true);
                fs.renameSync(path.join(outDir, val.entryName), this.contentName);
            }
            if (val.entryName.indexOf("Pictures") >= 0) {
                //console.log(val.entryName);
                zip.extractEntryTo(val, path.join(outDir, imgDir), false, true);
                var baseName = path.basename(val.entryName);
                fs.renameSync(path.join(outDir, imgDir, baseName), path.join(outDir, imgDir, this.name + '-' + baseName));
            }
        }.bind(this));
    };

    this.convert = function () {
        console.log('convert : ' + this.name);
        var mdWriter = new MDWriter();
        var outName = this.name;
        var saxStream = sax.createStream(true, {
            trim: true,
            normalize: true,
            xmlns: false,
            lowercase: true
        });

        saxStream.on("opentag", function (node) {

            if (node.name === "draw:page") {
                mdWriter.addPage();
            } else if (node.name === "draw:image") {
                mdWriter.addImage(outName + '-' + node.attributes["xlink:href"]);
            } else if (node.name === "text:list") {
                mdWriter.startList();
            } else if (node.name === "text:list-item") {
                mdWriter.addBullet();
            } else if (node.name === "draw:custom-shape" || node.name === "draw:rect") {
                mdWriter.startCodeBlock();
            } else if (node.name === "text:p") {
                mdWriter.addParagraph();
            } else if (node.name === "presentation:notes") {
                mdWriter.startNotes();
            } else if (node.name === "table:table") {
                mdWriter.startTable();
            } else if (node.name === "table:table-row") {
                mdWriter.startRow();
            }
        });

        saxStream.on("closetag", function (node) {

            if (node === "draw:custom-shape" || node === "draw:rect") {
                mdWriter.endCodeBlock();
            } else if (node === "text:list") {
                mdWriter.endList();
            } else if (node === "presentation:notes") {
                mdWriter.endNotes();
            } else if (node === "table:table") {
                mdWriter.endTable();
            } else if (node === "table:table-cell") {
                mdWriter.closeCell();
            }
        });

        saxStream.on("end", function () {
            mdWriter.addEnd();
            console.log('write to ' + path.join(outDir, outName + '.md'));
            fs.writeFile(path.join(outDir, outName + '.md'), mdWriter.text);
        });

        saxStream.on("text", function (t) {
            mdWriter.addText(t);
        });

        fs.createReadStream(this.contentName).pipe(saxStream);
    }
}

if(inDir) {
    var outSlidesJson = [];
    _.forEach(
        _.filter(
            fs.readdirSync(inDir),
            function (file) {
                return path.extname(file) === '.odp';
            }
        ),
        function (odp) {
            var conv = new OdpConverter(path.join(inDir, odp), outDir);
            outSlidesJson.push(conv.name + '.md');
            conv.extract();
            conv.convert();
        }
    );
    fs.writeFile(path.join(outDir, 'slides.json'), JSON.stringify(outSlidesJson, null, 2));
} else {
    var conv = new OdpConverter(inFile, outDir);
    conv.extract();
    conv.convert();
}
<commit_msg>Fix #20 éviter 3 \n dans les codeBlock<commit_after>var fs = require('fs');
var path = require('path');
var sax = require("sax");
var AdmZip = require('adm-zip');
var _ = require('lodash');

var argv = require('minimist')(process.argv.slice(2));

if (!argv['d'] && !argv['f']) {
    console.log("no file or dir, use -d or -f argument");
    process.exit(1);
}

var outDir = argv['o'] || 'target';
var imgDir = 'ressources/images';

var inDir = argv["d"];
var inFile = argv["f"];

if (inDir && (!fs.existsSync(inDir) || !fs.statSync(inDir).isDirectory())) {
    console.log("directory not found", inDir);
    process.exit(1);
}
if (!inDir && !fs.existsSync(inFile)) {
    console.log("file not found", inFile);
    process.exit(1);
}

function MDWriter() {
    this.text = "";
    this.inCodeBlock = false;
    this.inNotes = false;
    this.listLevel = 0;
    this.titreAdded = false;
    this.mainTitleAdded = false;
    this.firstPage = false;

    this.addPage = function () {
        this.text += "\n\n\n\n";
        this.titreAdded = false;
        this.firstPage = true;
    };

    this.addParagraph = function () {
        if (this.inCodeBlock || this.inNotes) {
            this.text += "\n";
        }
    };

    this.addText = function (text) {
        if(this.firstPage && !this.mainTitleAdded) {
            this.text = '#' + text + '\n\n<!-- .slide: class="page-title" -->\n\n\n\n';
            this.mainTitleAdded = true;
        }
        else if (!this.titreAdded) {
            this.text += "## " + text + "\n";
            this.titreAdded = true;
        }
        else {
            this.text += text;
        }
    };

    this.addImage = function (path) {
        this.text += "\n\n![](ressources/images/" + path.replace("Pictures/", "") + ")\n";
    };

    this.startList = function () {
        this.listLevel++;
        if (this.listLevel == 1 && this.text[this.text.length-1] !== '\n') {
            this.text += '\n';
        }
    };

    this.endList = function () {
        this.listLevel--;
    };

    this.addBullet = function () {
        if (!this.listLevel) {
            console.warning("not in a list");
        }
        this.text += "\n" + new Array(this.listLevel).join("\t") + "- ";
    };

    this.addEnd = function () {
        this.text += "\n\n\n\n<!-- .slide: class=\"page-questions\" -->\n\n\n\n<!-- .slide: class=\"page-tp1\" -->";
    };

    this.startCodeBlock = function () {
        this.tmp = this.text;
        this.text = "";
        if (this.tmp[this.tmp.length-1] !== '\n') {
            this.text += "\n";
        }
        this.text += "\n```";
        this.inCodeBlock = true;
    };

    this.endCodeBlock = function () {
        this.text += "\n```\n";
        this.inCodeBlock = false;
        var codeBlock = this.text;
        this.text = this.tmp;
        this.text += codeBlock.replace(/\n{3}/g,"\n\n");
    };

    this.startNotes = function () {
        this.text += "\nNotes :\n";
        this.inNotes = true;
    };

    this.endNotes = function () {
        this.inNotes = false;
    };

    this.startTable = function() {
        this.inTable = true;
        this.rowNumber = 0;
        this.colNumber = 0;
    }

    this.endTable = function() {
        this.inTable = false;
    }

    this.startRow = function() {
        this.text += "\n|";
        if (this.rowNumber == 1) {
            //If header was written we write separation line
            this.text += Array(this.colNumber + 1).join("---|") + "\n|";
        }
        this.rowNumber++;
    }

    this.closeCell = function() {
        if (this.rowNumber == 1) {
            this.colNumber++;
        }
        this.text += "|";
    }

}

function OdpConverter(filepath, outDir) {

    this.filePath = filepath;
    this.name = path.basename(this.filePath, '.odp').replace(/\s+/g, '_');
    this.contentName = path.join(outDir, this.name + '-content.xml');

    this.extract = function () {
        console.log('extract : ' + this.filePath);
        var zip = new AdmZip(this.filePath);
        var zipEntries = zip.getEntries();
        zipEntries.forEach(function (val) {
            if (val.entryName.indexOf("content.xml") >= 0) {
                //console.log(val.entryName);
                zip.extractEntryTo(val, outDir, true, true);
                fs.renameSync(path.join(outDir, val.entryName), this.contentName);
            }
            if (val.entryName.indexOf("Pictures") >= 0) {
                //console.log(val.entryName);
                zip.extractEntryTo(val, path.join(outDir, imgDir), false, true);
                var baseName = path.basename(val.entryName);
                fs.renameSync(path.join(outDir, imgDir, baseName), path.join(outDir, imgDir, this.name + '-' + baseName));
            }
        }.bind(this));
    };

    this.convert = function () {
        console.log('convert : ' + this.name);
        var mdWriter = new MDWriter();
        var outName = this.name;
        var saxStream = sax.createStream(true, {
            trim: true,
            normalize: true,
            xmlns: false,
            lowercase: true
        });

        saxStream.on("opentag", function (node) {

            if (node.name === "draw:page") {
                mdWriter.addPage();
            } else if (node.name === "draw:image") {
                mdWriter.addImage(outName + '-' + node.attributes["xlink:href"]);
            } else if (node.name === "text:list") {
                mdWriter.startList();
            } else if (node.name === "text:list-item") {
                mdWriter.addBullet();
            } else if (node.name === "draw:custom-shape" || node.name === "draw:rect") {
                mdWriter.startCodeBlock();
            } else if (node.name === "text:p") {
                mdWriter.addParagraph();
            } else if (node.name === "presentation:notes") {
                mdWriter.startNotes();
            } else if (node.name === "table:table") {
                mdWriter.startTable();
            } else if (node.name === "table:table-row") {
                mdWriter.startRow();
            }
        });

        saxStream.on("closetag", function (node) {

            if (node === "draw:custom-shape" || node === "draw:rect") {
                mdWriter.endCodeBlock();
            } else if (node === "text:list") {
                mdWriter.endList();
            } else if (node === "presentation:notes") {
                mdWriter.endNotes();
            } else if (node === "table:table") {
                mdWriter.endTable();
            } else if (node === "table:table-cell") {
                mdWriter.closeCell();
            }
        });

        saxStream.on("end", function () {
            mdWriter.addEnd();
            console.log('write to ' + path.join(outDir, outName + '.md'));
            fs.writeFile(path.join(outDir, outName + '.md'), mdWriter.text);
        });

        saxStream.on("text", function (t) {
            mdWriter.addText(t);
        });

        fs.createReadStream(this.contentName).pipe(saxStream);
    }
}

if(inDir) {
    var outSlidesJson = [];
    _.forEach(
        _.filter(
            fs.readdirSync(inDir),
            function (file) {
                return path.extname(file) === '.odp';
            }
        ),
        function (odp) {
            var conv = new OdpConverter(path.join(inDir, odp), outDir);
            outSlidesJson.push(conv.name + '.md');
            conv.extract();
            conv.convert();
        }
    );
    fs.writeFile(path.join(outDir, 'slides.json'), JSON.stringify(outSlidesJson, null, 2));
} else {
    var conv = new OdpConverter(inFile, outDir);
    conv.extract();
    conv.convert();
}
<|endoftext|>


<commit_before>var fs = require('fs');
var dot = require('dot');

function fetchHighlightRules(aceSheet) {
  var lines = (''+aceSheet).split('\n');
  var beforeStart = true;  // Before the start of the function we fetch.
  var beforeEnd = true;
  var result = 'function() {\n';
  for (var i = 0; i < lines.length; i++) {
    var line = lines[i];
    if (beforeStart) {
      if (line.match(/HighlightRules = function/)) {
        beforeStart = false;
      }
    } else if (beforeEnd) {
      if (line.match(/^oop\.inherits/)) {
        beforeEnd = false;
      } else {
        // Indent it properly.
        result += '  ' + line + '\n';
      }
    } else { break; }
  }
  return result;
}

var modeConf = {};

// Read all arguments.
// eg, ./ace2cm asciidoc ./asciidoc_highlight_rules.js '{"textRules":false}'

// The mode name is first.
modeConf.modeName = process.argv[2] || 'mode';
modeConf.rules = fetchHighlightRules(fs.readFileSync(process.argv[3]));

// Defaults.
modeConf.textRules = true;
modeConf.mixedMode = false;

// The last argument is a JSON object.
function addOtherConf(modeConf, otherConf) {
  for (var key in otherConf) {
    modeConf[key] = otherConf[key];
  }
}
addOtherConf(modeConf, JSON.parse(process.argv[4]));

// Fetch the template.
var template = ''+fs.readFileSync('mode-template.js');
dot.templateSettings.strip = false;

process.stdout.write(dot.template(template)(modeConf));
<commit_msg>Replace references to the highlight rule's function name.<commit_after>var fs = require('fs');
var dot = require('dot');

function fetchHighlightRules(aceSheet) {
  var lines = (''+aceSheet).split('\n');
  var beforeStart = true;  // Before the start of the function we fetch.
  var beforeEnd = true;
  var functionName = '';  // Name of the real *HighlightRules function.
  var result = 'function() {\n';
  for (var i = 0; i < lines.length; i++) {
    var line = lines[i];
    if (beforeStart) {
      if (line.match(/HighlightRules = function/)) {
        functionName = line.match(/ ([a-zA-Z\$_]+HighlightRules) =/)[1];
        beforeStart = false;
      }
    } else if (beforeEnd) {
      if (line.match(/^oop\.inherits/)) {
        beforeEnd = false;
      } else {
        // Indent it properly.
        result += '  ' + line.replace(functionName, 'HighlightRules') + '\n';
      }
    } else { break; }
  }
  return result;
}

var modeConf = {};

// Read all arguments.
// eg, ./ace2cm asciidoc ./asciidoc_highlight_rules.js '{"textRules":false}'

// The mode name is first.
modeConf.modeName = process.argv[2] || 'mode';
modeConf.rules = fetchHighlightRules(fs.readFileSync(process.argv[3]));

// Defaults.
modeConf.textRules = true;
modeConf.mixedMode = false;

// The last argument is a JSON object.
function addOtherConf(modeConf, otherConf) {
  for (var key in otherConf) {
    modeConf[key] = otherConf[key];
  }
}
addOtherConf(modeConf, JSON.parse(process.argv[4]));

// Fetch the template.
var template = ''+fs.readFileSync('mode-template.js');
dot.templateSettings.strip = false;

process.stdout.write(dot.template(template)(modeConf));
<|endoftext|>


<commit_before>$(function ($) {
  // Constants
  var flameHost = 'noetica-flames.local:5000'
  var pollingInterval = 1000 // in milliseconds
  var prefixes = {
    hydraulics: 'hydraulics-attract',
    poofer: 'poofer-sequence'
  }

  // Test values - replace these with empty arrays in prod
  var hydraulicsAttractFiles = ['wicked poof', 'poofy smurf', 'superfly', 'black sunshine']
  var pooferSequenceFiles = ['poofy pooferson', 'poof daddy', 'sugar poof', 'poofy mcpoofface']

  // Initial values
  var selectedPoofer = null
  var inverted = false
  var selectedFilenames = {}
  var toggleStates = {
    'poofer-main': true,
    'hydraulics-main': true,
    'hydraulics-attract': true
  }
  var hydraulicsAttractModeActive = false
  var pooferSequenceModeActive = false
  var hydraulicsRecordActive = false
  var hydraulicsState = {x:0,y:0,z:0,pid_x:0,pid_y:0,pid_z:0}

  // jQuery helpers
  $.fn.reduce = function() {
    return [].reduce.apply(this.toArray(), arguments)
  }

  // Helper functions
  var flameUrl = function(endpoint) {
    if (endpoint === undefined) {
      endpoint = ''
    }
    return `http://${flameHost}/flame${endpoint}`
  }
  var hydraulicsUrl = function(endpoint) {
    if (endpoint === undefined) {
      endpoint = ''
    }
    return `http://${flameHost}/hydraulics${endpoint}`
  }
  var getOrSetFilename = function(prefix, filename) {
    if (filename !== undefined) {
      selectedFilenames[prefix] = filename
    }
    return selectedFilenames[prefix]
  }
  var selectedHydraulicsFilename = function(filename) {
    return getOrSetFilename(prefixes.hydraulics, filename)
  }
  var selectedPooferFilename = function(filename) {
    return getOrSetFilename(prefixes.poofer, filename)
  }

  // jQuery helper functions
  var findPoofer = function(name) {
    return $('.poofer-' + name)
  }

  // Display state update functions
  var updateToggleState = function(prefix) {
    var $element = $(`.${prefix}-toggle-button`)
    var state = toggleStates[prefix]
    $element.prop('checked', state)
  }
  var updateToggleStates = function() {
    Object.keys(toggleStates).map(updateToggleState)
  }
  var updatePooferToggleButtonState = function() {
    var enabled = !selectedPoofer || selectedPoofer.enabled
    $('.poofer-individual-toggle-button').prop('checked', enabled)
  }
  var updateSelectedPooferName = function() {
    var name = (selectedPoofer && selectedPoofer.name) || 'None'
    $('.poofer-selected-display-field').html(name)
  }
  var updateSelectedPooferDependentState = function() {
    updateSelectedPooferName()
    updatePooferToggleButtonState()
  }
  var updatePooferDisplayState = function(poofer) {
    var $poofer = findPoofer(poofer.name)
    if (poofer.enabled) {
      $poofer.addClass('poofer-enabled')
    } else {
      $poofer.removeClass('poofer-enabled')
    }
  }
  var updateAllPoofersDisplayState = function() {
    allPoofers.forEach(function (pooferName) {
      var poofer = allPoofersState[pooferName]
      updatePooferDisplayState(poofer)
    })
  }
  var updateHydraulicsDisplayState = function() {
    ['x','y','z','pid_x','pid_y','pid_z'].forEach(function(coord) {
      $(`.hydraulics-label-${coord}`).text(hydraulicsState[coord])
    })
  }
  var updateFilePicker = function(prefix, fileList) {
    var $files = $(`.${prefix}-file`)
    var $fileList = $(`.${prefix}-file-picker`)
    var addedFiles = []
    var removedFiles = []
    fileList.forEach(function(file) {
      var fileInList = $files.reduce(function(acc, cur) {
        var $cur = $(cur)
        return acc || $cur.data('name') === file
      }, false)
      if (!fileInList) {
        addedFiles.push(file)
      }
    })
    $files.each(function(idx, file) {
      var $file = $(file)
      var fileInList = fileList.reduce(function(acc, cur) {
        return acc || $file.data('name') === cur
      }, false)
      if (!fileInList) {
        removedFiles.push($file)
        if ($file.hasClass('selected')) {
          selectedFilenames[prefix] = null
        }
      }
    })
    removedFiles.forEach(function($file) {
      $file.remove()
    })
    addedFiles.forEach(function(file) {
      var $file = $(`<li class="${prefix}-file sequence-file" data-name="${file}">${file}</li>`)
      $fileList.append($file)
    })
  }
  var updateHydraulicsAttractFilePicker = function() {
    updateFilePicker(prefixes.hydraulics, hydraulicsAttractFiles)
  }
  var updatePooferSequenceFilePicker = function() {
    updateFilePicker(prefixes.poofer, pooferSequenceFiles)
  }
  var updateHydraulicsAttractPlayMode = function() {
    var oldMode = hydraulicsAttractModeActive
    var $button = $('.hydraulics-attract-play-stop-button')
    if (!selectedHydraulicsFilename()) {
      hydraulicsAttractModeActive = false
      $button.attr('disabled', true)
    } else if (!toggleStates['hydraulics-attract']) {
      $button.attr('disabled', true)
    } else {
      $button.attr('disabled', false)
    }
    if (hydraulicsAttractModeActive) {
      $button.removeClass('btn-play')
      $button.addClass('btn-stop')
    } else {
      $button.removeClass('btn-stop')
      $button.addClass('btn-play')
    }
    $button.attr('title', hydraulicsAttractModeActive ? 'Stop' : 'Start')
    if (hydraulicsAttractModeActive !== oldMode) {
      postHydraulicsAttractMode()
    }
  }
  var updatePooferSequencePlayMode = function() {
    var oldMode = pooferSequenceModeActive
    var $button = $('.poofer-sequence-play-stop-button')
    if (!selectedPooferFilename()) {
      pooferSequenceModeActive = false
      $button.attr('disabled', true)
    } else {
      $button.attr('disabled', false)
    }
    if (pooferSequenceModeActive) {
      $button.removeClass('btn-play')
      $button.addClass('btn-stop')
    } else {
      $button.removeClass('btn-stop')
      $button.addClass('btn-play')
    }
    $button.attr('title', pooferSequenceModeActive ? 'Stop' : 'Start')
    if (pooferSequenceModeActive !== oldMode) {
      postPooferSequenceMode()
    }
  }
  var updateHydraulicsRecordButtonState = function() {
    var $button = $('.hydraulics-record-toggle-button')
    if (hydraulicsRecordActive) {
      $button.removeClass('btn-record')
      $button.addClass('btn-stop')
    } else {
      $button.removeClass('btn-stop')
      $button.addClass('btn-record')
    }
    $button.attr('title', hydraulicsAttractModeActive ? 'Stop Recording' : 'Record')

  }
  var updateSelectedFileDependentState = function() {
    updateHydraulicsAttractPlayMode()
    updatePooferSequencePlayMode()
  }
  var updateColorInversion = function() {
    if (inverted) {
      $('html').addClass('inverted')
    }
    else {
      $('html').removeClass('inverted')
    }
  }
  var updateAllUIState = function() {
    // Since we don't have automatic data binding and it's a pain to call update functions
    // in just the right places, this function just updates everything and is called
    // when anything changes. Where possible, functions called from here should be
    // written to not force refreshes of state that didn't actually change.
    updateToggleStates()
    updateSelectedPooferDependentState()
    updateAllPoofersDisplayState()
    updateHydraulicsDisplayState()
    updateHydraulicsAttractFilePicker()
    updatePooferSequenceFilePicker()
    updateSelectedFileDependentState()
    updateHydraulicsRecordButtonState()
    updateColorInversion()
  }

  // Data state setting functions
  var setSelectedPoofer = function(name) {
    selectedPoofer = allPoofersState[name]
    updateSelectedPooferDependentState()
  }
  var updatePooferData = function (data) {
    data.poofers.forEach(function(poofer) {
      var pooferState = allPoofersState[poofer.id.toLowerCase()]
      pooferState.enabled = poofer.enabled
    })
    pooferSequenceFiles = data.patterns
  }
  var updateHydraulicsData = function (data) {
    hydraulicsState = data
    hydraulicsAttractFiles = data.playbacks
  }

  // Setup poofer data structures
  var allPoofers = ['nn','nw','nt','ne','wn','tn','en','ww','wt','tw','tt','te','et','ee','ws','ts','es','sw','st','se','ss','bn','bs','be','bw']
  var PooferState = function(name) {
    this.name = name
    this.enabled = true
    return this
  }
  var allPoofersState = {}
  for (var i = 0; i < allPoofers.length; i++) {
    var name = allPoofers[i]
    allPoofersState[name] = new PooferState(name)
    // Setup poofer event handlers
    var $currentPoofer = findPoofer(name)
    ;(function () {
      var tempName = name
      $currentPoofer.on('click', function () {
        setSelectedPoofer(tempName)
      })
    })()
  }

  // Setup button event handlers
  $('.invert-button').on('click', function () {
    inverted = !inverted
    updateAllUIState()
  })
  $('.poofer-individual-toggle-button').on('click', function () {
    if (selectedPoofer) {
      selectedPoofer.enabled = !selectedPoofer.enabled
      updatePooferDisplayState(selectedPoofer)
      updatePooferToggleButtonState()
      $.post(flameUrl(`/poofers/${selectedPoofer.name}`), { enabled: selectedPoofer.enabled })
    }
  })
  Object.keys(prefixes).forEach(function(prefixKey) {
    var prefix = prefixes[prefixKey]
    $(`.${prefix}-file-picker`).on('click', function(event) {
      var $selectedItem = $(event.target)
      if ($selectedItem.length === 0 || !$selectedItem.hasClass(`${prefix}-file`)) {
        return
      }
      selectedFilenames[prefix] = $selectedItem.data('name')
      if (!$selectedItem.hasClass('selected')) {
        $(`.${prefix}-file-picker li`).removeClass('selected')
        $selectedItem.addClass('selected')
      }
      postAttractFile(prefix)
      updateAllUIState()
    })
  })
  $('.hydraulics-attract-play-stop-button').on('click', function() {
    hydraulicsAttractModeActive = !hydraulicsAttractModeActive
    postHydraulicsAttractMode()
    updateAllUIState()
  })
  $('.poofer-sequence-play-stop-button').on('click', function() {
    pooferSequenceModeActive = !pooferSequenceModeActive
    postPooferSequenceMode()
    updateAllUIState()
  })
  $('.hydraulics-record-toggle-button').on('click', function() {
    hydraulicsRecordActive = !hydraulicsRecordActive
    postHydraulicsRecordMode()
    updateAllUIState()
  })
  Object.keys(toggleStates).forEach(function(p) {
    var prefix = p
    $(`.${prefix}-toggle-button`).on('click', function() {
      toggleStates[prefix] = !toggleStates[prefix]
      postToggleState(prefix)
      updateAllUIState()
    })
  })
  $('.hydraulics-attract-rename-button').on('click', function () {
    postHydraulicsAttractRename()
  })
  $('.hydraulics-attract-trash-button').on('click', function () {
    deleteHydraulicsAttractFile()
  })

  // Ajax functions
  var postHydraulicsAttractMode = function() {
    if (hydraulicsAttractModeActive && selectedHydraulicsFilename()) {
      $.post(hydraulicsUrl(), {
        state: 'attract',
        currentPlayback: selectedHydraulicsFilename()
      })
    } else {
      // just in case, make state consistent
      hydraulicsAttractModeActive = false
      $.post(hydraulicsUrl(), {
        state: 'passthrough'
      })
    }
  }
  var postPooferSequenceMode = function() {
    var name = selectedPooferFilename()
    if (name) {
      $.post(flameUrl(`patterns/${name}`), {
        active: pooferSequenceModeActive
      })
    } else {
      // just in case, make state consistent
      pooferSequenceModeActive = false
    }
  }
  postHydraulicsRecordMode = function() {
    $.post(hydraulicsUrl('playbacks'), {
      record: '' + hydraulicsRecordActive
    })
  }
  var postHydraulicsAttractRename = function() {
    var oldName = selectedHydraulicsFilename()
    var newName = $('.hydraulics-attract-name-field').val()
    if (!oldName || !newName) {
      return
    }
    $.post(hydraulicsUrl(`/playbacks/${oldName}`), {
      newName: newName
    })
  }
  var deleteHydraulicsAttractFile = function() {
    var filename = selectedHydraulicsFilename()
    if (!filename) {
      return
    }
    $.ajax(hydraulicsUrl(`/playbacks/${filename}`), {
      method: 'DELETE'
    })
  }
  var postAttractFile = function(prefix) {
    var filename = selectedFilenames[prefix]
    if (filename) {
      if (prefix === prefixes.hydraulics) {
        $.post(hydraulicsUrl(), {
          currentPlayback: filename
        })
      } else if (prefix === prefixes.poofer) {
        $.post(flameUrl(), {
          currentPlayback: filename
        })
      }
    }
  }
  var postToggleState = function(prefix) {
    if (prefix === 'poofer-main') {
      $.post(flameUrl(), {
        playState: toggleStates[prefix] ? 'play' : 'pause'
      })
    } else if (prefix === 'hydraulics-main') {
      $.post(hydraulicsUrl(), {
        state: toggleStates[prefix] ? 'passthrough' : 'nomove'
      })
    } else if (prefix === 'hydraulics-attract') {
      if (toggleStates['hydraulics-main']) {
        $.post(hydraulicsUrl(), {
          state: toggleStates[prefix] ? 'attract' : 'passthrough'
        })
      }
    }
  }

  // Ajax callbacks
  var flameDataCallback = function(data) {
    updatePooferData(data)
    updateAllUIState()
  }
  var hydraulicsDataCallback = function(data) {
    updateHydraulicsData(data)
    updateAllUIState()
  }

  // Setup polling
  var pollingFunction = function() {
    $.getJSON(flameUrl()).done(flameDataCallback)
    $.getJSON(hydraulicsUrl()).done(hydraulicsDataCallback)
  }
  setInterval(pollingFunction, pollingInterval)

  // Make sure initial UI state is correct
  updateAllUIState()
})
<commit_msg>Get poofer sequences by name<commit_after>$(function ($) {
  // Constants
  var flameHost = 'noetica-flames.local:5000'
  var pollingInterval = 1000 // in milliseconds
  var prefixes = {
    hydraulics: 'hydraulics-attract',
    poofer: 'poofer-sequence'
  }

  // Test values - replace these with empty arrays in prod
  var hydraulicsAttractFiles = ['wicked poof', 'poofy smurf', 'superfly', 'black sunshine']
  var pooferSequenceFiles = ['poofy pooferson', 'poof daddy', 'sugar poof', 'poofy mcpoofface']

  // Initial values
  var selectedPoofer = null
  var inverted = false
  var selectedFilenames = {}
  var toggleStates = {
    'poofer-main': true,
    'hydraulics-main': true,
    'hydraulics-attract': true
  }
  var hydraulicsAttractModeActive = false
  var pooferSequenceModeActive = false
  var hydraulicsRecordActive = false
  var hydraulicsState = {x:0,y:0,z:0,pid_x:0,pid_y:0,pid_z:0}

  // jQuery helpers
  $.fn.reduce = function() {
    return [].reduce.apply(this.toArray(), arguments)
  }

  // Helper functions
  var flameUrl = function(endpoint) {
    if (endpoint === undefined) {
      endpoint = ''
    }
    return `http://${flameHost}/flame${endpoint}`
  }
  var hydraulicsUrl = function(endpoint) {
    if (endpoint === undefined) {
      endpoint = ''
    }
    return `http://${flameHost}/hydraulics${endpoint}`
  }
  var getOrSetFilename = function(prefix, filename) {
    if (filename !== undefined) {
      selectedFilenames[prefix] = filename
    }
    return selectedFilenames[prefix]
  }
  var selectedHydraulicsFilename = function(filename) {
    return getOrSetFilename(prefixes.hydraulics, filename)
  }
  var selectedPooferFilename = function(filename) {
    return getOrSetFilename(prefixes.poofer, filename)
  }

  // jQuery helper functions
  var findPoofer = function(name) {
    return $('.poofer-' + name)
  }

  // Display state update functions
  var updateToggleState = function(prefix) {
    var $element = $(`.${prefix}-toggle-button`)
    var state = toggleStates[prefix]
    $element.prop('checked', state)
  }
  var updateToggleStates = function() {
    Object.keys(toggleStates).map(updateToggleState)
  }
  var updatePooferToggleButtonState = function() {
    var enabled = !selectedPoofer || selectedPoofer.enabled
    $('.poofer-individual-toggle-button').prop('checked', enabled)
  }
  var updateSelectedPooferName = function() {
    var name = (selectedPoofer && selectedPoofer.name) || 'None'
    $('.poofer-selected-display-field').html(name)
  }
  var updateSelectedPooferDependentState = function() {
    updateSelectedPooferName()
    updatePooferToggleButtonState()
  }
  var updatePooferDisplayState = function(poofer) {
    var $poofer = findPoofer(poofer.name)
    if (poofer.enabled) {
      $poofer.addClass('poofer-enabled')
    } else {
      $poofer.removeClass('poofer-enabled')
    }
  }
  var updateAllPoofersDisplayState = function() {
    allPoofers.forEach(function (pooferName) {
      var poofer = allPoofersState[pooferName]
      updatePooferDisplayState(poofer)
    })
  }
  var updateHydraulicsDisplayState = function() {
    ['x','y','z','pid_x','pid_y','pid_z'].forEach(function(coord) {
      $(`.hydraulics-label-${coord}`).text(hydraulicsState[coord])
    })
  }
  var updateFilePicker = function(prefix, fileList) {
    var $files = $(`.${prefix}-file`)
    var $fileList = $(`.${prefix}-file-picker`)
    var addedFiles = []
    var removedFiles = []
    fileList.forEach(function(file) {
      var fileInList = $files.reduce(function(acc, cur) {
        var $cur = $(cur)
        return acc || $cur.data('name') === file
      }, false)
      if (!fileInList) {
        addedFiles.push(file)
      }
    })
    $files.each(function(idx, file) {
      var $file = $(file)
      var fileInList = fileList.reduce(function(acc, cur) {
        return acc || $file.data('name') === cur
      }, false)
      if (!fileInList) {
        removedFiles.push($file)
        if ($file.hasClass('selected')) {
          selectedFilenames[prefix] = null
        }
      }
    })
    removedFiles.forEach(function($file) {
      $file.remove()
    })
    addedFiles.forEach(function(file) {
      var $file = $(`<li class="${prefix}-file sequence-file" data-name="${file}">${file}</li>`)
      $fileList.append($file)
    })
  }
  var updateHydraulicsAttractFilePicker = function() {
    updateFilePicker(prefixes.hydraulics, hydraulicsAttractFiles)
  }
  var updatePooferSequenceFilePicker = function() {
    updateFilePicker(prefixes.poofer, pooferSequenceFiles)
  }
  var updateHydraulicsAttractPlayMode = function() {
    var oldMode = hydraulicsAttractModeActive
    var $button = $('.hydraulics-attract-play-stop-button')
    if (!selectedHydraulicsFilename()) {
      hydraulicsAttractModeActive = false
      $button.attr('disabled', true)
    } else if (!toggleStates['hydraulics-attract']) {
      $button.attr('disabled', true)
    } else {
      $button.attr('disabled', false)
    }
    if (hydraulicsAttractModeActive) {
      $button.removeClass('btn-play')
      $button.addClass('btn-stop')
    } else {
      $button.removeClass('btn-stop')
      $button.addClass('btn-play')
    }
    $button.attr('title', hydraulicsAttractModeActive ? 'Stop' : 'Start')
    if (hydraulicsAttractModeActive !== oldMode) {
      postHydraulicsAttractMode()
    }
  }
  var updatePooferSequencePlayMode = function() {
    var oldMode = pooferSequenceModeActive
    var $button = $('.poofer-sequence-play-stop-button')
    if (!selectedPooferFilename()) {
      pooferSequenceModeActive = false
      $button.attr('disabled', true)
    } else {
      $button.attr('disabled', false)
    }
    if (pooferSequenceModeActive) {
      $button.removeClass('btn-play')
      $button.addClass('btn-stop')
    } else {
      $button.removeClass('btn-stop')
      $button.addClass('btn-play')
    }
    $button.attr('title', pooferSequenceModeActive ? 'Stop' : 'Start')
    if (pooferSequenceModeActive !== oldMode) {
      postPooferSequenceMode()
    }
  }
  var updateHydraulicsRecordButtonState = function() {
    var $button = $('.hydraulics-record-toggle-button')
    if (hydraulicsRecordActive) {
      $button.removeClass('btn-record')
      $button.addClass('btn-stop')
    } else {
      $button.removeClass('btn-stop')
      $button.addClass('btn-record')
    }
    $button.attr('title', hydraulicsAttractModeActive ? 'Stop Recording' : 'Record')

  }
  var updateSelectedFileDependentState = function() {
    updateHydraulicsAttractPlayMode()
    updatePooferSequencePlayMode()
  }
  var updateColorInversion = function() {
    if (inverted) {
      $('html').addClass('inverted')
    }
    else {
      $('html').removeClass('inverted')
    }
  }
  var updateAllUIState = function() {
    // Since we don't have automatic data binding and it's a pain to call update functions
    // in just the right places, this function just updates everything and is called
    // when anything changes. Where possible, functions called from here should be
    // written to not force refreshes of state that didn't actually change.
    updateToggleStates()
    updateSelectedPooferDependentState()
    updateAllPoofersDisplayState()
    updateHydraulicsDisplayState()
    updateHydraulicsAttractFilePicker()
    updatePooferSequenceFilePicker()
    updateSelectedFileDependentState()
    updateHydraulicsRecordButtonState()
    updateColorInversion()
  }

  // Data state setting functions
  var setSelectedPoofer = function(name) {
    selectedPoofer = allPoofersState[name]
    updateSelectedPooferDependentState()
  }
  var updatePooferData = function (data) {
    data.poofers.forEach(function(poofer) {
      var pooferState = allPoofersState[poofer.id.toLowerCase()]
      pooferState.enabled = poofer.enabled
    })
    pooferSequenceFiles = data.patterns.map(function(p) { return p.name })
  }
  var updateHydraulicsData = function (data) {
    hydraulicsState = data
    hydraulicsAttractFiles = data.playbacks
  }

  // Setup poofer data structures
  var allPoofers = ['nn','nw','nt','ne','wn','tn','en','ww','wt','tw','tt','te','et','ee','ws','ts','es','sw','st','se','ss','bn','bs','be','bw']
  var PooferState = function(name) {
    this.name = name
    this.enabled = true
    return this
  }
  var allPoofersState = {}
  for (var i = 0; i < allPoofers.length; i++) {
    var name = allPoofers[i]
    allPoofersState[name] = new PooferState(name)
    // Setup poofer event handlers
    var $currentPoofer = findPoofer(name)
    ;(function () {
      var tempName = name
      $currentPoofer.on('click', function () {
        setSelectedPoofer(tempName)
      })
    })()
  }

  // Setup button event handlers
  $('.invert-button').on('click', function () {
    inverted = !inverted
    updateAllUIState()
  })
  $('.poofer-individual-toggle-button').on('click', function () {
    if (selectedPoofer) {
      selectedPoofer.enabled = !selectedPoofer.enabled
      updatePooferDisplayState(selectedPoofer)
      updatePooferToggleButtonState()
      $.post(flameUrl(`/poofers/${selectedPoofer.name}`), { enabled: selectedPoofer.enabled })
    }
  })
  Object.keys(prefixes).forEach(function(prefixKey) {
    var prefix = prefixes[prefixKey]
    $(`.${prefix}-file-picker`).on('click', function(event) {
      var $selectedItem = $(event.target)
      if ($selectedItem.length === 0 || !$selectedItem.hasClass(`${prefix}-file`)) {
        return
      }
      selectedFilenames[prefix] = $selectedItem.data('name')
      if (!$selectedItem.hasClass('selected')) {
        $(`.${prefix}-file-picker li`).removeClass('selected')
        $selectedItem.addClass('selected')
      }
      postAttractFile(prefix)
      updateAllUIState()
    })
  })
  $('.hydraulics-attract-play-stop-button').on('click', function() {
    hydraulicsAttractModeActive = !hydraulicsAttractModeActive
    postHydraulicsAttractMode()
    updateAllUIState()
  })
  $('.poofer-sequence-play-stop-button').on('click', function() {
    pooferSequenceModeActive = !pooferSequenceModeActive
    postPooferSequenceMode()
    updateAllUIState()
  })
  $('.hydraulics-record-toggle-button').on('click', function() {
    hydraulicsRecordActive = !hydraulicsRecordActive
    postHydraulicsRecordMode()
    updateAllUIState()
  })
  Object.keys(toggleStates).forEach(function(p) {
    var prefix = p
    $(`.${prefix}-toggle-button`).on('click', function() {
      toggleStates[prefix] = !toggleStates[prefix]
      postToggleState(prefix)
      updateAllUIState()
    })
  })
  $('.hydraulics-attract-rename-button').on('click', function () {
    postHydraulicsAttractRename()
  })
  $('.hydraulics-attract-trash-button').on('click', function () {
    deleteHydraulicsAttractFile()
  })

  // Ajax functions
  var postHydraulicsAttractMode = function() {
    if (hydraulicsAttractModeActive && selectedHydraulicsFilename()) {
      $.post(hydraulicsUrl(), {
        state: 'attract',
        currentPlayback: selectedHydraulicsFilename()
      })
    } else {
      // just in case, make state consistent
      hydraulicsAttractModeActive = false
      $.post(hydraulicsUrl(), {
        state: 'passthrough'
      })
    }
  }
  var postPooferSequenceMode = function() {
    var name = selectedPooferFilename()
    if (name) {
      $.post(flameUrl(`patterns/${name}`), {
        active: pooferSequenceModeActive
      })
    } else {
      // just in case, make state consistent
      pooferSequenceModeActive = false
    }
  }
  postHydraulicsRecordMode = function() {
    $.post(hydraulicsUrl('playbacks'), {
      record: '' + hydraulicsRecordActive
    })
  }
  var postHydraulicsAttractRename = function() {
    var oldName = selectedHydraulicsFilename()
    var newName = $('.hydraulics-attract-name-field').val()
    if (!oldName || !newName) {
      return
    }
    $.post(hydraulicsUrl(`/playbacks/${oldName}`), {
      newName: newName
    })
  }
  var deleteHydraulicsAttractFile = function() {
    var filename = selectedHydraulicsFilename()
    if (!filename) {
      return
    }
    $.ajax(hydraulicsUrl(`/playbacks/${filename}`), {
      method: 'DELETE'
    })
  }
  var postAttractFile = function(prefix) {
    var filename = selectedFilenames[prefix]
    if (filename) {
      if (prefix === prefixes.hydraulics) {
        $.post(hydraulicsUrl(), {
          currentPlayback: filename
        })
      } else if (prefix === prefixes.poofer) {
        $.post(flameUrl(), {
          currentPlayback: filename
        })
      }
    }
  }
  var postToggleState = function(prefix) {
    if (prefix === 'poofer-main') {
      $.post(flameUrl(), {
        playState: toggleStates[prefix] ? 'play' : 'pause'
      })
    } else if (prefix === 'hydraulics-main') {
      $.post(hydraulicsUrl(), {
        state: toggleStates[prefix] ? 'passthrough' : 'nomove'
      })
    } else if (prefix === 'hydraulics-attract') {
      if (toggleStates['hydraulics-main']) {
        $.post(hydraulicsUrl(), {
          state: toggleStates[prefix] ? 'attract' : 'passthrough'
        })
      }
    }
  }

  // Ajax callbacks
  var flameDataCallback = function(data) {
    updatePooferData(data)
    updateAllUIState()
  }
  var hydraulicsDataCallback = function(data) {
    updateHydraulicsData(data)
    updateAllUIState()
  }

  // Setup polling
  var pollingFunction = function() {
    $.getJSON(flameUrl()).done(flameDataCallback)
    $.getJSON(hydraulicsUrl()).done(hydraulicsDataCallback)
  }
  setInterval(pollingFunction, pollingInterval)

  // Make sure initial UI state is correct
  updateAllUIState()
})
<|endoftext|>