<commit_before>import trimStart from 'lodash/trimStart';
import semaphore from "semaphore";
import AuthenticationPage from "./AuthenticationPage";
import API from "./API";
import { fileExtension } from '../../lib/pathHelper'

const MAX_CONCURRENT_DOWNLOADS = 10;

export default class GitHub {
  constructor(config, proxied = false) {
    this.config = config;

    if (!proxied && config.getIn(["backend", "repo"]) == null) {
      throw new Error("The GitHub backend needs a \"repo\" in the backend configuration.");
    }

    this.repo = config.getIn(["backend", "repo"], "");
    this.branch = config.getIn(["backend", "branch"], "master");
    this.api_root = config.getIn(["backend", "api_root"], "https://api.github.com");
    this.token = '';
  }

  authComponent() {
    return AuthenticationPage;
  }

  restoreUser(user) {
    return this.authenticate(user);
  }

  authenticate(state) {
    this.token = state.token;
    this.api = new API({ token: this.token, branch: this.branch, repo: this.repo, api_root: this.api_root });
    return this.api.user().then(user =>
      this.api.hasWriteAccess().then((isCollab) => {
        // Unauthorized user
        if (!isCollab) throw new Error("Your GitHub user account does not have access to this repo.");
        // Authorized user
        user.token = state.token;
        return user;
      })
    );
  }

  logout() {
    this.token = null;
    return;
  }

  getToken() {
    return Promise.resolve(this.token);
  }

  entriesByFolder(collection, extension) {
    return this.api.listFiles(collection.get("folder"))
    .then(files => files.filter(file => fileExtension(file.name) === extension))
    .then(this.fetchFiles);
  }

  entriesByFiles(collection) {
    const files = collection.get("files").map(collectionFile => ({
      path: collectionFile.get("file"),
      label: collectionFile.get("label"),
    }));
    return this.fetchFiles(files);
  }

  fetchFiles = (files) => {
    const sem = semaphore(MAX_CONCURRENT_DOWNLOADS);
    const promises = [];
    files.forEach((file) => {
      promises.push(new Promise((resolve, reject) => (
        sem.take(() => this.api.readFile(file.path, file.sha).then((data) => {
          resolve({ file, data });
          sem.leave();
        }).catch((err) => {
          sem.leave();
          reject(err);
        }))
      )));
    });
    return Promise.all(promises);
  };

  // Fetches a single entry.
  getEntry(collection, slug, path) {
    return this.api.readFile(path).then(data => ({
      file: { path },
      data,
    }));
  }

  getMedia() {
    return this.api.listFiles(this.config.get('media_folder'))
      .then(files => files.filter(file => file.type === 'file'))
      .then(files => files.map(({ sha, name, size, download_url, path }) => {
        return { id: sha, name, size, url: download_url, path };
      }));
  }

  persistEntry(entry, mediaFiles = [], options = {}) {
    return this.api.persistFiles(entry, mediaFiles, options);
  }

  async persistMedia(mediaFile, options = {}) {
    try {
      const response = await this.api.persistFiles(null, [mediaFile], options);
      const { value, size, path, fileObj } = mediaFile;
      const url = `https://raw.githubusercontent.com/${this.repo}/${this.branch}${path}`;
      return { id: response.sha, name: value, size: fileObj.size, url, path: trimStart(path, '/') };
    }
    catch(error) {
      console.error(error);
      throw error;
    }
  }

  deleteFile(path, commitMessage, options) {
    return this.api.deleteFile(path, commitMessage, options);
  }

  unpublishedEntries() {
    return this.api.listUnpublishedBranches().then((branches) => {
      const sem = semaphore(MAX_CONCURRENT_DOWNLOADS);
      const promises = [];
      branches.map((branch) => {
        promises.push(new Promise((resolve, reject) => {
          const slug = branch.ref.split("refs/heads/cms/").pop();
          return sem.take(() => this.api.readUnpublishedBranchFile(slug).then((data) => {
            if (data === null || data === undefined) {
              resolve(null);
              sem.leave();
            } else {
              const path = data.metaData.objects.entry.path;
              resolve({
                slug,
                file: { path },
                data: data.fileData,
                metaData: data.metaData,
                isModification: data.isModification,
              });
              sem.leave();
            }
          }).catch((err) => {
            sem.leave();
            resolve(null);
          }));
        }));
      });
      return Promise.all(promises);
    })
    .catch((error) => {
      if (error.message === "Not Found") {
        return Promise.resolve([]);
      }
      return error;
    });
  }

  unpublishedEntry(collection, slug) {
    return this.api.readUnpublishedBranchFile(slug)
    .then((data) => {
      if (!data) return null;
      return {
        slug,
        file: { path: data.metaData.objects.entry.path },
        data: data.fileData,
        metaData: data.metaData,
        isModification: data.isModification,
      };
    });
  }

  updateUnpublishedEntryStatus(collection, slug, newStatus) {
    return this.api.updateUnpublishedEntryStatus(collection, slug, newStatus);
  }

  deleteUnpublishedEntry(collection, slug) {
    return this.api.deleteUnpublishedEntry(collection, slug);
  }
  publishUnpublishedEntry(collection, slug) {
    return this.api.publishUnpublishedEntry(collection, slug);
  }
}
<commit_msg>fix image not shown after upload for git gateway<commit_after>import trimStart from 'lodash/trimStart';
import semaphore from "semaphore";
import AuthenticationPage from "./AuthenticationPage";
import API from "./API";
import { fileExtension } from '../../lib/pathHelper'

const MAX_CONCURRENT_DOWNLOADS = 10;

export default class GitHub {
  constructor(config, proxied = false) {
    this.config = config;

    if (!proxied && config.getIn(["backend", "repo"]) == null) {
      throw new Error("The GitHub backend needs a \"repo\" in the backend configuration.");
    }

    this.repo = config.getIn(["backend", "repo"], "");
    this.branch = config.getIn(["backend", "branch"], "master");
    this.api_root = config.getIn(["backend", "api_root"], "https://api.github.com");
    this.token = '';
  }

  authComponent() {
    return AuthenticationPage;
  }

  restoreUser(user) {
    return this.authenticate(user);
  }

  authenticate(state) {
    this.token = state.token;
    this.api = new API({ token: this.token, branch: this.branch, repo: this.repo, api_root: this.api_root });
    return this.api.user().then(user =>
      this.api.hasWriteAccess().then((isCollab) => {
        // Unauthorized user
        if (!isCollab) throw new Error("Your GitHub user account does not have access to this repo.");
        // Authorized user
        user.token = state.token;
        return user;
      })
    );
  }

  logout() {
    this.token = null;
    return;
  }

  getToken() {
    return Promise.resolve(this.token);
  }

  entriesByFolder(collection, extension) {
    return this.api.listFiles(collection.get("folder"))
    .then(files => files.filter(file => fileExtension(file.name) === extension))
    .then(this.fetchFiles);
  }

  entriesByFiles(collection) {
    const files = collection.get("files").map(collectionFile => ({
      path: collectionFile.get("file"),
      label: collectionFile.get("label"),
    }));
    return this.fetchFiles(files);
  }

  fetchFiles = (files) => {
    const sem = semaphore(MAX_CONCURRENT_DOWNLOADS);
    const promises = [];
    files.forEach((file) => {
      promises.push(new Promise((resolve, reject) => (
        sem.take(() => this.api.readFile(file.path, file.sha).then((data) => {
          resolve({ file, data });
          sem.leave();
        }).catch((err) => {
          sem.leave();
          reject(err);
        }))
      )));
    });
    return Promise.all(promises);
  };

  // Fetches a single entry.
  getEntry(collection, slug, path) {
    return this.api.readFile(path).then(data => ({
      file: { path },
      data,
    }));
  }

  getMedia() {
    return this.api.listFiles(this.config.get('media_folder'))
      .then(files => files.filter(file => file.type === 'file'))
      .then(files => files.map(({ sha, name, size, download_url, path }) => {
        return { id: sha, name, size, url: download_url, path };
      }));
  }

  persistEntry(entry, mediaFiles = [], options = {}) {
    return this.api.persistFiles(entry, mediaFiles, options);
  }

  /**
   * Pulls repo info from a `repos` response url property.
   *
   * Turns this:
   * '<api_root>/repo/<username>/<repo>/...'
   *
   * Into this:
   * '<username>/<repo>'
   */
  getRepoFromResponseUrl(url) {
    return url

      // -> '/repo/<username>/<repo>/...'
      .slice(this.api_root.length)

      // -> [ '', 'repo', '<username>', '<repo>', ... ]
      .split('/')

      // -> [ '<username>', '<repo>' ]
      .slice(2, 4)

      // -> '<username>/<repo>'
      .join('/');
  }

  async persistMedia(mediaFile, options = {}) {
    try {
      const response = await this.api.persistFiles(null, [mediaFile], options);
      const repo = this.repo || this.getRepoFromResponseUrl(response.url);
      const { value, size, path, fileObj } = mediaFile;
      const url = `https://raw.githubusercontent.com/${repo}/${this.branch}${path}`;
      return { id: response.sha, name: value, size: fileObj.size, url, path: trimStart(path, '/') };
    }
    catch(error) {
      console.error(error);
      throw error;
    }
  }

  deleteFile(path, commitMessage, options) {
    return this.api.deleteFile(path, commitMessage, options);
  }

  unpublishedEntries() {
    return this.api.listUnpublishedBranches().then((branches) => {
      const sem = semaphore(MAX_CONCURRENT_DOWNLOADS);
      const promises = [];
      branches.map((branch) => {
        promises.push(new Promise((resolve, reject) => {
          const slug = branch.ref.split("refs/heads/cms/").pop();
          return sem.take(() => this.api.readUnpublishedBranchFile(slug).then((data) => {
            if (data === null || data === undefined) {
              resolve(null);
              sem.leave();
            } else {
              const path = data.metaData.objects.entry.path;
              resolve({
                slug,
                file: { path },
                data: data.fileData,
                metaData: data.metaData,
                isModification: data.isModification,
              });
              sem.leave();
            }
          }).catch((err) => {
            sem.leave();
            resolve(null);
          }));
        }));
      });
      return Promise.all(promises);
    })
    .catch((error) => {
      if (error.message === "Not Found") {
        return Promise.resolve([]);
      }
      return error;
    });
  }

  unpublishedEntry(collection, slug) {
    return this.api.readUnpublishedBranchFile(slug)
    .then((data) => {
      if (!data) return null;
      return {
        slug,
        file: { path: data.metaData.objects.entry.path },
        data: data.fileData,
        metaData: data.metaData,
        isModification: data.isModification,
      };
    });
  }

  updateUnpublishedEntryStatus(collection, slug, newStatus) {
    return this.api.updateUnpublishedEntryStatus(collection, slug, newStatus);
  }

  deleteUnpublishedEntry(collection, slug) {
    return this.api.deleteUnpublishedEntry(collection, slug);
  }
  publishUnpublishedEntry(collection, slug) {
    return this.api.publishUnpublishedEntry(collection, slug);
  }
}
<|endoftext|>


<commit_before>import CompletionsInteractor from '../usecases/completions';
import CommandInteractor from '../usecases/command';
import Completions from '../domains/completions';

export default class CommandController {
  constructor() {
    this.completionsInteractor = new CompletionsInteractor();
    this.commandIndicator = new CommandInteractor();
  }

  getCompletions(line) {
    let trimmed = line.trimStart();
    let words = trimmed.split(/ +/);
    let name = words[0];
    if (words.length === 1) {
      return this.completionsInteractor.queryConsoleCommand(name);
    }
    let keywords = trimmed.slice(name.length).trimStart();
    switch (words[0]) {
    case 'o':
    case 'open':
    case 't':
    case 'tabopen':
    case 'w':
    case 'winopen':
      return this.completionsInteractor.queryOpen(name, keywords);
    case 'b':
    case 'buffer':
      return this.completionsInteractor.queryBuffer(name, keywords);
    case 'bd':
    case 'bdel':
    case 'bdelete':
    case 'bdeletes':
      return this.completionsInteractor.queryBdelete(name, keywords);
    case 'bd!':
    case 'bdel!':
    case 'bdelete!':
    case 'bdeletes!':
      return this.completionsInteractor.queryBdeleteForce(name, keywords);
    case 'set':
      return this.completionsInteractor.querySet(name, keywords);
    }
    return Promise.resolve(Completions.empty());
  }

  // eslint-disable-next-line complexity
  exec(line) {
    let trimmed = line.trimStart();
    let words = trimmed.split(/ +/);
    let name = words[0];
    if (words[0].length === 0) {
      return Promise.resolve();
    }

    let keywords = trimmed.slice(name.length).trimStart();
    switch (words[0]) {
    case 'o':
    case 'open':
      return this.commandIndicator.open(keywords);
    case 't':
    case 'tabopen':
      return this.commandIndicator.tabopen(keywords);
    case 'w':
    case 'winopen':
      return this.commandIndicator.winopen(keywords);
    case 'b':
    case 'buffer':
      return this.commandIndicator.buffer(keywords);
    case 'bd':
    case 'bdel':
    case 'bdelete':
      return this.commandIndicator.bdelete(false, keywords);
    case 'bd!':
    case 'bdel!':
    case 'bdelete!':
      return this.commandIndicator.bdelete(true, keywords);
    case 'bdeletes':
      return this.commandIndicator.bdeletes(false, keywords);
    case 'bdeletes!':
      return this.commandIndicator.bdeletes(true, keywords);
    case 'addbookmark':
      return this.commandIndicator.addbookmark(keywords);
    case 'q':
    case 'quit':
      return this.commandIndicator.quit();
    case 'qa':
    case 'quitall':
      return this.commandIndicator.quitAll();
    case 'set':
      return this.commandIndicator.set(keywords);
    }
    throw new Error(words[0] + ' command is not defined');
  }
}
<commit_msg>Replace trimStart<commit_after>import CompletionsInteractor from '../usecases/completions';
import CommandInteractor from '../usecases/command';
import Completions from '../domains/completions';

const trimStart = (str) => {
  // NOTE String.trimStart is available on Firefox 61
  return str.replace(/^\s+/, '');
};

export default class CommandController {
  constructor() {
    this.completionsInteractor = new CompletionsInteractor();
    this.commandIndicator = new CommandInteractor();
  }

  getCompletions(line) {
    let trimmed = trimStart(line);
    let words = trimmed.split(/ +/);
    let name = words[0];
    if (words.length === 1) {
      return this.completionsInteractor.queryConsoleCommand(name);
    }
    let keywords = trimStart(trimmed.slice(name.length));
    switch (words[0]) {
    case 'o':
    case 'open':
    case 't':
    case 'tabopen':
    case 'w':
    case 'winopen':
      return this.completionsInteractor.queryOpen(name, keywords);
    case 'b':
    case 'buffer':
      return this.completionsInteractor.queryBuffer(name, keywords);
    case 'bd':
    case 'bdel':
    case 'bdelete':
    case 'bdeletes':
      return this.completionsInteractor.queryBdelete(name, keywords);
    case 'bd!':
    case 'bdel!':
    case 'bdelete!':
    case 'bdeletes!':
      return this.completionsInteractor.queryBdeleteForce(name, keywords);
    case 'set':
      return this.completionsInteractor.querySet(name, keywords);
    }
    return Promise.resolve(Completions.empty());
  }

  // eslint-disable-next-line complexity
  exec(line) {
    let trimmed = trimStart(line);
    let words = trimmed.split(/ +/);
    let name = words[0];
    if (words[0].length === 0) {
      return Promise.resolve();
    }

    let keywords = trimStart(trimmed.slice(name.length));
    switch (words[0]) {
    case 'o':
    case 'open':
      return this.commandIndicator.open(keywords);
    case 't':
    case 'tabopen':
      return this.commandIndicator.tabopen(keywords);
    case 'w':
    case 'winopen':
      return this.commandIndicator.winopen(keywords);
    case 'b':
    case 'buffer':
      return this.commandIndicator.buffer(keywords);
    case 'bd':
    case 'bdel':
    case 'bdelete':
      return this.commandIndicator.bdelete(false, keywords);
    case 'bd!':
    case 'bdel!':
    case 'bdelete!':
      return this.commandIndicator.bdelete(true, keywords);
    case 'bdeletes':
      return this.commandIndicator.bdeletes(false, keywords);
    case 'bdeletes!':
      return this.commandIndicator.bdeletes(true, keywords);
    case 'addbookmark':
      return this.commandIndicator.addbookmark(keywords);
    case 'q':
    case 'quit':
      return this.commandIndicator.quit();
    case 'qa':
    case 'quitall':
      return this.commandIndicator.quitAll();
    case 'set':
      return this.commandIndicator.set(keywords);
    }
    throw new Error(words[0] + ' command is not defined');
  }
}
<|endoftext|>


<commit_before>var Article = mongoose.model('Article')
  , Comment = mongoose.model('Comment')

module.exports = function(app){

  // New article
  app.get('/articles/new', function(req, res){
    res.render('articles/new', {
      title: 'New Article',
      article: new Article({})
    })
  })

  app.param('id', function(req, res, next, id){
    Article
      .findOne({ _id : req.params.id })
      .populate('comments')
      .run(function(err,article) {
        if (err) return next(err)
        if (!article) return next(new Error('Failed to load article ' + id))
        req.article = article
        Comment
          .find({article : req.article})
          .run(function (err, comments) {
            if (err) throw err
            req.comments = comments
            next()
          })
      })
  })

  // Create an article
  app.post('/articles', function(req, res){
    article = new Article(req.body.article)

    article.save(function(err){
      if (err) {
        utils.mongooseErrorHandler(err, req)
        res.render('articles/new', {
            title: 'New Article'
          , article: article
        })
      }
      else {
        req.flash('notice', 'Created successfully')
        res.redirect('/article/'+article._id)
      }
    })
  })

  // Edit an article
  app.get('/article/:id/edit', function(req, res){
    res.render('articles/edit', {
      title: 'Edit '+req.article.title,
      article: req.article
    })
  })

  // Update article
  app.put('/articles/:id', function(req, res){
    article = req.article

    if (req.body.article.title) article.title = req.body.article.title
    if (req.body.article.body) article.body = req.body.article.body
    req.flash('notice', 'Updated successfully')

    article.save(function(err, doc) {
      if (err) throw err
      res.redirect('/article/'+req.body.article._id)
    })
  })

  // View an article
  app.get('/article/:id', function(req, res){
    res.render('articles/show', {
      title: req.article.title,
      article: req.article,
      comments: req.comments
    })
  })

  // Delete an article
  app.del('/article/:id', function(req, res){
    article = req.article
    article.remove(function(err){
      req.flash('notice', 'Deleted successfully')
      res.redirect('/articles')
    })
  })

  // Listing of Articles
  app.get('/articles', function(req, res){
    Article
      .find({})
      .desc('created_at') // sort by date
      .run(function(err, articles) {
        res.render('articles/index', {
          title: 'List of Articles',
          articles: articles
        })
      })
  })
}
<commit_msg>add route for home path<commit_after>var Article = mongoose.model('Article')
  , Comment = mongoose.model('Comment')

module.exports = function(app){

  // New article
  app.get('/articles/new', function(req, res){
    res.render('articles/new', {
      title: 'New Article',
      article: new Article({})
    })
  })

  app.param('id', function(req, res, next, id){
    Article
      .findOne({ _id : req.params.id })
      .populate('comments')
      .run(function(err,article) {
        if (err) return next(err)
        if (!article) return next(new Error('Failed to load article ' + id))
        req.article = article
        Comment
          .find({article : req.article})
          .run(function (err, comments) {
            if (err) throw err
            req.comments = comments
            next()
          })
      })
  })

  // Create an article
  app.post('/articles', function(req, res){
    article = new Article(req.body.article)

    article.save(function(err){
      if (err) {
        utils.mongooseErrorHandler(err, req)
        res.render('articles/new', {
            title: 'New Article'
          , article: article
        })
      }
      else {
        req.flash('notice', 'Created successfully')
        res.redirect('/article/'+article._id)
      }
    })
  })

  // Edit an article
  app.get('/article/:id/edit', function(req, res){
    res.render('articles/edit', {
      title: 'Edit '+req.article.title,
      article: req.article
    })
  })

  // Update article
  app.put('/articles/:id', function(req, res){
    article = req.article

    if (req.body.article.title) article.title = req.body.article.title
    if (req.body.article.body) article.body = req.body.article.body
    req.flash('notice', 'Updated successfully')

    article.save(function(err, doc) {
      if (err) throw err
      res.redirect('/article/'+req.body.article._id)
    })
  })

  // View an article
  app.get('/article/:id', function(req, res){
    res.render('articles/show', {
      title: req.article.title,
      article: req.article,
      comments: req.comments
    })
  })

  // Delete an article
  app.del('/article/:id', function(req, res){
    article = req.article
    article.remove(function(err){
      req.flash('notice', 'Deleted successfully')
      res.redirect('/articles')
    })
  })

  // Listing of Articles
  app.get('/articles', function(req, res){
    Article
      .find({})
      .desc('created_at') // sort by date
      .run(function(err, articles) {
        res.render('articles/index', {
          title: 'List of Articles',
          articles: articles
        })
      })
  })

  // home
  app.get('/', function(req, res){
    res.redirect('/articles')
  })
}
<|endoftext|>


<commit_before>/**
 * Returns the type of content in the current document,
 * currently either 'PDF' or 'HTML'.
 *
 * This function is injected as a content script into tabs in
 * order to detect the type of content on the page (PDF, HTML) etc.
 * by sniffing for viewer plugins.
 *
 * In future this could also be extended to support extraction of the URLs
 * of content in embedded viewers where that differs from the tab's
 * main URL.
 */
export default function detectContentType(document_) {
  document_ = document_ || document;

  function detectChromePDFViewer() {
    // When viewing a PDF in Chrome, the viewer consists of a top-level
    // document with an <embed> tag, which in turn instantiates an inner HTML
    // document providing the PDF viewer UI plus another <embed> tag which
    // instantiates the native PDF renderer.
    //
    // The selector below matches the <embed> tag in the top-level document. To
    // see this document, open the developer tools from Chrome's menu rather
    // than right-clicking on the viewport and selecting the 'Inspect' option
    // which will instead show the _inner_ document.
    if (document_.querySelector('embed[type="application/pdf"]')) {
      return { type: 'PDF' };
    }
    return null;
  }

  function detectFirefoxPDFViewer() {
    // The Firefox PDF viewer is an instance of PDF.js.
    //
    // The Firefox PDF plugin specifically can be detected via the <base>
    // tag it includes, which can be done from a content script (which runs
    // in an isolated JS world from the page's own scripts).
    //
    // Generic PDF.js detection can be done by looking for the
    // `window.PDFViewerApplication` object. This however requires running JS
    // code in the same JS context as the page's own code.
    if (document_.baseURI.indexOf('resource://pdf.js') === 0) {
      return { type: 'PDF' };
    }
    return null;
  }

  const detectFns = [detectChromePDFViewer, detectFirefoxPDFViewer];
  for (let i = 0; i < detectFns.length; i++) {
    const typeInfo = detectFns[i]();
    if (typeInfo) {
      return typeInfo;
    }
  }

  return { type: 'HTML' };
}
<commit_msg>Improve JSDoc for detect-content-type.js<commit_after>/**
 * Returns the type of content in the current document,
 * currently either 'PDF' or 'HTML'.
 *
 * This function is injected as a content script into tabs in
 * order to detect the type of content on the page (PDF, HTML) etc.
 * by sniffing for viewer plugins.
 *
 * In future this could also be extended to support extraction of the URLs
 * of content in embedded viewers where that differs from the tab's
 * main URL.
 *
 * @param {Document} document_ - Test seam
 */
export default function detectContentType(document_ = document) {
  function detectChromePDFViewer() {
    // When viewing a PDF in Chrome, the viewer consists of a top-level
    // document with an <embed> tag, which in turn instantiates an inner HTML
    // document providing the PDF viewer UI plus another <embed> tag which
    // instantiates the native PDF renderer.
    //
    // The selector below matches the <embed> tag in the top-level document. To
    // see this document, open the developer tools from Chrome's menu rather
    // than right-clicking on the viewport and selecting the 'Inspect' option
    // which will instead show the _inner_ document.
    if (document_.querySelector('embed[type="application/pdf"]')) {
      return { type: 'PDF' };
    }
    return null;
  }

  function detectFirefoxPDFViewer() {
    // The Firefox PDF viewer is an instance of PDF.js.
    //
    // The Firefox PDF plugin specifically can be detected via the <base>
    // tag it includes, which can be done from a content script (which runs
    // in an isolated JS world from the page's own scripts).
    //
    // Generic PDF.js detection can be done by looking for the
    // `window.PDFViewerApplication` object. This however requires running JS
    // code in the same JS context as the page's own code.
    if (document_.baseURI.indexOf('resource://pdf.js') === 0) {
      return { type: 'PDF' };
    }
    return null;
  }

  const detectFns = [detectChromePDFViewer, detectFirefoxPDFViewer];
  for (let i = 0; i < detectFns.length; i++) {
    const typeInfo = detectFns[i]();
    if (typeInfo) {
      return typeInfo;
    }
  }

  return { type: 'HTML' };
}
<|endoftext|>


<commit_before>import { CallbackNotifier, AcceptNotifier } from 'common/notifiers';
// import { INITIALIZE } from 'base/messages';
import { ROOT_COMPONENT_ID } from 'base/entries';
import ReactDOM from 'react-dom';

export default {
  create({ app }) {

    // re-render on each notification
    app.notifier.push(CallbackNotifier.create(function() {
        var rootComponentEntry = app.registry.find(sift({ id: ROOT_COMPONENT_ID }));
        ReactDOM.render(rootComponentEntry.create({ app: app }), app.config.element);
    }));
  }
};
<commit_msg>remove root renderer<commit_after><|endoftext|>


<commit_before>
// https://stackoverflow.com/questions/18720218/get-a-browser-rendered-htmljavascript
console.log(document.documentElement);
<commit_msg>Update getPageHTML.js<commit_after>
console.log(document.documentElement);

// https://stackoverflow.com/questions/18720218/get-a-browser-rendered-htmljavascript
<|endoftext|>


<commit_before>/*jshint forin:false, eqnull:true*/
/* globals JSYG*/

(function(factory) {

    if (typeof define != "undefined" && define.amd) define("jsyg-container",["jsyg","jsyg-path"],factory);
    else if (typeof JSYG != "undefined") {

        if (JSYG.Path) factory(JSYG,JSYG.Alignment);
        else throw new Error("JSYG.Path is needed");
    }
    else throw new Error("JSYG is needed");

})(function(JSYG,Alignment) {

    "use strict";

    function Container(arg) {

        if (!(this instanceof Container)) return new Container(arg);

        if (!arg) arg = '<g>';

        JSYG.call(this,arg);

        if (this.getTag() != "g") throw new Error("L'argument ne fait pas référence à un conteneur g.");
    }

    Container.prototype = Object.create(JSYG.prototype);

    Container.prototype.constructor = Container;

    Container.prototype.onadditem = null;
    Container.prototype.onfreeitem = null;
    Container.prototype.onchange = null;

    Container.prototype.addItems = function(elmt) {

        var that = this,
        mtx = this.getMtx().inverse();

        JSYG.makeArray(arguments).forEach(function(elmt) {

            new JSYG(elmt).each(function() {

                var $this = new JSYG(this);

                try { $this.addMtx(mtx); } //éléments non tracés
                catch(e){}

                $this.appendTo(that[0]);

                that.trigger('additem',that[0],this);
                that.trigger('change');

            });

        });

        return this;
    };

    Container.prototype.applyTransform = function() {

        var mtx = this.getMtx(),
        that = this;

        this.children().each(function() {
            var $this = new JSYG(this);
            $this.setMtx( mtx.multiply($this.getMtx(that)) );
        });

        this.resetTransf();

        return this;
    };

    Container.prototype.freeItems = function(elmt) {

        var parent = this.parent()[0],
        mtx = this.getMtx(),
        that = this,
        args = JSYG.makeArray( arguments.length === 0 ? this.children() : arguments);

        args.forEach(function(elmt) {

            new JSYG(elmt).each(function() {

                var $this = new JSYG(this);

                if (!$this.isChildOf(that)) return;

                try { $this.setMtx( mtx.multiply($this.getMtx(that)) ); }
                catch(e) {}

                $this.appendTo(parent);

                that.trigger('freeitem',that[0],this);
                that.trigger('change');

            });
        });

        return this;
    };

    JSYG.Container = Container;

    return Container;

});<commit_msg>Suppression des fonctions d'alignement (plugin spécifique Alignment).<commit_after>/*jshint forin:false, eqnull:true*/
/* globals JSYG*/

(function(factory) {

    if (typeof define != "undefined" && define.amd) define("jsyg-container",["jsyg","jsyg-path"],factory);
    else if (typeof JSYG != "undefined") {

        if (JSYG.Path) factory(JSYG);
        else throw new Error("JSYG.Path is needed");
    }
    else throw new Error("JSYG is needed");

})(function(JSYG) {

    "use strict";

    function Container(arg) {

        if (!(this instanceof Container)) return new Container(arg);

        if (!arg) arg = '<g>';

        JSYG.call(this,arg);

        if (this.getTag() != "g") throw new Error("L'argument ne fait pas référence à un conteneur g.");
    }

    Container.prototype = Object.create(JSYG.prototype);

    Container.prototype.constructor = Container;

    Container.prototype.onadditem = null;
    Container.prototype.onfreeitem = null;
    Container.prototype.onchange = null;

    Container.prototype.addItems = function(elmt) {

        var that = this,
        mtx = this.getMtx().inverse();

        JSYG.makeArray(arguments).forEach(function(elmt) {

            new JSYG(elmt).each(function() {

                var $this = new JSYG(this);

                try { $this.addMtx(mtx); } //éléments non tracés
                catch(e){}

                $this.appendTo(that[0]);

                that.trigger('additem',that[0],this);
                that.trigger('change');

            });

        });

        return this;
    };

    Container.prototype.applyTransform = function() {

        var mtx = this.getMtx(),
        that = this;

        this.children().each(function() {
            var $this = new JSYG(this);
            $this.setMtx( mtx.multiply($this.getMtx(that)) );
        });

        this.resetTransf();

        return this;
    };

    Container.prototype.freeItems = function(elmt) {

        var parent = this.parent()[0],
        mtx = this.getMtx(),
        that = this,
        args = JSYG.makeArray( arguments.length === 0 ? this.children() : arguments);

        args.forEach(function(elmt) {

            new JSYG(elmt).each(function() {

                var $this = new JSYG(this);

                if (!$this.isChildOf(that)) return;

                try { $this.setMtx( mtx.multiply($this.getMtx(that)) ); }
                catch(e) {}

                $this.appendTo(parent);

                that.trigger('freeitem',that[0],this);
                that.trigger('change');

            });
        });

        return this;
    };

    JSYG.Container = Container;

    return Container;

});<|endoftext|>


<commit_before>var cls = window.cls || ( window.cls = {} );
// this should go in a own file

/**
  * @constructor
  * @extends BaseActions
  */

cls.DOMInspectorActions = function(id)
{
  var self = this;

  this.__active_container = null;
  this.__target = null;
  this.view_id = id;

  var nav_filters =
  {
    attr_text: function(ele)
    {
      switch(ele.nodeName)
      {
        case 'text':
        case 'key':
        case 'value':
        {
          return true;
        }
        case 'node':
        {
          return !/<\//.test(ele.textContent) && !ele.getElementsByTagName('key')[0];
        }
      }
      return false;
    }
  }

  this.editor = null;
  this.editors =
  {
    "dom-attr-text-editor": new DOMAttrAndTextEditor(nav_filters),
    "dom-markup-editor": new DOMMarkupEditor(nav_filters)
  };

  this.set_editor = function(type)
  {
    if( this.editor )
    {
      this.editor.submit()
    }
    if( !this.editor || this.editor.type != type )
    {
      this.editor = this.editors[type];
    }
  }

  /*
  this.getFirstTarget = function()
  {
    return self.__active_container && self.__active_container.getElementsByTagName('styles')[1].getElementsByTagName('property')[0];
  }

  this.clearSelected = function()
  {
    if( self.__target )
    {
      self.__target.removeClass('selected');
    }
  }
  */
  this.setSelected = function(new_target)
  {
    if(new_target)
    {
      self.__target = new_target;
      /*
      TODO set target here
      if( self.__target )
      {
        self.__target.removeClass('selected');
      }
      ( self.__target = new_target ).addClass('selected');
      self.__target.scrollSoftIntoView();
      */
    }
  }
  /*
  this.resetTarget = function()
  {
    if( self.__active_container && self.__target && !self.__active_container.parentNode )
    {
      var new_container = document.getElementById(self.__active_container.id);
      if(new_container)
      {
        var
        targets = self.__active_container.getElementsByTagName(self.__target.nodeName),
        target = null,
        i = 0;
        for( ; ( target = targets[i] ) && target != self.__target; i++ );
        if( target && ( target = new_container.getElementsByTagName(self.__target.nodeName)[i] ) )
        {
          self.__active_container = new_container;
          self.setSelected(target);
        }
      }
    }
  }

  this.moveFocusUp = function(event, target)
  {
    if( self.__target )
    {
      self.setSelected( self.__target.getPreviousWithFilter( self.__active_container,
        self.__target.nodeName == 'header' && self.__target.parentElement.getAttribute('handler')
        ? nav_filter.header
        : nav_filter._default ) );
    }
    else
    {
      opera.postError('keyboard_handler: no target to move');
    }
  }

  var nav_filter =
  {
    _default: function(ele)
    {
      return ( ( ele.nodeName == 'property' && ele.parentElement.hasAttribute('rule-id') )
               || ele.nodeName == 'header'
               || ele.getAttribute('handler') == 'display-rule-in-stylesheet' );
    },
    header: function(ele)
    {
      return ele.nodeName == 'header';
    },
    property_editable: function(ele)
    {
      return ele.nodeName == 'property' && ele.parentElement.hasAttribute('rule-id');
    }
  }

  this.moveFocusDown = function(event, target)
  {
    if( self.__target )
    {
      self.setSelected( self.__target.getNextWithFilter( self.__active_container,
        self.__target.nodeName == 'header' && !self.__target.parentElement.getAttribute('handler')
        ? nav_filter.header
        : nav_filter._default ) );
    }
    else
    {
      opera.postError('keyboard_handler: no target to move');
    }
  }

  this.setActiveContainer = function(event, container)
  {
    self.__active_container = container;
    if ( !self.__target || !self.__target.parentElement )
    {
      self.__target = self.getFirstTarget()
    }
    if( self.__target && !self.__target.hasClass('selected') )
    {
      self.setSelected(self.__target);
    }

  }

  this.clearActiveContainer = function()
  {
    self.clearSelected();
  }

  {
    "dom-attr-text-editor": new DOMAttrAndTextEditor(),
    "dom-markup-editor":

  */
  this.editDOM = function(event, target)
  {
    switch(event.target.nodeName)
    {
      case 'span':
      {
        if(/^(?:key|value|text|node)$/.test(event.target.parentElement.nodeName) )
        {
          event.target.parentElement.releaseEvent('dblclick');
        }
        break;
      }
      case 'key':
      case 'value':
      case 'text':
      {
        event.preventDefault();
        event.stopPropagation();

        key_identifier.setModeEdit(self);
        self.setSelected(event.target.parentNode);
        self.set_editor("dom-attr-text-editor");
        self.editor.edit(event, event.target);

        break;
      }
      case 'node':
      {
        var new_target = event.target;
        if(/^<\//.test(new_target.textContent))
        {
          new_target = event.target.getPreviousWithFilter
            (event.target.parentNode.parentNode, self.makeFilterGetStartTag(event.target.textContent));
          if( !new_target )
          {
            opera.postError('failed getting start tag in this.editDOM in action_dom.js')
            return;
          }
        }
        event.preventDefault();
        event.stopPropagation();
        key_identifier.setModeEdit(self);
        self.setSelected(new_target.parentNode);
        self.set_editor("dom-markup-editor");
        self.editor.edit(event, new_target);
        /*
        if(event.target.parentElement.hasAttribute('rule-id'))
        {
          key_identifier.setModeEdit(self);
          self.setSelected(event.target);
          self.editor.edit(event);
        }
        */
        // execute property click action
        break;
      }
    }
  }

  /*
  this['css-toggle-category'] = function(event, target)
  {
    if(/header/.test(target.nodeName))
    {
      target = target.firstChild;
    }
    var cat = target.getAttribute('cat-id'), value = target.hasClass('unfolded');
    var cat_container = target.parentNode.parentNode;
    if( value )
    {
      target.removeClass('unfolded');
      cat_container.removeClass('unfolded');
      var styles = cat_container.getElementsByTagName('styles')[0];
      if( styles )
      {
        styles.innerHTML = "";
      }
    }
    else
    {
      target.addClass('unfolded');
      cat_container.addClass('unfolded');
    }
    self.setSelected(target.parentNode);
    elementStyle.setUnfoldedCat( cat , !value);
    settings['css-inspector'].set(cat, !value);
  }

  this['display-rule-in-stylesheet'] = function(event, target)
  {
    var index = parseInt(target.getAttribute('index'));
    var rt_id = target.getAttribute('rt-id');
    var rule_id = target.parentNode.getAttribute('rule-id');
    // stylesheets.getRulesWithSheetIndex will call this function again if data is not avaible
    // handleGetRulesWithIndex in stylesheets will
    // set for this reason __call_count on the event object
    var rules = stylesheets.getRulesWithSheetIndex(rt_id, index, arguments);
    if(rules)
    {
      self.setSelected(target);
      stylesheets.setSelectedSheet(rt_id, index, rules, rule_id);
      topCell.showView(views.stylesheets.id);
    }
  }

  this.target_enter = function(event, action_id)
  {
    if(this.__target)
    {
      this.__target.releaseEvent('click');
    }
  }

  this.nav_previous_edit_mode = function(event, action_id)
  {
    if( !this.editor.nav_previous(event, action_id) )
    {
      var new_target =
        this.__target.getPreviousWithFilter( this.__active_container, nav_filter.property_editable );
      if(new_target)
      {
        this.setSelected(new_target);
        this.editor.edit(null, this.__target);
        this.editor.focusLastToken();
      }
    }

    // to stop default action
    return false;
  }

  this.nav_next_edit_mode = function(event, action_id)
  {
    if( !this.editor.nav_next(event, action_id) )
    {
      var new_target =
        this.__target.getNextWithFilter( this.__active_container, nav_filter.property_editable );
      if(new_target)
      {
        this.setSelected(new_target);
        this.editor.edit(null, this.__target);
        this.editor.focusFirstToken();
      }
    }

    // to stop default action
    return false;
  }

  this.autocomplete = function(event, action_id)
  {
    this.editor.autocomplete(event, action_id);
    return false;
  }

  this.escape_edit_mode = function(event, action_id)
  {

    if(!this.editor.escape())
    {
      var cur_target = this.__target;
      this.moveFocusUp();
      cur_target.parentElement.removeChild(cur_target);
    }
    key_identifier.setModeDefault(self);

    return false;
  }

  this.blur_edit_mode = function()
  {
    this.escape_edit_mode();
    this.clearActiveContainer();
  }

  this.enter_edit_mode = function(event, action_id)
  {
    if( !this.editor.enter(event, action_id) )
    {
      key_identifier.setModeDefault(self);
      if( !this.__target.textContent )
      {
        var cur_target = this.__target;
        this.moveFocusUp();
        cur_target.parentElement.removeChild(cur_target);
      }
    }
    return false;
  }
  */

  this.enter_edit_mode = function(event, action_id)
  {
    if( this.editor.type == "dom-attr-text-editor" )
    {
      var navigation_target = this.editor.submit();
      if(!navigation_target)
      {
        // TODO get a valid navigation target
      }
      else
      {
        // TODO set the return value as navigation target
      }
      key_identifier.setModeDefault(self);
      return false;
    }
    else
    {

      return true;
    }

    /*
    if( !this.editor.enter(event, action_id) )
    {
      key_identifier.setModeDefault(self);
      if( !this.__target.textContent )
      {
        var cur_target = this.__target;
        this.moveFocusUp();
        cur_target.parentElement.removeChild(cur_target);
      }
    }
    return false;
    */
  }

  this.ctrl_enter_edit_mode = function(event, action_id)
  {
    if( this.editor.type == "dom-attr-text-editor" )
    {

      return false;
    }
    else
    {
      var navigation_target = this.editor.submit();
      if(!navigation_target)
      {
        // TODO get a valid navigation target
      }
      else
      {
        // TODO set the return value as navigation target
      }
      key_identifier.setModeDefault(self);
      return false;
    }
  }

  this.nav_next_edit_mode = function(event, action_id)
  {
    if( this.editor.type == "dom-attr-text-editor" )
    {
      if( !this.editor.nav_next(event, action_id) )
      {
        key_identifier.setModeDefault(self);
      }
      return false;
    }
    else
    {
      this.editor.nav_next(event, action_id);
    }
    return false;
  }

  this.nav_previous_edit_mode = function(event, action_id)
  {
    if( this.editor.type == "dom-attr-text-editor" )
    {
      if( !this.editor.nav_previous(event, action_id) )
      {
        key_identifier.setModeDefault(self);
      }
      return false;
    }
    else
    {
      return true
    }
  }

  this.escape_edit_mode = function(event, action_id)
  {
    var navigation_target = this.editor.cancel();
    if(!navigation_target)
    {
      // TODO get a valid navigation target
    }
    else
    {
      // TODO set the return value as navigation target
    }
    key_identifier.setModeDefault(self);
    return false;
  }

  this.edit_onclick = function(event)
  {
    if( this.editor )
    {
      if( this.editor.onclick(event) )
      {

      }
      else
      {
        key_identifier.setModeDefault(self);
      }
    }
  }

  this.makeFilterGetStartTag = function(endtag)
  {
    var start_tag = endtag.replace(/[\/>]/g, '');
    return function(node)
    {
      return node.nodeName == 'node' && node.textContent.indexOf(start_tag) ==  0;
    }
  }

  this.init(id);

  var onViewCreated = function(msg)
  {
    if(msg.id == "dom" )
    {
      // self.resetTarget();
    }
  }
  messages.addListener('view-created', onViewCreated)

};

cls.DOMInspectorActions.prototype = BaseActions;

new cls.DOMInspectorActions('dom'); // the view id

/**
  * @constructor
  * @extends BaseKeyhandler
  */

cls.DOMInspectorKeyhandler = function(id)
{

  var __actions = actions[id]

  /*
  this[this.NAV_LEFT] = this[this.NAV_UP] = __actions.moveFocusUp;

  this[this.NAV_RIGHT] = this[this.NAV_DOWN] = __actions.moveFocusDown;

  this[this.ENTER] = function(event, action_id)
  {
    __actions.target_enter(event, action_id);
  }

  this.focus = __actions.setActiveContainer;

  this.blur = __actions.clearActiveContainer;
  */

  this.init(id);
};

cls.DOMInspectorKeyhandler.prototype = BaseKeyhandler;

new cls.DOMInspectorKeyhandler('dom');

/**
  * @constructor
  * @extends BaseEditKeyhandler
  */

cls.DOMInspectorEditKeyhandler = function(id)
{

  var __actions = actions[id]

  /*
  this[this.NAV_UP] = this[this.NAV_DOWN] = function(event, action_id)
  {
    __actions.autocomplete(event, action_id);
  }

  this[this.NAV_NEXT] = function(event, action_id)
  {
    __actions.nav_next_edit_mode(event, action_id);
  }

  this[this.NAV_PREVIOUS] = function(event, action_id)
  {
    __actions.nav_previous_edit_mode(event, action_id);
  }

  this[this.ESCAPE] = function(event, action_id)
  {
    __actions.escape_edit_mode(event, action_id);
  }

  this[this.ENTER] = function(event, action_id)
  {
    __actions.enter_edit_mode(event, action_id);
  }

  this.focus = __actions.test;

  this.blur = function()
  {
    __actions.blur_edit_mode();
  }
  */

  this[this.NAV_PREVIOUS] = function(event, action_id)
  {
    return __actions.nav_previous_edit_mode(event, action_id);
  }

  this[this.NAV_NEXT] = function(event, action_id)
  {
    return __actions.nav_next_edit_mode(event, action_id);
  }

  this[this.ENTER] = function(event, action_id)
  {
    return __actions.enter_edit_mode(event, action_id);
  }

  this[this.CTRL_ENTER] = function(event, action_id)
  {
    return __actions.ctrl_enter_edit_mode(event, action_id);
  }

  this[this.ESCAPE] = function(event, action_id)
  {
    return __actions.escape_edit_mode(event, action_id);
  }

  this.blur = function(event)
  {
    if( __actions.editor )
    {
      __actions.editor.submit();
    }
  }

  this.onclick = function(event)
  {
    __actions.edit_onclick(event);
  }

  this.init(id);
};

cls.DOMInspectorEditKeyhandler.prototype = BaseEditKeyhandler;

new cls.DOMInspectorEditKeyhandler('dom');

eventHandlers.dblclick['edit-dom'] = actions['dom'].editDOM;<commit_msg>fix for DFL-285<commit_after>var cls = window.cls || ( window.cls = {} );
// this should go in a own file

/**
  * @constructor
  * @extends BaseActions
  */

cls.DOMInspectorActions = function(id)
{
  var self = this;

  this.__active_container = null;
  this.__target = null;
  this.view_id = id;

  var nav_filters =
  {
    attr_text: function(ele)
    {
      switch(ele.nodeName)
      {
        case 'text':
        case 'key':
        case 'value':
        {
          return true;
        }
        case 'node':
        {
          return !/<\//.test(ele.textContent) && !ele.getElementsByTagName('key')[0];
        }
      }
      return false;
    }
  }

  this.editor = null;
  this.editors =
  {
    "dom-attr-text-editor": new DOMAttrAndTextEditor(nav_filters),
    "dom-markup-editor": new DOMMarkupEditor(nav_filters)
  };

  this.set_editor = function(type)
  {
    if( this.editor )
    {
      this.editor.submit()
    }
    if( !this.editor || this.editor.type != type )
    {
      this.editor = this.editors[type];
    }
  }

  /*
  this.getFirstTarget = function()
  {
    return self.__active_container && self.__active_container.getElementsByTagName('styles')[1].getElementsByTagName('property')[0];
  }

  this.clearSelected = function()
  {
    if( self.__target )
    {
      self.__target.removeClass('selected');
    }
  }
  */
  this.setSelected = function(new_target)
  {
    if(new_target)
    {
      self.__target = new_target;
      /*
      TODO set target here
      if( self.__target )
      {
        self.__target.removeClass('selected');
      }
      ( self.__target = new_target ).addClass('selected');
      self.__target.scrollSoftIntoView();
      */
    }
  }
  /*
  this.resetTarget = function()
  {
    if( self.__active_container && self.__target && !self.__active_container.parentNode )
    {
      var new_container = document.getElementById(self.__active_container.id);
      if(new_container)
      {
        var
        targets = self.__active_container.getElementsByTagName(self.__target.nodeName),
        target = null,
        i = 0;
        for( ; ( target = targets[i] ) && target != self.__target; i++ );
        if( target && ( target = new_container.getElementsByTagName(self.__target.nodeName)[i] ) )
        {
          self.__active_container = new_container;
          self.setSelected(target);
        }
      }
    }
  }

  this.moveFocusUp = function(event, target)
  {
    if( self.__target )
    {
      self.setSelected( self.__target.getPreviousWithFilter( self.__active_container,
        self.__target.nodeName == 'header' && self.__target.parentElement.getAttribute('handler')
        ? nav_filter.header
        : nav_filter._default ) );
    }
    else
    {
      opera.postError('keyboard_handler: no target to move');
    }
  }

  var nav_filter =
  {
    _default: function(ele)
    {
      return ( ( ele.nodeName == 'property' && ele.parentElement.hasAttribute('rule-id') )
               || ele.nodeName == 'header'
               || ele.getAttribute('handler') == 'display-rule-in-stylesheet' );
    },
    header: function(ele)
    {
      return ele.nodeName == 'header';
    },
    property_editable: function(ele)
    {
      return ele.nodeName == 'property' && ele.parentElement.hasAttribute('rule-id');
    }
  }

  this.moveFocusDown = function(event, target)
  {
    if( self.__target )
    {
      self.setSelected( self.__target.getNextWithFilter( self.__active_container,
        self.__target.nodeName == 'header' && !self.__target.parentElement.getAttribute('handler')
        ? nav_filter.header
        : nav_filter._default ) );
    }
    else
    {
      opera.postError('keyboard_handler: no target to move');
    }
  }

  this.setActiveContainer = function(event, container)
  {
    self.__active_container = container;
    if ( !self.__target || !self.__target.parentElement )
    {
      self.__target = self.getFirstTarget()
    }
    if( self.__target && !self.__target.hasClass('selected') )
    {
      self.setSelected(self.__target);
    }

  }

  this.clearActiveContainer = function()
  {
    self.clearSelected();
  }

  {
    "dom-attr-text-editor": new DOMAttrAndTextEditor(),
    "dom-markup-editor":

  */
  this.editDOM = function(event, target)
  {
    switch(event.target.nodeName)
    {
      case 'span':
      {
        if(/^(?:key|value|text|node)$/.test(event.target.parentElement.nodeName) )
        {
          event.target.parentElement.releaseEvent('dblclick');
        }
        break;
      }
      case 'key':
      case 'value':
      case 'text':
      {
        event.preventDefault();
        event.stopPropagation();

        key_identifier.setModeEdit(self);
        self.setSelected(event.target.parentNode);
        self.set_editor("dom-attr-text-editor");
        self.editor.edit(event, event.target);

        break;
      }
      case 'node':
      {
        var new_target = event.target;
        if(/^<\//.test(new_target.textContent))
        {
          new_target = event.target.getPreviousWithFilter
            (event.target.parentNode.parentNode, self.makeFilterGetStartTag(event.target));
          if( !new_target )
          {
            opera.postError('failed getting start tag in this.editDOM in action_dom.js')
            return;
          }
        }
        event.preventDefault();
        event.stopPropagation();
        key_identifier.setModeEdit(self);
        self.setSelected(new_target.parentNode);
        self.set_editor("dom-markup-editor");
        self.editor.edit(event, new_target);
        /*
        if(event.target.parentElement.hasAttribute('rule-id'))
        {
          key_identifier.setModeEdit(self);
          self.setSelected(event.target);
          self.editor.edit(event);
        }
        */
        // execute property click action
        break;
      }
    }
  }

  /*
  this['css-toggle-category'] = function(event, target)
  {
    if(/header/.test(target.nodeName))
    {
      target = target.firstChild;
    }
    var cat = target.getAttribute('cat-id'), value = target.hasClass('unfolded');
    var cat_container = target.parentNode.parentNode;
    if( value )
    {
      target.removeClass('unfolded');
      cat_container.removeClass('unfolded');
      var styles = cat_container.getElementsByTagName('styles')[0];
      if( styles )
      {
        styles.innerHTML = "";
      }
    }
    else
    {
      target.addClass('unfolded');
      cat_container.addClass('unfolded');
    }
    self.setSelected(target.parentNode);
    elementStyle.setUnfoldedCat( cat , !value);
    settings['css-inspector'].set(cat, !value);
  }

  this['display-rule-in-stylesheet'] = function(event, target)
  {
    var index = parseInt(target.getAttribute('index'));
    var rt_id = target.getAttribute('rt-id');
    var rule_id = target.parentNode.getAttribute('rule-id');
    // stylesheets.getRulesWithSheetIndex will call this function again if data is not avaible
    // handleGetRulesWithIndex in stylesheets will
    // set for this reason __call_count on the event object
    var rules = stylesheets.getRulesWithSheetIndex(rt_id, index, arguments);
    if(rules)
    {
      self.setSelected(target);
      stylesheets.setSelectedSheet(rt_id, index, rules, rule_id);
      topCell.showView(views.stylesheets.id);
    }
  }

  this.target_enter = function(event, action_id)
  {
    if(this.__target)
    {
      this.__target.releaseEvent('click');
    }
  }

  this.nav_previous_edit_mode = function(event, action_id)
  {
    if( !this.editor.nav_previous(event, action_id) )
    {
      var new_target =
        this.__target.getPreviousWithFilter( this.__active_container, nav_filter.property_editable );
      if(new_target)
      {
        this.setSelected(new_target);
        this.editor.edit(null, this.__target);
        this.editor.focusLastToken();
      }
    }

    // to stop default action
    return false;
  }

  this.nav_next_edit_mode = function(event, action_id)
  {
    if( !this.editor.nav_next(event, action_id) )
    {
      var new_target =
        this.__target.getNextWithFilter( this.__active_container, nav_filter.property_editable );
      if(new_target)
      {
        this.setSelected(new_target);
        this.editor.edit(null, this.__target);
        this.editor.focusFirstToken();
      }
    }

    // to stop default action
    return false;
  }

  this.autocomplete = function(event, action_id)
  {
    this.editor.autocomplete(event, action_id);
    return false;
  }

  this.escape_edit_mode = function(event, action_id)
  {

    if(!this.editor.escape())
    {
      var cur_target = this.__target;
      this.moveFocusUp();
      cur_target.parentElement.removeChild(cur_target);
    }
    key_identifier.setModeDefault(self);

    return false;
  }

  this.blur_edit_mode = function()
  {
    this.escape_edit_mode();
    this.clearActiveContainer();
  }

  this.enter_edit_mode = function(event, action_id)
  {
    if( !this.editor.enter(event, action_id) )
    {
      key_identifier.setModeDefault(self);
      if( !this.__target.textContent )
      {
        var cur_target = this.__target;
        this.moveFocusUp();
        cur_target.parentElement.removeChild(cur_target);
      }
    }
    return false;
  }
  */

  this.enter_edit_mode = function(event, action_id)
  {
    if( this.editor.type == "dom-attr-text-editor" )
    {
      var navigation_target = this.editor.submit();
      if(!navigation_target)
      {
        // TODO get a valid navigation target
      }
      else
      {
        // TODO set the return value as navigation target
      }
      key_identifier.setModeDefault(self);
      return false;
    }
    else
    {

      return true;
    }

    /*
    if( !this.editor.enter(event, action_id) )
    {
      key_identifier.setModeDefault(self);
      if( !this.__target.textContent )
      {
        var cur_target = this.__target;
        this.moveFocusUp();
        cur_target.parentElement.removeChild(cur_target);
      }
    }
    return false;
    */
  }

  this.ctrl_enter_edit_mode = function(event, action_id)
  {
    if( this.editor.type == "dom-attr-text-editor" )
    {

      return false;
    }
    else
    {
      var navigation_target = this.editor.submit();
      if(!navigation_target)
      {
        // TODO get a valid navigation target
      }
      else
      {
        // TODO set the return value as navigation target
      }
      key_identifier.setModeDefault(self);
      return false;
    }
  }

  this.nav_next_edit_mode = function(event, action_id)
  {
    if( this.editor.type == "dom-attr-text-editor" )
    {
      if( !this.editor.nav_next(event, action_id) )
      {
        key_identifier.setModeDefault(self);
      }
      return false;
    }
    else
    {
      this.editor.nav_next(event, action_id);
    }
    return false;
  }

  this.nav_previous_edit_mode = function(event, action_id)
  {
    if( this.editor.type == "dom-attr-text-editor" )
    {
      if( !this.editor.nav_previous(event, action_id) )
      {
        key_identifier.setModeDefault(self);
      }
      return false;
    }
    else
    {
      return true
    }
  }

  this.escape_edit_mode = function(event, action_id)
  {
    var navigation_target = this.editor.cancel();
    if(!navigation_target)
    {
      // TODO get a valid navigation target
    }
    else
    {
      // TODO set the return value as navigation target
    }
    key_identifier.setModeDefault(self);
    return false;
  }

  this.edit_onclick = function(event)
  {
    if( this.editor )
    {
      if( this.editor.onclick(event) )
      {

      }
      else
      {
        key_identifier.setModeDefault(self);
      }
    }
  }

  this.makeFilterGetStartTag = function(start_node)
  {
    var start_tag = start_node.textContent.replace(/[\/>]/g, '');
    var margin_left = start_node.parentElement.style.marginLeft;
    return function(node)
    {
      return (
        node.nodeName == 'node'
        && node.textContent.indexOf(start_tag) ==  0
        && node.parentElement.style.marginLeft == margin_left
        );
    }
  }

  this.init(id);

  var onViewCreated = function(msg)
  {
    if(msg.id == "dom" )
    {
      // self.resetTarget();
    }
  }
  messages.addListener('view-created', onViewCreated)

};

cls.DOMInspectorActions.prototype = BaseActions;

new cls.DOMInspectorActions('dom'); // the view id

/**
  * @constructor
  * @extends BaseKeyhandler
  */

cls.DOMInspectorKeyhandler = function(id)
{

  var __actions = actions[id]

  /*
  this[this.NAV_LEFT] = this[this.NAV_UP] = __actions.moveFocusUp;

  this[this.NAV_RIGHT] = this[this.NAV_DOWN] = __actions.moveFocusDown;

  this[this.ENTER] = function(event, action_id)
  {
    __actions.target_enter(event, action_id);
  }

  this.focus = __actions.setActiveContainer;

  this.blur = __actions.clearActiveContainer;
  */

  this.init(id);
};

cls.DOMInspectorKeyhandler.prototype = BaseKeyhandler;

new cls.DOMInspectorKeyhandler('dom');

/**
  * @constructor
  * @extends BaseEditKeyhandler
  */

cls.DOMInspectorEditKeyhandler = function(id)
{

  var __actions = actions[id]

  /*
  this[this.NAV_UP] = this[this.NAV_DOWN] = function(event, action_id)
  {
    __actions.autocomplete(event, action_id);
  }

  this[this.NAV_NEXT] = function(event, action_id)
  {
    __actions.nav_next_edit_mode(event, action_id);
  }

  this[this.NAV_PREVIOUS] = function(event, action_id)
  {
    __actions.nav_previous_edit_mode(event, action_id);
  }

  this[this.ESCAPE] = function(event, action_id)
  {
    __actions.escape_edit_mode(event, action_id);
  }

  this[this.ENTER] = function(event, action_id)
  {
    __actions.enter_edit_mode(event, action_id);
  }

  this.focus = __actions.test;

  this.blur = function()
  {
    __actions.blur_edit_mode();
  }
  */

  this[this.NAV_PREVIOUS] = function(event, action_id)
  {
    return __actions.nav_previous_edit_mode(event, action_id);
  }

  this[this.NAV_NEXT] = function(event, action_id)
  {
    return __actions.nav_next_edit_mode(event, action_id);
  }

  this[this.ENTER] = function(event, action_id)
  {
    return __actions.enter_edit_mode(event, action_id);
  }

  this[this.CTRL_ENTER] = function(event, action_id)
  {
    return __actions.ctrl_enter_edit_mode(event, action_id);
  }

  this[this.ESCAPE] = function(event, action_id)
  {
    return __actions.escape_edit_mode(event, action_id);
  }

  this.blur = function(event)
  {
    if( __actions.editor )
    {
      __actions.editor.submit();
    }
  }

  this.onclick = function(event)
  {
    __actions.edit_onclick(event);
  }

  this.init(id);
};

cls.DOMInspectorEditKeyhandler.prototype = BaseEditKeyhandler;

new cls.DOMInspectorEditKeyhandler('dom');

eventHandlers.dblclick['edit-dom'] = actions['dom'].editDOM;<|endoftext|>


<commit_before>/**
 *                              _                _
 *                             | |              | |
 *    __ _ _ __  _ __   ___ ___| | ___ _ __ __ _| |_ ___  _ __
 *   / _` | '_ \| '_ \ / __/ _ \ |/ _ \ '__/ _` | __/ _ \| '__|
 *  | (_| | |_) | |_) | (_|  __/ |  __/ | | (_| | || (_) | |
 *   \__,_| .__/| .__/ \___\___|_|\___|_|  \__,_|\__\___/|_|
 *        | |   | |
 *        |_|   |_|
 *
 *
 * @overview
 * This is the controller file for the Directory View. The directory view loads data from
 * a flat file, and derives a Sectioned and Indexed (iOS) TableView displaying all contacts.
 * The Directory has two TableView Templates, one for standard contacts, the other to denote
 * that you have a marked the contact as a Bookmark (or Favorite). Also, the Directory View
 * can be filtered so that it only displays bookmarked or favorited contacts.
 *
 * This file is an override of the directory view for iOS and Android, as ListView is not supported for MobileWeb
 * so we are using the TableView component instead.
 *
 * @copyright
 * Copyright (c) 2014 by Appcelerator, Inc. All Rights Reserved.
 *
 * @license
 * Licensed under the terms of the Apache Public License
 * Please see the LICENSE included with this distribution for details.
 */

/**
 * Instantiate the local variables for this controller
 */
var _args = arguments[0] || {}, // Any passed in arguments will fall into this propert
	users = null,  // Array placeholder for all users
	indexes = [],  // Array placeholder for the TableView Index (used by iOS only)
	title = _args.title || L("Directory");

/**
 * Appcelerator Analytics Call
 */
Ti.Analytics.featureEvent(Ti.Platform.osname+"."+title+".viewed");

/**
 * Update the Window title if required (only used when we create the Bookmarks View)
 */
if(title){
	$.winTitle.text = title;
}

/**
 * Function to inialize the View, gathers data from the flat file and sets up the TableView
 */
function init(){

	/**
	 * Access the FileSystem Object to read in the information from a flat file (lib/userData/data.js)
	 * DOCS: http://docs.appcelerator.com/platform/latest/#!/api/Titanium.Filesystem
	 */
	var file = Ti.Filesystem.getFile(Ti.Filesystem.resourcesDirectory + "userData/data.json");

	/**
	 * Populate the users variable from the file this call returns an array
	 */
	users = JSON.parse(file.read().text).users;

	/**
	 * Sorts the `users` array by the lastName property of the user (leverages UnderscoreJS _.sortBy function)
	 */
	users = _.sortBy(users, function(user){
		return user.lastName;
	});

	/**
	 * IF the users array exists
	 */
	if(users) {

		/**
		 * Setup our Indexes and Sections Array for building out the TableView components
		 *
		 */
		indexes = [];
		var sections = [];

		/**
		 * Group the data by first letter of last name to make it easier to create
		 * sections. (leverages the UndrescoreJS _.groupBy function)
		 */
		var userGroups  = _.groupBy(users, function(item){
		 	return item.lastName.charAt(0);
		});

        /**
         * Iterate through each group created, and prepare the data for the TableView
         * (Leverages the UnderscoreJS _.each function)
         */
		_.each(userGroups, function(group){

			/**
			 * Take the group data that is passed into the function, and parse/transform
			 * it for use in the ListView templates as defined in the directory.xml file.
			 */
			var dataToAdd = preprocessForTableView(group);

			/**
			 * Check to make sure that there is data to add to the table,
			 * if not lets exit
			 */
			if(dataToAdd.length < 1) return;

			/**
			 * Lets take the first Character of the LastName and push it onto the index
			 * Array - this will be used to generate the indices for the TableView on IOS
			 */
			indexes.push({
				index: indexes.length,
				title: group[0].lastName.charAt(0)
			});

			/**
			 * Create the TableViewSection header view
			 * DOCS: http://docs.appcelerator.com/platform/latest/#!/api/Titanium.UI.ListSection-property-headerView
			 */

			 var sectionHeader = Ti.UI.createView({
			 	backgroundColor: "#ececec",
			 	width: Ti.UI.FIll,
			 	height: 30
			 });

			 /**
			  * Create and Add the Label to the TableView Section header view
			  */
			 var sectionLabel = Ti.UI.createLabel({
			 	text: group[0].lastName.charAt(0),
			 	left: 20,
			 	font:{
			 		fontSize: 20
			 	},
			 	color: "#666"
			 });
			 sectionHeader.add(sectionLabel);

			/**
			 * Create a new TableViewSection, and ADD the header view created above to it.
			 */
			 var section = Ti.UI.createTableViewSection({
				headerView: sectionHeader
			});

			/**
			 * Add Data to the TableViewSection
			 */
			_.each(dataToAdd, function(row){
				section.add(row);
			});

			/**
			 * Push the newly created TableViewSection onto the `sections` array. This will be used to populate
			 * the TableView
			 */
			sections.push(section);
		});

		/**
		 * Add the TableViewSections and data elements created above to the TableView
		 */
		$.tableView.data = sections;
	}
};

/**
 *	Convert an array of data from a JSON file into a format that can be added to the TableView
 *
 * 	@param {Object} Raw data elements from the JSON file.
 */
var preprocessForTableView = function(rawData) {

	/**
	 * Get the Bookmarks array from persistent storage in Ti.App.Properties
	 * DOCS: http://docs.appcelerator.com/platform/latest/#!/api/Titanium.App.Properties
	 */
	var bookmarks = Ti.App.Properties.getList("bookmarks", []);

	/**
	 * If we need to filter the view to only show bookmars, check to see if the `restrictBookmarks`
	 * flag has been passed in as an argument to the controller, and only show users that are bookmarked
	 */
	if(_args.restrictBookmarks) {

		/**
		 * redefines the collection to only have users that are currently listed as bookmarks (leverages
		 * 	the UnderscoreJS _.filter function )
		 */
		rawData = _.filter(rawData, function(item){

			/**
			 * each item (or user) that is referenced, we look to see if the user id is included in bookmarks array
			 * retrieved from persistent storage above
			 */
			return _.find(bookmarks, function(bookmark){
				return item.id === bookmark;
			});
		});
	}

	/**
	 * Using the rawData collection, we map data properties of the users in this array to an array that maps an array to properly
	 * display the data in the TableView based on the templates defined in directory.xml (levearges the _.map Function of UnderscoreJS)
	 */
	return _.map(rawData, function(item) {

		/**
		 * Need to check to see if this user item is a bookmark. If it is, we will use the `favoriteTemplate` in the TableView.
		 * (leverages the _.find function of UnderscoreJS)
		 */
		item.isBookmark = _.find(bookmarks, function(bookmark){
			return item.id === bookmark;
		});

		/**
		 * Create the new user object which is added to the Array that is returned by the _.map function.
		 */
		var row = Alloy.createController("directoryRow", item);

		return row.getView();
	});
};

/**
 * This function handles the click events for the rows in the TableView.
 * We want to capture the user property associated with the row, and pass
 * it into the profile View
 *
 * @param {Object} Event data passed to the function
 */
function onItemClick(e){

	/**
	 * Appcelerator Analytics Call
	 */
	Ti.Analytics.featureEvent(Ti.Platform.osname+"."+title+".contact.clicked");

	/**
	 * Open the profile view, and pass in the user data for this contact
	 * Adding displayHomeAsUp to the payload so the navigator knows this is a child window
	 */
	var payload=e.row.user;
	payload.displayHomeAsUp=true;
	Alloy.Globals.Navigator.open("profile", payload);
}

/**
 * Handles the Bookmark icon click event. Launches this same control as a child window, but limits the view
 * to only bookmarked items.
 *
 * @param {Object} Event data passed to the function
 */
var onBookmarkClick = function onClick (e){

	/**
	 * Appcelerator Analytics Call
	 */
	Ti.Analytics.featureEvent(Ti.Platform.osname+"."+title+".bookmarks.clicked");

	/**
	 * Open this same controller into a new page, pass the flag to restrict the list only to Bookmarked Contacts and force the title
	 */
	Alloy.Globals.Navigator.open("directory", {restrictBookmarks:true, title:L("Bookmarks"),displayHomeAsUp:true});
};

/*
 * Custom NavBar Buttons
 */
if(!_args.restrictBookmarks){

	var bookmarkBtn = Ti.UI.createLabel({
		text:"\uf02e",
		color: "#C41230",
		font:{
			fontFamily:"icomoon",
			fontSize:28
		}
	});
	bookmarkBtn.addEventListener("click", onBookmarkClick);
	$.wrapper.rightNavButton = bookmarkBtn;
}
else {

	/**
	 * In order to override the standard button style in the Navigation Bar, we will create our own
	 * view to use
	 */
	var backBtn = Ti.UI.createLabel({
		text:"\uf104 Back",
		color: "#C41230",
		font:{
			fontFamily:"icomoon",
			fontSize:20
		}
	});
	backBtn.addEventListener("click", function(e){
		Alloy.Globals.Navigator.navGroup.close($.wrapper);
	});
	$.wrapper.leftNavButton = backBtn;

}

/**
 * Initialize View
 */

init();

/**
 * Listen for the refresh event, and re-initialize
 */
Ti.App.addEventListener("refresh-data", function(e){

	/**
	 * Reset the TableView
	 */
	init();
});

<commit_msg>Comment updates<commit_after>/**
 *                              _                _
 *                             | |              | |
 *    __ _ _ __  _ __   ___ ___| | ___ _ __ __ _| |_ ___  _ __
 *   / _` | '_ \| '_ \ / __/ _ \ |/ _ \ '__/ _` | __/ _ \| '__|
 *  | (_| | |_) | |_) | (_|  __/ |  __/ | | (_| | || (_) | |
 *   \__,_| .__/| .__/ \___\___|_|\___|_|  \__,_|\__\___/|_|
 *        | |   | |
 *        |_|   |_|
 *
 *
 * @overview
 * This is the controller file for the Directory View. The directory view loads data from
 * a flat file, and derives a Sectioned and Indexed (iOS) TableView displaying all contacts.
 * The Directory has two TableView Templates, one for standard contacts, the other to denote
 * that you have a marked the contact as a Bookmark (or Favorite). Also, the Directory View
 * can be filtered so that it only displays bookmarked or favorited contacts.
 *
 * Note:
 * This file is an override of the directory view for iOS and Android, as ListView is not supported for MobileWeb
 * so we are using the TableView component instead.
 *
 * @copyright
 * Copyright (c) 2014 by Appcelerator, Inc. All Rights Reserved.
 *
 * @license
 * Licensed under the terms of the Apache Public License
 * Please see the LICENSE included with this distribution for details.
 */

/**
 * Instantiate the local variables for this controller
 */
var _args = arguments[0] || {}, // Any passed in arguments will fall into this propert
	users = null,  // Array placeholder for all users
	indexes = [],  // Array placeholder for the TableView Index (used by iOS only)
	title = _args.title || L("Directory");

/**
 * Appcelerator Analytics Call
 */
Ti.Analytics.featureEvent(Ti.Platform.osname+"."+title+".viewed");

/**
 * Update the Window title if required (only used when we create the Bookmarks View)
 */
if(title){
	$.winTitle.text = title;
}

/**
 * Function to inialize the View, gathers data from the flat file and sets up the TableView
 */
function init(){

	/**
	 * Access the FileSystem Object to read in the information from a flat file (lib/userData/data.js)
	 * DOCS: http://docs.appcelerator.com/platform/latest/#!/api/Titanium.Filesystem
	 */
	var file = Ti.Filesystem.getFile(Ti.Filesystem.resourcesDirectory + "userData/data.json");

	/**
	 * Populate the users variable from the file this call returns an array
	 */
	users = JSON.parse(file.read().text).users;

	/**
	 * Sorts the `users` array by the lastName property of the user (leverages UnderscoreJS _.sortBy function)
	 */
	users = _.sortBy(users, function(user){
		return user.lastName;
	});

	/**
	 * IF the users array exists
	 */
	if(users) {

		/**
		 * Setup our Indexes and Sections Array for building out the TableView components
		 *
		 */
		indexes = [];
		var sections = [];

		/**
		 * Group the data by first letter of last name to make it easier to create
		 * sections. (leverages the UndrescoreJS _.groupBy function)
		 */
		var userGroups  = _.groupBy(users, function(item){
		 	return item.lastName.charAt(0);
		});

        /**
         * Iterate through each group created, and prepare the data for the TableView
         * (Leverages the UnderscoreJS _.each function)
         */
		_.each(userGroups, function(group){

			/**
			 * Take the group data that is passed into the function, and parse/transform
			 * it for use in the ListView templates as defined in the directory.xml file.
			 */
			var dataToAdd = preprocessForTableView(group);

			/**
			 * Check to make sure that there is data to add to the table,
			 * if not lets exit
			 */
			if(dataToAdd.length < 1) return;

			/**
			 * Lets take the first Character of the LastName and push it onto the index
			 * Array - this will be used to generate the indices for the TableView on IOS
			 */
			indexes.push({
				index: indexes.length,
				title: group[0].lastName.charAt(0)
			});

			/**
			 * Create the TableViewSection header view
			 * DOCS: http://docs.appcelerator.com/platform/latest/#!/api/Titanium.UI.ListSection-property-headerView
			 */

			 var sectionHeader = Ti.UI.createView({
			 	backgroundColor: "#ececec",
			 	width: Ti.UI.FIll,
			 	height: 30
			 });

			 /**
			  * Create and Add the Label to the TableView Section header view
			  */
			 var sectionLabel = Ti.UI.createLabel({
			 	text: group[0].lastName.charAt(0),
			 	left: 20,
			 	font:{
			 		fontSize: 20
			 	},
			 	color: "#666"
			 });
			 sectionHeader.add(sectionLabel);

			/**
			 * Create a new TableViewSection, and ADD the header view created above to it.
			 */
			 var section = Ti.UI.createTableViewSection({
				headerView: sectionHeader
			});

			/**
			 * Add Data to the TableViewSection
			 */
			_.each(dataToAdd, function(row){
				section.add(row);
			});

			/**
			 * Push the newly created TableViewSection onto the `sections` array. This will be used to populate
			 * the TableView
			 */
			sections.push(section);
		});

		/**
		 * Add the TableViewSections and data elements created above to the TableView
		 */
		$.tableView.data = sections;
	}
};

/**
 *	Convert an array of data from a JSON file into a format that can be added to the TableView
 *
 * 	@param {Object} Raw data elements from the JSON file.
 */
var preprocessForTableView = function(rawData) {

	/**
	 * Get the Bookmarks array from persistent storage in Ti.App.Properties
	 * DOCS: http://docs.appcelerator.com/platform/latest/#!/api/Titanium.App.Properties
	 */
	var bookmarks = Ti.App.Properties.getList("bookmarks", []);

	/**
	 * If we need to filter the view to only show bookmars, check to see if the `restrictBookmarks`
	 * flag has been passed in as an argument to the controller, and only show users that are bookmarked
	 */
	if(_args.restrictBookmarks) {

		/**
		 * redefines the collection to only have users that are currently listed as bookmarks (leverages
		 * 	the UnderscoreJS _.filter function )
		 */
		rawData = _.filter(rawData, function(item){

			/**
			 * each item (or user) that is referenced, we look to see if the user id is included in bookmarks array
			 * retrieved from persistent storage above
			 */
			return _.find(bookmarks, function(bookmark){
				return item.id === bookmark;
			});
		});
	}

	/**
	 * Using the rawData collection, we map data properties of the users in this array to an array that maps an array to properly
	 * display the data in the TableView based on the templates defined in directory.xml (levearges the _.map Function of UnderscoreJS)
	 */
	return _.map(rawData, function(item) {

		/**
		 * Need to check to see if this user item is a bookmark. If it is, we will use the `favoriteTemplate` in the TableView.
		 * (leverages the _.find function of UnderscoreJS)
		 */
		item.isBookmark = _.find(bookmarks, function(bookmark){
			return item.id === bookmark;
		});

		/**
		 * Create the new user object which is added to the Array that is returned by the _.map function.
		 */
		var row = Alloy.createController("directoryRow", item);

		return row.getView();
	});
};

/**
 * This function handles the click events for the rows in the TableView.
 * We want to capture the user property associated with the row, and pass
 * it into the profile View
 *
 * @param {Object} Event data passed to the function
 */
function onItemClick(e){

	/**
	 * Appcelerator Analytics Call
	 */
	Ti.Analytics.featureEvent(Ti.Platform.osname+"."+title+".contact.clicked");

	/**
	 * Open the profile view, and pass in the user data for this contact
	 * Adding displayHomeAsUp to the payload so the navigator knows this is a child window
	 */
	var payload=e.row.user;
	payload.displayHomeAsUp=true;
	Alloy.Globals.Navigator.open("profile", payload);
}

/**
 * Handles the Bookmark icon click event. Launches this same control as a child window, but limits the view
 * to only bookmarked items.
 *
 * @param {Object} Event data passed to the function
 */
var onBookmarkClick = function onClick (e){

	/**
	 * Appcelerator Analytics Call
	 */
	Ti.Analytics.featureEvent(Ti.Platform.osname+"."+title+".bookmarks.clicked");

	/**
	 * Open this same controller into a new page, pass the flag to restrict the list only to Bookmarked Contacts and force the title
	 */
	Alloy.Globals.Navigator.open("directory", {restrictBookmarks:true, title:L("Bookmarks"),displayHomeAsUp:true});
};

/*
 * Custom NavBar Buttons
 */
if(!_args.restrictBookmarks){

	var bookmarkBtn = Ti.UI.createLabel({
		text:"\uf02e",
		color: "#C41230",
		font:{
			fontFamily:"icomoon",
			fontSize:28
		}
	});
	bookmarkBtn.addEventListener("click", onBookmarkClick);
	$.wrapper.rightNavButton = bookmarkBtn;
}
else {

	/**
	 * In order to override the standard button style in the Navigation Bar, we will create our own
	 * view to use
	 */
	var backBtn = Ti.UI.createLabel({
		text:"\uf104 Back",
		color: "#C41230",
		font:{
			fontFamily:"icomoon",
			fontSize:20
		}
	});
	backBtn.addEventListener("click", function(e){
		Alloy.Globals.Navigator.navGroup.close($.wrapper);
	});
	$.wrapper.leftNavButton = backBtn;

}

/**
 * Initialize View
 */

init();

/**
 * Listen for the refresh event, and re-initialize
 */
Ti.App.addEventListener("refresh-data", function(e){

	/**
	 * Reset the TableView
	 */
	init();
});

<|endoftext|>


<commit_before>/**
 * ButtonElement is often mixed into other classes to generate a button, which is a clickable
 * interface element that can be configured with access keys for accessibility.
 * See the [OOjs UI documentation on MediaWiki] [1] for examples.
 *
 * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Buttons_and_Switches#Buttons
 * @abstract
 * @class
 *
 * @constructor
 * @param {Object} [config] Configuration options
 * @cfg {jQuery} [$button] Button node, assigned to #$button, omit to use a generated `<a>`
 * @cfg {boolean} [framed=true] Render button with a frame
 * @cfg {string} [accessKey] Button's access key
 */
OO.ui.ButtonElement = function OoUiButtonElement( config ) {
	// Configuration initialization
	config = config || {};

	// Properties
	this.$button = null;
	this.framed = null;
	this.accessKey = null;
	this.active = false;
	this.onMouseUpHandler = this.onMouseUp.bind( this );
	this.onMouseDownHandler = this.onMouseDown.bind( this );
	this.onKeyDownHandler = this.onKeyDown.bind( this );
	this.onKeyUpHandler = this.onKeyUp.bind( this );
	this.onClickHandler = this.onClick.bind( this );
	this.onKeyPressHandler = this.onKeyPress.bind( this );

	// Initialization
	this.$element.addClass( 'oo-ui-buttonElement' );
	this.toggleFramed( config.framed === undefined || config.framed );
	this.setAccessKey( config.accessKey );
	this.setButtonElement( config.$button || $( '<a>' ) );
};

/* Setup */

OO.initClass( OO.ui.ButtonElement );

/* Static Properties */

/**
 * Cancel mouse down events.
 *
 * @static
 * @inheritable
 * @property {boolean}
 */
OO.ui.ButtonElement.static.cancelButtonMouseDownEvents = true;

/* Events */

/**
 * @event click
 */

/* Methods */

/**
 * Set the button element.
 *
 * If an element is already set, it will be cleaned up before setting up the new element.
 *
 * @param {jQuery} $button Element to use as button
 */
OO.ui.ButtonElement.prototype.setButtonElement = function ( $button ) {
	if ( this.$button ) {
		this.$button
			.removeClass( 'oo-ui-buttonElement-button' )
			.removeAttr( 'role accesskey' )
			.off( {
				mousedown: this.onMouseDownHandler,
				keydown: this.onKeyDownHandler,
				click: this.onClickHandler,
				keypress: this.onKeyPressHandler
			} );
	}

	this.$button = $button
		.addClass( 'oo-ui-buttonElement-button' )
		.attr( { role: 'button', accesskey: this.accessKey } )
		.on( {
			mousedown: this.onMouseDownHandler,
			keydown: this.onKeyDownHandler,
			click: this.onClickHandler,
			keypress: this.onKeyPressHandler
		} );
};

/**
 * Handles mouse down events.
 *
 * @protected
 * @param {jQuery.Event} e Mouse down event
 */
OO.ui.ButtonElement.prototype.onMouseDown = function ( e ) {
	if ( this.isDisabled() || e.which !== 1 ) {
		return;
	}
	this.$element.addClass( 'oo-ui-buttonElement-pressed' );
	// Run the mouseup handler no matter where the mouse is when the button is let go, so we can
	// reliably remove the pressed class
	this.getElementDocument().addEventListener( 'mouseup', this.onMouseUpHandler, true );
	// Prevent change of focus unless specifically configured otherwise
	if ( this.constructor.static.cancelButtonMouseDownEvents ) {
		return false;
	}
};

/**
 * Handles mouse up events.
 *
 * @protected
 * @param {jQuery.Event} e Mouse up event
 */
OO.ui.ButtonElement.prototype.onMouseUp = function ( e ) {
	if ( this.isDisabled() || e.which !== 1 ) {
		return;
	}
	this.$element.removeClass( 'oo-ui-buttonElement-pressed' );
	// Stop listening for mouseup, since we only needed this once
	this.getElementDocument().removeEventListener( 'mouseup', this.onMouseUpHandler, true );
};

/**
 * Handles mouse click events.
 *
 * @protected
 * @param {jQuery.Event} e Mouse click event
 * @fires click
 */
OO.ui.ButtonElement.prototype.onClick = function ( e ) {
	if ( !this.isDisabled() && e.which === 1 ) {
		this.emit( 'click' );
	}
	return false;
};

/**
 * Handles key down events.
 *
 * @protected
 * @param {jQuery.Event} e Key down event
 */
OO.ui.ButtonElement.prototype.onKeyDown = function ( e ) {
	if ( this.isDisabled() || ( e.which !== OO.ui.Keys.SPACE && e.which !== OO.ui.Keys.ENTER ) ) {
		return;
	}
	this.$element.addClass( 'oo-ui-buttonElement-pressed' );
	// Run the keyup handler no matter where the key is when the button is let go, so we can
	// reliably remove the pressed class
	this.getElementDocument().addEventListener( 'keyup', this.onKeyUpHandler, true );
};

/**
 * Handles key up events.
 *
 * @protected
 * @param {jQuery.Event} e Key up event
 */
OO.ui.ButtonElement.prototype.onKeyUp = function ( e ) {
	if ( this.isDisabled() || ( e.which !== OO.ui.Keys.SPACE && e.which !== OO.ui.Keys.ENTER ) ) {
		return;
	}
	this.$element.removeClass( 'oo-ui-buttonElement-pressed' );
	// Stop listening for keyup, since we only needed this once
	this.getElementDocument().removeEventListener( 'keyup', this.onKeyUpHandler, true );
};

/**
 * Handles key press events.
 *
 * @protected
 * @param {jQuery.Event} e Key press event
 * @fires click
 */
OO.ui.ButtonElement.prototype.onKeyPress = function ( e ) {
	if ( !this.isDisabled() && ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER ) ) {
		this.emit( 'click' );
		return false;
	}
};

/**
 * Check if button has a frame.
 *
 * @return {boolean} Button is framed
 */
OO.ui.ButtonElement.prototype.isFramed = function () {
	return this.framed;
};

/**
 * Toggle frame.
 *
 * @param {boolean} [framed] Make button framed, omit to toggle
 * @chainable
 */
OO.ui.ButtonElement.prototype.toggleFramed = function ( framed ) {
	framed = framed === undefined ? !this.framed : !!framed;
	if ( framed !== this.framed ) {
		this.framed = framed;
		this.$element
			.toggleClass( 'oo-ui-buttonElement-frameless', !framed )
			.toggleClass( 'oo-ui-buttonElement-framed', framed );
		this.updateThemeClasses();
	}

	return this;
};

/**
 * Set access key.
 *
 * @param {string} accessKey Button's access key, use empty string to remove
 * @chainable
 */
OO.ui.ButtonElement.prototype.setAccessKey = function ( accessKey ) {
	accessKey = typeof accessKey === 'string' && accessKey.length ? accessKey : null;

	if ( this.accessKey !== accessKey ) {
		if ( this.$button ) {
			if ( accessKey !== null ) {
				this.$button.attr( 'accesskey', accessKey );
			} else {
				this.$button.removeAttr( 'accesskey' );
			}
		}
		this.accessKey = accessKey;
	}

	return this;
};

/**
 * Set active state.
 *
 * @param {boolean} [value] Make button active
 * @chainable
 */
OO.ui.ButtonElement.prototype.setActive = function ( value ) {
	this.$element.toggleClass( 'oo-ui-buttonElement-active', !!value );
	return this;
};
<commit_msg>ButtonElement: Clarify description<commit_after>/**
 * ButtonElement is often mixed into other classes to generate a button, which is a clickable
 * interface element that can be configured with access keys for accessibility.
 * See the [OOjs UI documentation on MediaWiki] [1] for examples.
 *
 * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Buttons_and_Switches#Buttons
 * @abstract
 * @class
 *
 * @constructor
 * @param {Object} [config] Configuration options
 * @cfg {jQuery} [$button] The button element created by the class.
 *  If this configuration is omitted, the button element will use a generated `<a>`.
 * @cfg {boolean} [framed=true] Render the button with a frame
 * @cfg {string} [accessKey] Button's access key
 */
OO.ui.ButtonElement = function OoUiButtonElement( config ) {
	// Configuration initialization
	config = config || {};

	// Properties
	this.$button = null;
	this.framed = null;
	this.accessKey = null;
	this.active = false;
	this.onMouseUpHandler = this.onMouseUp.bind( this );
	this.onMouseDownHandler = this.onMouseDown.bind( this );
	this.onKeyDownHandler = this.onKeyDown.bind( this );
	this.onKeyUpHandler = this.onKeyUp.bind( this );
	this.onClickHandler = this.onClick.bind( this );
	this.onKeyPressHandler = this.onKeyPress.bind( this );

	// Initialization
	this.$element.addClass( 'oo-ui-buttonElement' );
	this.toggleFramed( config.framed === undefined || config.framed );
	this.setAccessKey( config.accessKey );
	this.setButtonElement( config.$button || $( '<a>' ) );
};

/* Setup */

OO.initClass( OO.ui.ButtonElement );

/* Static Properties */

/**
 * Cancel mouse down events.
 *
 * This property is usually set to `true` to prevent the focus from changing when the button is clicked.
 * Classes such as {@link OO.ui.DraggableElement DraggableElement} and {@link OO.ui.ButtonOptionWidget ButtonOptionWidget}
 * use a value of `false` so that dragging behavior is possible and mousedown events can be handled by a
 * parent widget.
 *
 * @static
 * @inheritable
 * @property {boolean}
 */
OO.ui.ButtonElement.static.cancelButtonMouseDownEvents = true;

/* Events */

/**
 * A 'click' event is emitted when the button element is clicked.
 *
 * @event click
 */

/* Methods */

/**
 * Set the button element.
 *
 * This method is used to retarget a button mixin so that its functionality applies to
 * the specified button element instead of the one created by the class. If a button element
 * is already set, the method will remove the mixin’s effect on that element.
 *
 * @param {jQuery} $button Element to use as button
 */
OO.ui.ButtonElement.prototype.setButtonElement = function ( $button ) {
	if ( this.$button ) {
		this.$button
			.removeClass( 'oo-ui-buttonElement-button' )
			.removeAttr( 'role accesskey' )
			.off( {
				mousedown: this.onMouseDownHandler,
				keydown: this.onKeyDownHandler,
				click: this.onClickHandler,
				keypress: this.onKeyPressHandler
			} );
	}

	this.$button = $button
		.addClass( 'oo-ui-buttonElement-button' )
		.attr( { role: 'button', accesskey: this.accessKey } )
		.on( {
			mousedown: this.onMouseDownHandler,
			keydown: this.onKeyDownHandler,
			click: this.onClickHandler,
			keypress: this.onKeyPressHandler
		} );
};

/**
 * Handles mouse down events.
 *
 * @protected
 * @param {jQuery.Event} e Mouse down event
 */
OO.ui.ButtonElement.prototype.onMouseDown = function ( e ) {
	if ( this.isDisabled() || e.which !== 1 ) {
		return;
	}
	this.$element.addClass( 'oo-ui-buttonElement-pressed' );
	// Run the mouseup handler no matter where the mouse is when the button is let go, so we can
	// reliably remove the pressed class
	this.getElementDocument().addEventListener( 'mouseup', this.onMouseUpHandler, true );
	// Prevent change of focus unless specifically configured otherwise
	if ( this.constructor.static.cancelButtonMouseDownEvents ) {
		return false;
	}
};

/**
 * Handles mouse up events.
 *
 * @protected
 * @param {jQuery.Event} e Mouse up event
 */
OO.ui.ButtonElement.prototype.onMouseUp = function ( e ) {
	if ( this.isDisabled() || e.which !== 1 ) {
		return;
	}
	this.$element.removeClass( 'oo-ui-buttonElement-pressed' );
	// Stop listening for mouseup, since we only needed this once
	this.getElementDocument().removeEventListener( 'mouseup', this.onMouseUpHandler, true );
};

/**
 * Handles mouse click events.
 *
 * @protected
 * @param {jQuery.Event} e Mouse click event
 * @fires click
 */
OO.ui.ButtonElement.prototype.onClick = function ( e ) {
	if ( !this.isDisabled() && e.which === 1 ) {
		this.emit( 'click' );
	}
	return false;
};

/**
 * Handles key down events.
 *
 * @protected
 * @param {jQuery.Event} e Key down event
 */
OO.ui.ButtonElement.prototype.onKeyDown = function ( e ) {
	if ( this.isDisabled() || ( e.which !== OO.ui.Keys.SPACE && e.which !== OO.ui.Keys.ENTER ) ) {
		return;
	}
	this.$element.addClass( 'oo-ui-buttonElement-pressed' );
	// Run the keyup handler no matter where the key is when the button is let go, so we can
	// reliably remove the pressed class
	this.getElementDocument().addEventListener( 'keyup', this.onKeyUpHandler, true );
};

/**
 * Handles key up events.
 *
 * @protected
 * @param {jQuery.Event} e Key up event
 */
OO.ui.ButtonElement.prototype.onKeyUp = function ( e ) {
	if ( this.isDisabled() || ( e.which !== OO.ui.Keys.SPACE && e.which !== OO.ui.Keys.ENTER ) ) {
		return;
	}
	this.$element.removeClass( 'oo-ui-buttonElement-pressed' );
	// Stop listening for keyup, since we only needed this once
	this.getElementDocument().removeEventListener( 'keyup', this.onKeyUpHandler, true );
};

/**
 * Handles key press events.
 *
 * @protected
 * @param {jQuery.Event} e Key press event
 * @fires click
 */
OO.ui.ButtonElement.prototype.onKeyPress = function ( e ) {
	if ( !this.isDisabled() && ( e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER ) ) {
		this.emit( 'click' );
		return false;
	}
};

/**
 * Check if button has a frame.
 *
 * @return {boolean} Button is framed
 */
OO.ui.ButtonElement.prototype.isFramed = function () {
	return this.framed;
};

/**
 * Render the button with or without a frame. Omit the `framed` parameter to toggle the button frame on and off.
 *
 * @param {boolean} [framed] Make button framed, omit to toggle
 * @chainable
 */
OO.ui.ButtonElement.prototype.toggleFramed = function ( framed ) {
	framed = framed === undefined ? !this.framed : !!framed;
	if ( framed !== this.framed ) {
		this.framed = framed;
		this.$element
			.toggleClass( 'oo-ui-buttonElement-frameless', !framed )
			.toggleClass( 'oo-ui-buttonElement-framed', framed );
		this.updateThemeClasses();
	}

	return this;
};

/**
 * Set the button's access key.
 *
 * @param {string} accessKey Button's access key, use empty string to remove
 * @chainable
 */
OO.ui.ButtonElement.prototype.setAccessKey = function ( accessKey ) {
	accessKey = typeof accessKey === 'string' && accessKey.length ? accessKey : null;

	if ( this.accessKey !== accessKey ) {
		if ( this.$button ) {
			if ( accessKey !== null ) {
				this.$button.attr( 'accesskey', accessKey );
			} else {
				this.$button.removeAttr( 'accesskey' );
			}
		}
		this.accessKey = accessKey;
	}

	return this;
};

/**
 * Set the button to its 'active' state.
 *
 * The active state occurs when a {@link OO.ui.ButtonOptionWidget ButtonOptionWidget} or
 * a {@link OO.ui.ToggleButtonWidget ToggleButtonWidget} is pressed. This method does nothing
 * for other button types.
 *
 * @param {boolean} [value] Make button active
 * @chainable
 */
OO.ui.ButtonElement.prototype.setActive = function ( value ) {
	this.$element.toggleClass( 'oo-ui-buttonElement-active', !!value );
	return this;
};
<|endoftext|>


<commit_before>module.exports = function() {
  return function(query) {
    return Object
      .keys(query)
      .reduce(function(queryStr, key) {
        const value = query[key];
        const part = key + '=' + encodeURIComponent(value);

        return queryStr.length ? queryStr + '&' + part : part;
      }, '');
  };
};
<commit_msg>fix(IE9): use var instead of const<commit_after>module.exports = function() {
  return function(query) {
    return Object
      .keys(query)
      .reduce(function(queryStr, key) {
        var value = query[key];
        var part = key + '=' + encodeURIComponent(value);

        return queryStr.length ? queryStr + '&' + part : part;
      }, '');
  };
};
<|endoftext|>