<commit_before>const electron = require('electron')
const fs = require('fs')
const path = require('path')
const app = electron.app // Module to control application life.
const BrowserWindow = electron.BrowserWindow // Module to create native browser window.

var userDataPath = app.getPath('userData')

const browserPage = 'file://' + __dirname + '/index.html'

var mainWindow = null
var isFocusMode = false
var appIsReady = false

var saveWindowBounds = function () {
  if (mainWindow) {
    fs.writeFile(path.join(userDataPath, 'windowBounds.json'), JSON.stringify(mainWindow.getBounds()))
  }
}

function sendIPCToWindow (window, action, data) {
  // if there are no windows, create a new one
  if (!mainWindow) {
    createWindow(function () {
      mainWindow.webContents.send(action, data || {})
    })
  } else {
    mainWindow.webContents.send(action, data || {})
  }
}

function openTabInWindow (url) {
  sendIPCToWindow(mainWindow, 'addTab', {
    url: url
  })
}

function createWindow (cb) {
  var savedBounds = fs.readFile(path.join(userDataPath, 'windowBounds.json'), 'utf-8', function (e, data) {
    if (e || !data) { // there was an error, probably because the file doesn't exist
      var size = electron.screen.getPrimaryDisplay().workAreaSize
      var bounds = {
        x: 0,
        y: 0,
        width: size.width,
        height: size.height
      }
    } else {
      var bounds = JSON.parse(data)
    }

// maximizes the window frame in windows 10
// fixes https://github.com/minbrowser/min/issues/214
// should be removed once https://github.com/electron/electron/issues/4045 is fixed
    if (process.platform === 'win32') {
      if ((bounds.x === 0 && bounds.y === 0) || (bounds.x === -8 && bounds.y === -8)) {
        var screenSize = electron.screen.getPrimaryDisplay().workAreaSize
        if ((screenSize.width === bounds.width || bounds.width - screenSize.width === 16) && (screenSize.height === bounds.height || bounds.height - screenSize.height === 16)) {
          var shouldMaximize = true
        }
      }
    }

    createWindowWithBounds(bounds, shouldMaximize)

    if (cb) {
      cb()
    }
  })
}

function createWindowWithBounds (bounds, shouldMaximize) {
  mainWindow = new BrowserWindow({
    width: bounds.width,
    height: bounds.height,
    x: bounds.x,
    y: bounds.y,
    minWidth: 320,
    minHeight: 500,
    titleBarStyle: 'hidden-inset',
    icon: __dirname + '/icons/icon256.png'
  })

  // and load the index.html of the app.
  mainWindow.loadURL(browserPage)

  if (shouldMaximize) {
    mainWindow.maximize()
  }

  // save the window size for the next launch of the app
  mainWindow.on('close', function () {
    saveWindowBounds()
  })

  // Emitted when the window is closed.
  mainWindow.on('closed', function () {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null
  })

  /* handle pdf downloads - ipc recieved in fileDownloadManager.js */

  mainWindow.webContents.session.on('will-download', function (event, item, webContents) {
    var itemURL = item.getURL()
    if (item.getMimeType() === 'application/pdf' && itemURL.indexOf('blob:') !== 0 && itemURL.indexOf('#pdfjs.action=download') === -1) { // clicking the download button in the viewer opens a blob url, so we don't want to open those in the viewer (since that would make it impossible to download a PDF)
      event.preventDefault()
      sendIPCToWindow(mainWindow, 'openPDF', {
        url: itemURL,
        webContentsId: webContents.getId(),
        event: event,
        item: item // as of electron 0.35.1, this is an empty object
      })
    }
    return true
  })

  mainWindow.on('enter-full-screen', function () {
    sendIPCToWindow(mainWindow, 'enter-full-screen')
  })

  mainWindow.on('leave-full-screen', function () {
    sendIPCToWindow(mainWindow, 'leave-full-screen')
  })

  mainWindow.on('app-command', function (e, command) {
    if (command === 'browser-backward') {
      sendIPCToWindow(mainWindow, 'goBack')
    } else if (command === 'browser-forward') {
      sendIPCToWindow(mainWindow, 'goForward')
    }
  })

  // prevent remote pages from being loaded using drag-and-drop, since they would have node access
  mainWindow.webContents.on('will-navigate', function (e, url) {
    if (url !== browserPage) {
      e.preventDefault()
    }
  })

  registerFiltering() // register filtering for the default session

  return mainWindow
}

// Quit when all windows are closed.
app.on('window-all-closed', function () {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on('ready', function () {
  appIsReady = true

  createWindow(function () {
    mainWindow.webContents.on('did-finish-load', function () {
      // if a URL was passed as a command line argument (probably because Min is set as the default browser on Linux), open it.
      if (process.argv && process.argv[1] && process.argv[1].toLowerCase() !== __dirname.toLowerCase() && process.argv[1].indexOf('://') !== -1) {
        sendIPCToWindow(mainWindow, 'addTab', {
          url: process.argv[1]
        })
      } else if (global.URLToOpen) {
        // if there is a previously set URL to open (probably from opening a link on macOS), open it
        sendIPCToWindow(mainWindow, 'addTab', {
          url: global.URLToOpen
        })
        global.URLToOpen = null
      }
    })
  })

  // Open the DevTools.
  // mainWindow.openDevTools()

  createAppMenu()
  createDockMenu()
})

app.on('open-url', function (e, url) {
  if (appIsReady) {
    sendIPCToWindow(mainWindow, 'addTab', {
      url: url
    })
  } else {
    global.URLToOpen = url // this will be handled later in the createWindow callback
  }
})

/**
 * Emitted when the application is activated, which usually happens when clicks on the applications's dock icon
 * https://github.com/electron/electron/blob/master/docs/api/app.md#event-activate-os-x
 *
 * Opens a new tab when all tabs are closed, and min is still open by clicking on the application dock icon
 */
app.on('activate', function (/* e, hasVisibleWindows */) {
  if (!mainWindow && appIsReady) { // sometimes, the event will be triggered before the app is ready, and creating new windows will fail
    createWindow()
  }
})

function createAppMenu () {
  // create the menu. based on example from http://electron.atom.io/docs/v0.34.0/api/menu/

  var Menu = electron.Menu
  var MenuItem = electron.MenuItem

  var template = [
    {
      label: l('appMenuFile'),
      submenu: [
        {
          label: l('appMenuNewTab'),
          accelerator: 'CmdOrCtrl+t',
          click: function (item, window) {
            sendIPCToWindow(window, 'addTab')
          }
        },
        {
          label: l('appMenuNewPrivateTab'),
          accelerator: 'shift+CmdOrCtrl+p',
          click: function (item, window) {
            sendIPCToWindow(window, 'addPrivateTab')
          }
        },
        {
          label: l('appMenuNewTask'),
          accelerator: 'CmdOrCtrl+n',
          click: function (item, window) {
            sendIPCToWindow(window, 'addTask')
          }
        },
        {
          type: 'separator'
        },
        {
          label: l('appMenuSavePageAs'),
          accelerator: 'CmdOrCtrl+s',
          click: function (item, window) {
            sendIPCToWindow(window, 'saveCurrentPage')
          }
        },
        {
          type: 'separator'
        },
        {
          label: l('appMenuPrint'),
          accelerator: 'CmdOrCtrl+p',
          click: function (item, window) {
            sendIPCToWindow(window, 'print')
          }
        }
      ]
    },
    {
      label: l('appMenuEdit'),
      submenu: [
        {
          label: l('appMenuUndo'),
          accelerator: 'CmdOrCtrl+Z',
          role: 'undo'
        },
        {
          label: l('appMenuRedo'),
          accelerator: 'Shift+CmdOrCtrl+Z',
          role: 'redo'
        },
        {
          type: 'separator'
        },
        {
          label: l('appMenuCut'),
          accelerator: 'CmdOrCtrl+X',
          role: 'cut'
        },
        {
          label: l('appMenuCopy'),
          accelerator: 'CmdOrCtrl+C',
          role: 'copy'
        },
        {
          label: l('appMenuPaste'),
          accelerator: 'CmdOrCtrl+V',
          role: 'paste'
        },
        {
          label: l('appMenuSelectAll'),
          accelerator: 'CmdOrCtrl+A',
          role: 'selectall'
        },
        {
          type: 'separator'
        },
        {
          label: l('appMenuFind'),
          accelerator: 'CmdOrCtrl+F',
          click: function (item, window) {
            sendIPCToWindow(window, 'findInPage')
          }
        }
      ]
    },
    /* these items are added by os x */
    {
      label: l('appMenuView'),
      submenu: [
        {
          label: l('appMenuZoomIn'),
          accelerator: 'CmdOrCtrl+=',
          click: function (item, window) {
            sendIPCToWindow(window, 'zoomIn')
          }
        },
        {
          label: l('appMenuZoomOut'),
          accelerator: 'CmdOrCtrl+-',
          click: function (item, window) {
            sendIPCToWindow(window, 'zoomOut')
          }
        },
        {
          label: l('appMenuActualSize'),
          accelerator: 'CmdOrCtrl+0',
          click: function (item, window) {
            sendIPCToWindow(window, 'zoomReset')
          }
        },
        {
          type: 'separator'
        },
        {
          label: l('appMenuFullScreen'),
          accelerator: (function () {
            if (process.platform == 'darwin')
              return 'Ctrl+Command+F'
            else
              return 'F11'
          })(),
          role: 'togglefullscreen'
        },
        {
          label: l('appMenuFocusMode'),
          accelerator: undefined,
          type: 'checkbox',
          checked: false,
          click: function (item, window) {
            if (isFocusMode) {
              item.checked = false
              isFocusMode = false
              sendIPCToWindow(window, 'exitFocusMode')
            } else {
              item.checked = true
              isFocusMode = true
              sendIPCToWindow(window, 'enterFocusMode')
            }
          }
        },
        {
          label: l('appMenuReadingList'),
          accelerator: undefined,
          click: function (item, window) {
            sendIPCToWindow(window, 'showReadingList')
          }
        }
      ]
    },
    {
      label: l('appMenuDeveloper'),
      submenu: [
        {
          label: l('appMenuReloadBrowser'),
          accelerator: undefined,
          click: function (item, focusedWindow) {
            if (focusedWindow) focusedWindow.reload()
          }
        },
        {
          label: l('appMenuInspectBrowser'),
          click: function (item, focusedWindow) {
            if (focusedWindow) focusedWindow.toggleDevTools()
          }
        },
        {
          type: 'separator'
        },
        {
          label: l('appMenuInspectPage'),
          accelerator: (function () {
            if (process.platform == 'darwin')
              return 'Cmd+Alt+I'
            else
              return 'Ctrl+Shift+I'
          })(),
          click: function (item, window) {
            sendIPCToWindow(window, 'inspectPage')
          }
        }
      ]
    },
    {
      label: l('appMenuWindow'),
      role: 'window',
      submenu: [
        {
          label: l('appMenuMinimize'),
          accelerator: 'CmdOrCtrl+M',
          role: 'minimize'
        },
        {
          label: l('appMenuClose'),
          accelerator: 'CmdOrCtrl+W',
          role: 'close'
        }
      ]
    },
    {
      label: l('appMenuHelp'),
      role: 'help',
      submenu: [
        {
          label: l('appMenuKeyboardShortcuts'),
          click: function () {
            openTabInWindow('https://github.com/minbrowser/min/wiki#keyboard-shortcuts')
          }
        },
        {
          label: l('appMenuReportBug'),
          click: function () {
            openTabInWindow('https://github.com/minbrowser/min/issues/new')
          }
        },
        {
          label: l('appMenuTakeTour'),
          click: function () {
            openTabInWindow('https://minbrowser.github.io/min/tour/')
          }
        },
        {
          label: l('appMenuViewGithub'),
          click: function () {
            openTabInWindow('https://github.com/minbrowser/min')
          }
        }
      ]
    }
  ]

  if (process.platform === 'darwin') {
    var name = app.getName()
    template.unshift({
      label: name,
      submenu: [
        {
          label: l('appMenuAbout').replace('%n', name),
          role: 'about'
        },
        {
          type: 'separator'
        },
        {
          label: l('appMenuPreferences'),
          accelerator: 'CmdOrCtrl+,',
          click: function (item, window) {
            sendIPCToWindow(window, 'addTab', {
              url: 'file://' + __dirname + '/pages/settings/index.html'
            })
          }
        },
        {
          label: 'Services',
          role: 'services',
          submenu: []
        },
        {
          type: 'separator'
        },
        {
          label: l('appMenuHide').replace('%n', name),
          accelerator: 'CmdOrCtrl+H',
          role: 'hide'
        },
        {
          label: l('appMenuHideOthers'),
          accelerator: 'CmdOrCtrl+Shift+H',
          role: 'hideothers'
        },
        {
          label: l('appMenuShowAll'),
          role: 'unhide'
        },
        {
          type: 'separator'
        },
        {
          label: l('appMenuQuit').replace('%n', name),
          accelerator: 'CmdOrCtrl+Q',
          click: function () {
            app.quit()
          }
        }
      ]
    })
    // Window menu.
    template[3].submenu.push({
      type: 'separator'
    }, {
      label: l('appMenuBringToFront'),
      role: 'front'
    })
  }

  // preferences item on linux and windows

  if (process.platform !== 'darwin') {
    template[1].submenu.push({
      type: 'separator'
    })

    template[1].submenu.push({
      label: l('appMenuPreferences'),
      accelerator: 'CmdOrCtrl+,',
      click: function (item, window) {
        sendIPCToWindow(window, 'addTab', {
          url: 'file://' + __dirname + '/pages/settings/index.html'
        })
      }
    })
  }

  var menu = new Menu()

  menu = Menu.buildFromTemplate(template)
  Menu.setApplicationMenu(menu)
}

function createDockMenu () {
  // create the menu. based on example from https://github.com/electron/electron/blob/master/docs/tutorial/desktop-environment-integration.md#custom-dock-menu-macos
  if (process.platform === 'darwin') {
    var Menu = electron.Menu

    var template = [
      {
        label: l('appMenuNewTab'),
        click: function (item, window) {
          sendIPCToWindow(window, 'addTab')
        }
      },
      {
        label: l('appMenuNewPrivateTab'),
        click: function (item, window) {
          sendIPCToWindow(window, 'addPrivateTab')
        }
      },
      {
        label: l('appMenuNewTask'),
        click: function (item, window) {
          sendIPCToWindow(window, 'addTask')
        }
      }
    ]

    var dockMenu = Menu.buildFromTemplate(template)
    app.dock.setMenu(dockMenu)
  }
}
<commit_msg>Adds mailto protocol handling (delegates to default system mail application) (#428)<commit_after>const electron = require('electron')
const fs = require('fs')
const path = require('path')
const app = electron.app // Module to control application life.
const protocol = electron.protocol // Module to control protocol handling
const shell = electron.shell // Module to call system functionality
const BrowserWindow = electron.BrowserWindow // Module to create native browser window.

var userDataPath = app.getPath('userData')

const browserPage = 'file://' + __dirname + '/index.html'

var mainWindow = null
var isFocusMode = false
var appIsReady = false

var saveWindowBounds = function () {
  if (mainWindow) {
    fs.writeFile(path.join(userDataPath, 'windowBounds.json'), JSON.stringify(mainWindow.getBounds()))
  }
}

function sendIPCToWindow (window, action, data) {
  // if there are no windows, create a new one
  if (!mainWindow) {
    createWindow(function () {
      mainWindow.webContents.send(action, data || {})
    })
  } else {
    mainWindow.webContents.send(action, data || {})
  }
}

function openTabInWindow (url) {
  sendIPCToWindow(mainWindow, 'addTab', {
    url: url
  })
}

function createWindow (cb) {
  var savedBounds = fs.readFile(path.join(userDataPath, 'windowBounds.json'), 'utf-8', function (e, data) {
    if (e || !data) { // there was an error, probably because the file doesn't exist
      var size = electron.screen.getPrimaryDisplay().workAreaSize
      var bounds = {
        x: 0,
        y: 0,
        width: size.width,
        height: size.height
      }
    } else {
      var bounds = JSON.parse(data)
    }

// maximizes the window frame in windows 10
// fixes https://github.com/minbrowser/min/issues/214
// should be removed once https://github.com/electron/electron/issues/4045 is fixed
    if (process.platform === 'win32') {
      if ((bounds.x === 0 && bounds.y === 0) || (bounds.x === -8 && bounds.y === -8)) {
        var screenSize = electron.screen.getPrimaryDisplay().workAreaSize
        if ((screenSize.width === bounds.width || bounds.width - screenSize.width === 16) && (screenSize.height === bounds.height || bounds.height - screenSize.height === 16)) {
          var shouldMaximize = true
        }
      }
    }

    createWindowWithBounds(bounds, shouldMaximize)

    if (cb) {
      cb()
    }
  })
}

function createWindowWithBounds (bounds, shouldMaximize) {
  mainWindow = new BrowserWindow({
    width: bounds.width,
    height: bounds.height,
    x: bounds.x,
    y: bounds.y,
    minWidth: 320,
    minHeight: 500,
    titleBarStyle: 'hidden-inset',
    icon: __dirname + '/icons/icon256.png'
  })

  // and load the index.html of the app.
  mainWindow.loadURL(browserPage)

  if (shouldMaximize) {
    mainWindow.maximize()
  }

  // save the window size for the next launch of the app
  mainWindow.on('close', function () {
    saveWindowBounds()
  })

  // Emitted when the window is closed.
  mainWindow.on('closed', function () {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null
  })

  /* handle pdf downloads - ipc recieved in fileDownloadManager.js */

  mainWindow.webContents.session.on('will-download', function (event, item, webContents) {
    var itemURL = item.getURL()
    if (item.getMimeType() === 'application/pdf' && itemURL.indexOf('blob:') !== 0 && itemURL.indexOf('#pdfjs.action=download') === -1) { // clicking the download button in the viewer opens a blob url, so we don't want to open those in the viewer (since that would make it impossible to download a PDF)
      event.preventDefault()
      sendIPCToWindow(mainWindow, 'openPDF', {
        url: itemURL,
        webContentsId: webContents.getId(),
        event: event,
        item: item // as of electron 0.35.1, this is an empty object
      })
    }
    return true
  })

  mainWindow.on('enter-full-screen', function () {
    sendIPCToWindow(mainWindow, 'enter-full-screen')
  })

  mainWindow.on('leave-full-screen', function () {
    sendIPCToWindow(mainWindow, 'leave-full-screen')
  })

  mainWindow.on('app-command', function (e, command) {
    if (command === 'browser-backward') {
      sendIPCToWindow(mainWindow, 'goBack')
    } else if (command === 'browser-forward') {
      sendIPCToWindow(mainWindow, 'goForward')
    }
  })

  // prevent remote pages from being loaded using drag-and-drop, since they would have node access
  mainWindow.webContents.on('will-navigate', function (e, url) {
    if (url !== browserPage) {
      e.preventDefault()
    }
  })

  registerFiltering() // register filtering for the default session

  return mainWindow
}

// Quit when all windows are closed.
app.on('window-all-closed', function () {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on('ready', function () {
  appIsReady = true

  registerProtocols()

  createWindow(function () {
    mainWindow.webContents.on('did-finish-load', function () {
      // if a URL was passed as a command line argument (probably because Min is set as the default browser on Linux), open it.
      if (process.argv && process.argv[1] && process.argv[1].toLowerCase() !== __dirname.toLowerCase() && process.argv[1].indexOf('://') !== -1) {
        sendIPCToWindow(mainWindow, 'addTab', {
          url: process.argv[1]
        })
      } else if (global.URLToOpen) {
        // if there is a previously set URL to open (probably from opening a link on macOS), open it
        sendIPCToWindow(mainWindow, 'addTab', {
          url: global.URLToOpen
        })
        global.URLToOpen = null
      }
    })
  })

  // Open the DevTools.
  // mainWindow.openDevTools()

  createAppMenu()
  createDockMenu()
})

app.on('open-url', function (e, url) {
  if (appIsReady) {
    sendIPCToWindow(mainWindow, 'addTab', {
      url: url
    })
  } else {
    global.URLToOpen = url // this will be handled later in the createWindow callback
  }
})

/**
 * Emitted when the application is activated, which usually happens when clicks on the applications's dock icon
 * https://github.com/electron/electron/blob/master/docs/api/app.md#event-activate-os-x
 *
 * Opens a new tab when all tabs are closed, and min is still open by clicking on the application dock icon
 */
app.on('activate', function (/* e, hasVisibleWindows */) {
  if (!mainWindow && appIsReady) { // sometimes, the event will be triggered before the app is ready, and creating new windows will fail
    createWindow()
  }
})

function registerProtocols() {
  protocol.registerStringProtocol('mailto', function(req, cb) {
    shell.openExternal(req.url)
    return null
  }, function (error) {
    if (error) {
      console.log('Could not register mailto protocol.')
    }
  })
}

function createAppMenu () {
  // create the menu. based on example from http://electron.atom.io/docs/v0.34.0/api/menu/

  var Menu = electron.Menu
  var MenuItem = electron.MenuItem

  var template = [
    {
      label: l('appMenuFile'),
      submenu: [
        {
          label: l('appMenuNewTab'),
          accelerator: 'CmdOrCtrl+t',
          click: function (item, window) {
            sendIPCToWindow(window, 'addTab')
          }
        },
        {
          label: l('appMenuNewPrivateTab'),
          accelerator: 'shift+CmdOrCtrl+p',
          click: function (item, window) {
            sendIPCToWindow(window, 'addPrivateTab')
          }
        },
        {
          label: l('appMenuNewTask'),
          accelerator: 'CmdOrCtrl+n',
          click: function (item, window) {
            sendIPCToWindow(window, 'addTask')
          }
        },
        {
          type: 'separator'
        },
        {
          label: l('appMenuSavePageAs'),
          accelerator: 'CmdOrCtrl+s',
          click: function (item, window) {
            sendIPCToWindow(window, 'saveCurrentPage')
          }
        },
        {
          type: 'separator'
        },
        {
          label: l('appMenuPrint'),
          accelerator: 'CmdOrCtrl+p',
          click: function (item, window) {
            sendIPCToWindow(window, 'print')
          }
        }
      ]
    },
    {
      label: l('appMenuEdit'),
      submenu: [
        {
          label: l('appMenuUndo'),
          accelerator: 'CmdOrCtrl+Z',
          role: 'undo'
        },
        {
          label: l('appMenuRedo'),
          accelerator: 'Shift+CmdOrCtrl+Z',
          role: 'redo'
        },
        {
          type: 'separator'
        },
        {
          label: l('appMenuCut'),
          accelerator: 'CmdOrCtrl+X',
          role: 'cut'
        },
        {
          label: l('appMenuCopy'),
          accelerator: 'CmdOrCtrl+C',
          role: 'copy'
        },
        {
          label: l('appMenuPaste'),
          accelerator: 'CmdOrCtrl+V',
          role: 'paste'
        },
        {
          label: l('appMenuSelectAll'),
          accelerator: 'CmdOrCtrl+A',
          role: 'selectall'
        },
        {
          type: 'separator'
        },
        {
          label: l('appMenuFind'),
          accelerator: 'CmdOrCtrl+F',
          click: function (item, window) {
            sendIPCToWindow(window, 'findInPage')
          }
        }
      ]
    },
    /* these items are added by os x */
    {
      label: l('appMenuView'),
      submenu: [
        {
          label: l('appMenuZoomIn'),
          accelerator: 'CmdOrCtrl+=',
          click: function (item, window) {
            sendIPCToWindow(window, 'zoomIn')
          }
        },
        {
          label: l('appMenuZoomOut'),
          accelerator: 'CmdOrCtrl+-',
          click: function (item, window) {
            sendIPCToWindow(window, 'zoomOut')
          }
        },
        {
          label: l('appMenuActualSize'),
          accelerator: 'CmdOrCtrl+0',
          click: function (item, window) {
            sendIPCToWindow(window, 'zoomReset')
          }
        },
        {
          type: 'separator'
        },
        {
          label: l('appMenuFullScreen'),
          accelerator: (function () {
            if (process.platform == 'darwin')
              return 'Ctrl+Command+F'
            else
              return 'F11'
          })(),
          role: 'togglefullscreen'
        },
        {
          label: l('appMenuFocusMode'),
          accelerator: undefined,
          type: 'checkbox',
          checked: false,
          click: function (item, window) {
            if (isFocusMode) {
              item.checked = false
              isFocusMode = false
              sendIPCToWindow(window, 'exitFocusMode')
            } else {
              item.checked = true
              isFocusMode = true
              sendIPCToWindow(window, 'enterFocusMode')
            }
          }
        },
        {
          label: l('appMenuReadingList'),
          accelerator: undefined,
          click: function (item, window) {
            sendIPCToWindow(window, 'showReadingList')
          }
        }
      ]
    },
    {
      label: l('appMenuDeveloper'),
      submenu: [
        {
          label: l('appMenuReloadBrowser'),
          accelerator: undefined,
          click: function (item, focusedWindow) {
            if (focusedWindow) focusedWindow.reload()
          }
        },
        {
          label: l('appMenuInspectBrowser'),
          click: function (item, focusedWindow) {
            if (focusedWindow) focusedWindow.toggleDevTools()
          }
        },
        {
          type: 'separator'
        },
        {
          label: l('appMenuInspectPage'),
          accelerator: (function () {
            if (process.platform == 'darwin')
              return 'Cmd+Alt+I'
            else
              return 'Ctrl+Shift+I'
          })(),
          click: function (item, window) {
            sendIPCToWindow(window, 'inspectPage')
          }
        }
      ]
    },
    {
      label: l('appMenuWindow'),
      role: 'window',
      submenu: [
        {
          label: l('appMenuMinimize'),
          accelerator: 'CmdOrCtrl+M',
          role: 'minimize'
        },
        {
          label: l('appMenuClose'),
          accelerator: 'CmdOrCtrl+W',
          role: 'close'
        }
      ]
    },
    {
      label: l('appMenuHelp'),
      role: 'help',
      submenu: [
        {
          label: l('appMenuKeyboardShortcuts'),
          click: function () {
            openTabInWindow('https://github.com/minbrowser/min/wiki#keyboard-shortcuts')
          }
        },
        {
          label: l('appMenuReportBug'),
          click: function () {
            openTabInWindow('https://github.com/minbrowser/min/issues/new')
          }
        },
        {
          label: l('appMenuTakeTour'),
          click: function () {
            openTabInWindow('https://minbrowser.github.io/min/tour/')
          }
        },
        {
          label: l('appMenuViewGithub'),
          click: function () {
            openTabInWindow('https://github.com/minbrowser/min')
          }
        }
      ]
    }
  ]

  if (process.platform === 'darwin') {
    var name = app.getName()
    template.unshift({
      label: name,
      submenu: [
        {
          label: l('appMenuAbout').replace('%n', name),
          role: 'about'
        },
        {
          type: 'separator'
        },
        {
          label: l('appMenuPreferences'),
          accelerator: 'CmdOrCtrl+,',
          click: function (item, window) {
            sendIPCToWindow(window, 'addTab', {
              url: 'file://' + __dirname + '/pages/settings/index.html'
            })
          }
        },
        {
          label: 'Services',
          role: 'services',
          submenu: []
        },
        {
          type: 'separator'
        },
        {
          label: l('appMenuHide').replace('%n', name),
          accelerator: 'CmdOrCtrl+H',
          role: 'hide'
        },
        {
          label: l('appMenuHideOthers'),
          accelerator: 'CmdOrCtrl+Shift+H',
          role: 'hideothers'
        },
        {
          label: l('appMenuShowAll'),
          role: 'unhide'
        },
        {
          type: 'separator'
        },
        {
          label: l('appMenuQuit').replace('%n', name),
          accelerator: 'CmdOrCtrl+Q',
          click: function () {
            app.quit()
          }
        }
      ]
    })
    // Window menu.
    template[3].submenu.push({
      type: 'separator'
    }, {
      label: l('appMenuBringToFront'),
      role: 'front'
    })
  }

  // preferences item on linux and windows

  if (process.platform !== 'darwin') {
    template[1].submenu.push({
      type: 'separator'
    })

    template[1].submenu.push({
      label: l('appMenuPreferences'),
      accelerator: 'CmdOrCtrl+,',
      click: function (item, window) {
        sendIPCToWindow(window, 'addTab', {
          url: 'file://' + __dirname + '/pages/settings/index.html'
        })
      }
    })
  }

  var menu = new Menu()

  menu = Menu.buildFromTemplate(template)
  Menu.setApplicationMenu(menu)
}

function createDockMenu () {
  // create the menu. based on example from https://github.com/electron/electron/blob/master/docs/tutorial/desktop-environment-integration.md#custom-dock-menu-macos
  if (process.platform === 'darwin') {
    var Menu = electron.Menu

    var template = [
      {
        label: l('appMenuNewTab'),
        click: function (item, window) {
          sendIPCToWindow(window, 'addTab')
        }
      },
      {
        label: l('appMenuNewPrivateTab'),
        click: function (item, window) {
          sendIPCToWindow(window, 'addPrivateTab')
        }
      },
      {
        label: l('appMenuNewTask'),
        click: function (item, window) {
          sendIPCToWindow(window, 'addTask')
        }
      }
    ]

    var dockMenu = Menu.buildFromTemplate(template)
    app.dock.setMenu(dockMenu)
  }
}
<|endoftext|>


<commit_before>/**
* jQuery Youtube API Feed Plugin
* @author John Hoover <john@defvayne23.com>
* @version 1.3.1
*/
(function($){$.extend({jTube:function(g){var g=$.extend({user:'',userType:'uploads',search:'',feed:'',playlist:'',format:'flash',order:'published',time:'all_time',limit:5,page:1,success:function(a,b){},error:function(a){}},g);var h='http://gdata.youtube.com/feeds/';var i=this;var j='';if(g.user!='')h+='users/'+g.user+'/'+g.userType+'?';else if(g.search!='')h+='api/videos?q='+g.search+'&';else if(g.feed!='')h+='api/standardfeeds/'+g.feed+'?';else if(g.playlist!='')h+='api/playlists/'+g.playlist+'?';else{g.error("No feed choices given.");return false}h+='alt=json';h+='&max-results='+g.limit;h+='&start-index='+(((g.page*g.limit)-g.limit)+1);h+='&orderby='+g.order;h+='&time='+g.time;if(g.format=="mpeg")h+='&format=6';else if(g.format=="h263")h+='&format=1';else h+='&format=5';$.ajax({url:h,dataType:'json',success:function(d){if(d!=null){if(g.user!=''&&g.userType=='playlists'){var e=[];$(d.feed.entry).each(function(){var a={title:this.title.$t,description:this.yt$description.$t,id:this.yt$playlistId.$t,link:this.link[1].href,published:jTubePublished(this.published.$t)};e[e.length]=a});var f=e}else{var f=[];$(d.feed.entry).each(function(){var b=[];$(this.category).each(function(a){if(a!=0){b[a-1]=this.term}});var c={title:this.title.$t,description:this.media$group.media$description.$t,link:this.link[0].href,categories:b,author:{name:this.author[0].name.$t,link:this.author[0].uri.$t},thumbnail:this.media$group.media$thumbnail[3].url};videoFormats=[];$(this.media$group.media$content).each(function(){videoFormats[this.yt$format]=this.url});if(g.format=="mpeg")c.video=videoFormats[6];else if(g.format=="h263")c.video=videoFormats[1];else c.video=videoFormats[5];if(this.published)c.published=jTubePublished(this.published.$t);if(this.media$group.yt$duration.seconds){duration=this.media$group.yt$duration.seconds;hours=0;minutes=0;seconds=0;while(duration>=3600){hours=hours+1;duration=duration-3600}while(duration>=60){minutes=minutes+1;duration=duration-60}seconds=duration;if(seconds<10)seconds='0'+seconds;c.length=minutes+':'+seconds;if(hours>0)c.length=hours+':'+c.length}if(this.yt$statistics)c.views=this.yt$statistics.viewCount;f[f.length]=c})}pages=Math.ceil(d.feed.openSearch$totalResults.$t/g.limit);g.success(f,pages)}else{g.error("Bad request.")}}});return this},jTubeEmbed:function(a,b){var b=$.extend({width:290,height:250,autoplay:true,fullscreeen:false,related:true,loop:false,keyboard:true,genie:false,border:false,highdef:true,start:0},b);var c=a+"?";c+='autoplay='+(b.autoPlay?1:0);c+='&fs='+(b.fullscreen?1:0);c+='&rl=1'+(b.related?1:0);c+='&loop=1'+(b.loop?1:0);c+='&disablekb=0'+(b.keyboard?0:1);c+='&egm=1'+(b.genie?1:0);c+='&border=1'+(b.border?1:0);c+='&hd=1'+(b.highdef?1:0);c+='&start='+b.start;var d='<object width="'+b.width+'" height="'+b.height+'">';d+='<param name="movie" value="'+c+'"</param>';d+='<param name="allowScriptAccess" value="always"></param>';if(b.fullscreen==true)d+='<param name="allowFullScreen" value="true"></param>';d+='<embed src="'+c+'"';d+='    type="application/x-shockwave-flash"';if(b.fullscreen==true)d+='    allowfullscreen="true"';d+='    allowscriptaccess="always"';d+='    width="'+b.width+'" height="'+b.height+'">';d+='    </embed>';d+='</object>';return d},})})(jQuery);function jTubePublished(a){a=a.match(/([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/);meridiem="pm";hour=a[4];if(hour<12){meridiem="am"}else{if(hour>12)hour=hour-12}return{year:a[1],month:a[2],day:a[3],hour:hour,minute:a[5],seconds:a[6],meridiem:meridiem}}<commit_msg>Rename min file.<commit_after><|endoftext|>


<commit_before>import req from 'request';
// import CommerceNews from './commerce-news';
// import GovernmentNews from './government-news';
// import Ministerials from './ministerials';
// import Tweets from './tweets';

exports.combinedNews = function collateAllNews(request, reply) {
  // console.dir(request);
  console.dir(request.connection.server.info);
  // console.dir(server.info);
  const endpointProto = request.connection.server.info.protocol;
  console.log(endpointProto);
  const endpointPort = request.connection.server.info.port;
  console.log(endpointPort);
  const checkHost = request.connection.server.info.host;
  console.log(checkHost);
  let endpointHost = '';
  if (checkHost.indexOf('local') > 0 ) {
    console.log('localhost fired');
    endpointHost = 'localhost';
  } else {
    console.log('remote host fired');
    endpointHost = checkHost;
  };
  // const endpointUrl = 'http://localhost:3000';
  const endpointUrl = `${endpointProto}://${endpointHost}:${endpointPort}`;
  console.log('endpoint url:', endpointUrl);
  let news = [];
  req(`${endpointUrl}/api/v1/statements/commerce`, (error, response, body) => {
    if (error) {
      console.log(error);
    }
    // console.log(body);
    news.push(JSON.parse(body));
    console.dir(news);
  });
  console.dir(news);
  reply('testing');
};
<commit_msg>Got combined feed working, though it's a bit slowish<commit_after>import goodGuyHttp from 'good-guy-http';

const goodGuy = goodGuyHttp({
  // defaultCaching: {
  //   timeToLive: 300000,
  // },
  // headers: {
  //   'User-Agent': 'Department of Commerce Intranet - request',
  // },
  // proxy: process.env.HTTP_PROXY,
  timeout: 15000,
});

exports.combinedNews = async function collateAllNews(request, reply) {
  // console.dir(request);
  // console.dir(request.connection.server.info);
  // console.dir(server.info);
  const endpointProto = request.connection.server.info.protocol;
  // console.log(endpointProto);
  const endpointPort = request.connection.server.info.port;
  // console.log(endpointPort);
  const checkHost = request.connection.server.info.host;
  // console.log(checkHost);
  let endpointHost = '';
  if (checkHost.indexOf('local') > 0 ) {
    // console.log('localhost fired');
    endpointHost = 'localhost';
  } else {
    // console.log('remote host fired');
    endpointHost = checkHost;
  };
  // const endpointUrl = 'http://localhost:3000';
  const endpointUrl = `${endpointProto}://${endpointHost}:${endpointPort}`;
  // console.log('endpoint url:', endpointUrl);
  let news = [];
  const commerceNews = await goodGuy(`${endpointUrl}/api/v1/statements/commerce`);
  news.push(JSON.parse(commerceNews.body.toString()));
  const ministerials = await goodGuy(`${endpointUrl}/api/v1/statements/ministerials`);
  news.push(JSON.parse(ministerials.body.toString()));
  const governmentNews = await goodGuy(`${endpointUrl}/api/v1/statements/government`);
  news.push(JSON.parse(governmentNews.body.toString()));
  const dmirsTweets = await goodGuy(`${endpointUrl}/api/v1/tweets/dmirs`);
  news.push(JSON.parse(dmirsTweets.body.toString()));
  const combined = news.concat(
    JSON.parse(commerceNews.body.toString()),
    JSON.parse(ministerials.body.toString()),
    JSON.parse(governmentNews.body.toString()),
    JSON.parse(dmirsTweets.body.toString())
  );
  const flattened = combined.reduce((a, b) => {
    return a.concat(b);
  }, []);
  console.log('items count =', flattened.length);
  // console.dir(news);
  // reply('testing');
  // reply(combined);
  reply(flattened);
};
<|endoftext|>


<commit_before>angular
  .module('lnPatterns')
  .controller('lnOGravityFormController', lnOGravityFormController);

/*@ngInject*/
function lnOGravityFormController($log, $scope, $element, $attrs, $sce, lnOGravityFormService) {

  var FORM_INPUTS = ['input', 'textarea', 'select', 'datalist', 'keygen'];
  var vm = this;
  var inputs;

  vm.form = $element[0];
  vm.submitDisabled = false;

  var checkLoadFormResponse = function (response) {
    vm.formHTMLValid = response.data.isValid;
    vm.formHTML = $sce.trustAsHtml(response.data.html);
  };

  var getInputElements = function () {
    var _elements = [];

    for (var i = 0; i < FORM_INPUTS.length; i++) {
      var tag = FORM_INPUTS[i];
      var _inputs = $element.find(tag);

      if (!!_inputs && _inputs.length) {
        for (var j = 0; j < _inputs.length; j++) {
          _elements.push(_inputs[j]);
        }
      }
    }

    return _elements;
  };

  // Clear value of object inside input fields after a submission
  var clearInputElements = function () {
    angular.forEach(inputs, function (val) {
      val.value = '';
    });
  };

  /*
   var highlightInputErrors = function (validationMessages) {
   for (var i in validationMessages) {
   var name = 'input_' + i;
   for (var j = 0; j < inputs.length; j++) {
   if (inputs[j].name === name) {
   if(!!inputs[j].placeholder) {
   inputs[j].placeholder = validationMessages[i];
   }
   continue;
   }
   }
   }
   };
   */

  var gatherFormData = function () {
    var formData = {
      'input_values': {}
    };

    inputs = getInputElements();

    if (!!inputs && inputs.length) {
      for (var i = 0; i < inputs.length; i++) {
        var input = inputs[i];

        if (!!input.name && input.name !== '') {

          // Make sure we only get a selected radio button or checkbox
          if ((input.type === 'radio' || input.type === 'checkbox') && !input.checked) {
            continue;
          }

          formData.input_values[input.name] = input.value;
        }
      }
    }

    return formData;
  };

  // Event Handlers
  //

  var _submit = function () {
    var data = gatherFormData();

    $log.debug('lnOGravityFormController._submit() -> ', data);
    vm.submitDisabled = true;

    lnOGravityFormService
      .submit(vm.id, data)
      .then(_onSubmitted, _onError)
    ;
  };

  var _onSubmitted = function (response) {
    $log.debug('lnOGravityFormController._onSubmitted()->', response);

    vm.submitDisabled = false;
    clearInputElements();

    if ($attrs.lnSubmit) {
      vm.onSubmit({response: response});
    }
  };

  var _onError = function (error) {
    $log.error('lnOGravityFormController._onError()->', error);

    vm.submitDisabled = false;
    clearInputElements();
    //highlightInputErrors(error.data.response.validation_messages);

    if ($attrs.lnError) {
      vm.onError({error: error});
    }
  };

  // Init
  //

  if (angular.isUndefined(lnOGravityFormService.getConfig())) {
    var unbindInit = $scope.$on('lnOGravityFormConfigUpdated', init);
  } else {
    init();
  }

  function init() {
    $log.debug('lnOGravityFormController -> ', vm, '/ $element: ', $element);

    unbindInit();

    if (vm.loadFromAPI === true) {
      lnOGravityFormService.getForm(vm.id)
        .then(checkLoadFormResponse, checkLoadFormResponse);
    }

    vm.form.id = 'gform_' + vm.id;

    $element.on('submit', _submit);

    var cleanup = function () {
      $element.off('submit', _submit);
    };

    $scope.$on('$destroy', cleanup);
  }

}
<commit_msg>add check against unbindInit<commit_after>angular
  .module('lnPatterns')
  .controller('lnOGravityFormController', lnOGravityFormController);

/*@ngInject*/
function lnOGravityFormController($log, $scope, $element, $attrs, $sce, lnOGravityFormService) {

  var FORM_INPUTS = ['input', 'textarea', 'select', 'datalist', 'keygen'];
  var vm = this;
  var inputs;

  vm.form = $element[0];
  vm.submitDisabled = false;

  var checkLoadFormResponse = function (response) {
    vm.formHTMLValid = response.data.isValid;
    vm.formHTML = $sce.trustAsHtml(response.data.html);
  };

  var getInputElements = function () {
    var _elements = [];

    for (var i = 0; i < FORM_INPUTS.length; i++) {
      var tag = FORM_INPUTS[i];
      var _inputs = $element.find(tag);

      if (!!_inputs && _inputs.length) {
        for (var j = 0; j < _inputs.length; j++) {
          _elements.push(_inputs[j]);
        }
      }
    }

    return _elements;
  };

  // Clear value of object inside input fields after a submission
  var clearInputElements = function () {
    angular.forEach(inputs, function (val) {
      val.value = '';
    });
  };

  /*
   var highlightInputErrors = function (validationMessages) {
   for (var i in validationMessages) {
   var name = 'input_' + i;
   for (var j = 0; j < inputs.length; j++) {
   if (inputs[j].name === name) {
   if(!!inputs[j].placeholder) {
   inputs[j].placeholder = validationMessages[i];
   }
   continue;
   }
   }
   }
   };
   */

  var gatherFormData = function () {
    var formData = {
      'input_values': {}
    };

    inputs = getInputElements();

    if (!!inputs && inputs.length) {
      for (var i = 0; i < inputs.length; i++) {
        var input = inputs[i];

        if (!!input.name && input.name !== '') {

          // Make sure we only get a selected radio button or checkbox
          if ((input.type === 'radio' || input.type === 'checkbox') && !input.checked) {
            continue;
          }

          formData.input_values[input.name] = input.value;
        }
      }
    }

    return formData;
  };

  // Event Handlers
  //

  var _submit = function () {
    var data = gatherFormData();

    $log.debug('lnOGravityFormController._submit() -> ', data);
    vm.submitDisabled = true;

    lnOGravityFormService
      .submit(vm.id, data)
      .then(_onSubmitted, _onError)
    ;
  };

  var _onSubmitted = function (response) {
    $log.debug('lnOGravityFormController._onSubmitted()->', response);

    vm.submitDisabled = false;
    clearInputElements();

    if ($attrs.lnSubmit) {
      vm.onSubmit({response: response});
    }
  };

  var _onError = function (error) {
    $log.error('lnOGravityFormController._onError()->', error);

    vm.submitDisabled = false;
    clearInputElements();
    //highlightInputErrors(error.data.response.validation_messages);

    if ($attrs.lnError) {
      vm.onError({error: error});
    }
  };

  // Init
  //

  if (angular.isUndefined(lnOGravityFormService.getConfig())) {
    var unbindInit = $scope.$on('lnOGravityFormConfigUpdated', init);
  } else {
    init();
  }

  function init() {
    $log.debug('lnOGravityFormController -> ', vm, '/ $element: ', $element);

    if (angular.isDefined(unbindInit)) {
      unbindInit();
    }

    if (vm.loadFromAPI === true) {
      lnOGravityFormService.getForm(vm.id)
        .then(checkLoadFormResponse, checkLoadFormResponse);
    }

    vm.form.id = 'gform_' + vm.id;

    $element.on('submit', _submit);

    var cleanup = function () {
      $element.off('submit', _submit);
    };

    $scope.$on('$destroy', cleanup);
  }

}
<|endoftext|>


<commit_before>"use strict";
define(['app'], function (app) {
	app.register.directive('aTable',['AtomicPath','aTable',function (aPath,aTable) {
		const DEFAULT =  {preload:true};
		return {
			require:'ngModel',
			restrict: 'E',
			scope:{
				headers:"=",
				props:"=",
				data:"=",
				searchBy:'=',
				searchWord:'=',
				activeItem:'=ngModel',
				onRowClick:'&?',
				isPreload:'=?',
				onColClick:'&?'
			},
			templateUrl:function(elem,attr){
				return aPath.url('/view/atom/aTable.html');
			},
			link: function($scope,elem, attrs) {
				$scope.isPreload =  $scope.isPreload || DEFAULT.preload;
			},
			controller:function($scope){
				$scope.$watchGroup(['headers','props','data'],function(){
					$scope.Headers =  $scope.headers;
					$scope.Props = $scope.props;
					$scope.Items = $scope.data;
					$scope.activeItem = {};
					var sortDir = $scope.sortDir=='asc'?'down':'up';
					var sortField  = $scope.sortBy;

					$scope.__SortDir = sortDir;
					$scope.__SortField =sortField;
					$scope.ShowSort = sortField;
					console.log(sortDir,sortField);
					if(sortField)
						$scope.sortItemsBy(sortField);

				});
				$scope.$watch('activeItem',function(item){
					$scope.activeItem = item;
				});
				$scope.setActiveItem = function(item){
					$scope.activeItem  = item;
					var item  = angular.copy(item);
					$scope.onRowClick()(item);
				}
				$scope.$watch('searchWord',function(){
					$scope.searchFilter={};
					for(var i in  $scope.searchBy){
						var field = $scope.searchBy[i];
						$scope.searchFilter[field]=$scope.searchWord;
					}
				});
				$scope.UIItems = [];
				$scope.searchLocal = function(item){
					var isMatched = !$scope.searchWord;
					if($scope.searchWord){
						var SWORD = $scope.searchWord.toUpperCase();
						for(var i in  $scope.searchBy){
							var field = $scope.searchBy[i];
							if(item[field]){
								var value =  item[field];
								if( typeof value == "string")
									isMatched = isMatched || value.toUpperCase().includes(SWORD);
								else if( typeof value == "number")
									isMatched = isMatched || (value+"").indexOf(SWORD) == 0;
							}
						}
					}
					return isMatched;
				}
				$scope.sortItemsBy = function(field){

					if($scope.sortGlobal) {
						$scope.sortGlobal  =false;
						return;
					}
					var currDir = $scope.__SortDir;
					var newDir = currDir!='up'?'up':null;
					if($scope.__SortField!=field){
						newDir='up';
					}
					$scope.__SortField = field;
					$scope.__SortDir = newDir;
					if(!newDir)
						$scope.__SortField = null;
					$scope.orderLocal = newDir?'-'+field:null;
				}
				$scope.setShowSort = function(field){
					$scope.ShowSort = field;
				}
				$scope.setSortBy = function(field){

					var currDir = $scope.__SortDir;
					var newDir = currDir!='up'?'asc':'desc';
					if($scope.__SortField!=field){
						newDir='asc';
					}
					$scope.sortGlobal  =true;
					$scope.sortBy = field;
					$scope.sortDir = newDir;

					console.log("setSortBy",field,newDir);
					$scope.__SortField = field;

					$scope.__SortDir =  newDir=='asc'?'up':'down';
					if($scope.onColClick){
						$scope.onColClick()(field,newDir);
					}

				}
			}
		}
	}]);
	app.register.factory("aTable",[function(){
		var aTable = {
			colHeaders:function(headers,props){
				var colClass ="";
				if(typeof headers[0]=="string"){
					var propsLen = props.length;
					if(propsLen<5){
						var size= (12/propsLen)
						colClass = ' col-md-'+size+' col-sm-'+size;
					}
				}

				angular.forEach(headers, function(hdr,i){
					if(typeof hdr == 'string'){
						hdr = {label:hdr,class:colClass};
					}

					headers[i] = hdr;
				});
				return headers;
			},
			isEqual:function(a,b){
					// Create arrays of property names
				    var aProps = Object.getOwnPropertyNames(a);
				    var bProps = Object.getOwnPropertyNames(b);

				    // If number of properties is different,
				    // objects are not equivalent
				    if (aProps.length != bProps.length) {
				        return false;
				    }

				    for (var i = 0; i < aProps.length; i++) {
				        var propName = aProps[i];

				        // If values of same property are not equal,
				        // objects are not equivalent
				        if (a[propName] !== b[propName]) {
				            return false;
				        }
				    }

			    // If we made it this far, objects
			    // are considered equivalent
			    return true;
			},
			isArrayEqual:function(A,B){
				var isEqual = false;
				for(var i in A){
					var a =  A[i];
					var b =  B[i];
					isEqual = isEqual && aTable.isEqual(a,b);
				}
				for(var i in B){
					var a =  A[i];
					var b =  B[i];
					isEqual = isEqual && aTable.isEqual(a,b);
				}
				return isEqual;
			},
			scroll:function(elem,dir){
				var child = angular.element(elem[0].querySelector('.table-entry-data'))[0];
				var goTo = child.scrollHeight;
				if(typeof dir == "number"){
					var nth =  dir+1;
					var row = angular.element(elem[0].querySelector('.table-entry-data tbody>tr:nth-child('+nth+')'))[0];
					var rH = row.clientHeight;
					var rO = row.offsetTop;
					var sT = child.scrollTop;
					var dy = (rO-sT)>rH?1:-1;

					goTo = sT + (rH*dy);

				}

				return new Promise(function(resolve,reject){
					(function scrollTo(element, to, duration) {

					    var start = element.scrollTop,
					        change = to - start,
					        currentTime = 0,
					        increment = 20;

					    var animateScroll = function(){
					        currentTime += increment;
					        var val = easeInOutQuad(currentTime, start, change, duration);
					        element.scrollTop = val;
					        if(currentTime < duration) {
					            setTimeout(animateScroll, increment);
					        }else{
					        	resolve();
					        }
					    };
					    animateScroll();
					    function easeInOutQuad(t, b, c, d) {
						  t /= d/2;
							if (t < 1) return c/2*t*t + b;
							t--;
							return -c/2 * (t*(t-2) - 1) + b;
						};
					})(child,goTo,300);
				});

			}

		};
		return aTable;
	}]);
	app.register.filter('typeof', function() {
	  return function(obj) {
	    return typeof obj
	  };
	});
});<commit_msg>Update a-table<commit_after>"use strict";
define(['app'], function (app) {
	app.register.directive('aTable',['AtomicPath','aTable',function (aPath,aTable) {
		const DEFAULT =  {preload:true};
		return {
			require:'?ngModel',
			restrict: 'E',
			scope:{
				headers:"=",
				props:"=",
				data:"=",
				searchBy:'=',
				searchWord:'=',
				activeItem:'=?ngModel',
				onRowClick:'&?',
				isPreload:'=?',
				onColClick:'&?'
			},
			templateUrl:function(elem,attr){
				return aPath.url('/view/atom/aTable.html');
			},
			link: function($scope,elem, attrs) {
				$scope.hasModel = attrs.ngModel!=undefined;
				$scope.isPreload =  $scope.isPreload || DEFAULT.preload;
			},
			controller:function($scope){
				$scope.$watchGroup(['headers','props','data'],function(){
					$scope.Headers =  $scope.headers;
					$scope.Props = $scope.props;
					$scope.Items = $scope.data;
					$scope.activeItem = {};
					var sortDir = $scope.sortDir=='asc'?'down':'up';
					var sortField  = $scope.sortBy;

					$scope.__SortDir = sortDir;
					$scope.__SortField =sortField;
					$scope.ShowSort = sortField;
					console.log(sortDir,sortField);
					if(sortField)
						$scope.sortItemsBy(sortField);

				});
				$scope.$watch('activeItem',function(item){
					$scope.activeItem = item;
				});
				$scope.setActiveItem = function(item){
					if(!$scope.hasModel) return;
					$scope.activeItem  = item;
					var item  = angular.copy(item);
					if($scope.onRowClick)
						$scope.onRowClick()(item);
				}
				$scope.$watch('searchWord',function(){
					$scope.searchFilter={};
					for(var i in  $scope.searchBy){
						var field = $scope.searchBy[i];
						$scope.searchFilter[field]=$scope.searchWord;
					}
				});
				$scope.UIItems = [];
				$scope.searchLocal = function(item){
					var isMatched = !$scope.searchWord;
					if($scope.searchWord){
						var SWORD = $scope.searchWord.toUpperCase();
						for(var i in  $scope.searchBy){
							var field = $scope.searchBy[i];
							if(item[field]){
								var value =  item[field];
								if( typeof value == "string")
									isMatched = isMatched || value.toUpperCase().includes(SWORD);
								else if( typeof value == "number")
									isMatched = isMatched || (value+"").indexOf(SWORD) == 0;
							}
						}
					}
					return isMatched;
				}
				$scope.sortItemsBy = function(field){

					if($scope.sortGlobal) {
						$scope.sortGlobal  =false;
						return;
					}
					var currDir = $scope.__SortDir;
					var newDir = currDir!='up'?'up':null;
					if($scope.__SortField!=field){
						newDir='up';
					}
					$scope.__SortField = field;
					$scope.__SortDir = newDir;
					if(!newDir)
						$scope.__SortField = null;
					$scope.orderLocal = newDir?'-'+field:null;
				}
				$scope.setShowSort = function(field){
					$scope.ShowSort = field;
				}
				$scope.setSortBy = function(field){

					var currDir = $scope.__SortDir;
					var newDir = currDir!='up'?'asc':'desc';
					if($scope.__SortField!=field){
						newDir='asc';
					}
					$scope.sortGlobal  =true;
					$scope.sortBy = field;
					$scope.sortDir = newDir;

					console.log("setSortBy",field,newDir);
					$scope.__SortField = field;

					$scope.__SortDir =  newDir=='asc'?'up':'down';
					if($scope.onColClick){
						$scope.onColClick()(field,newDir);
					}

				}
			}
		}
	}]);
	app.register.factory("aTable",[function(){
		var aTable = {
			colHeaders:function(headers,props){
				var colClass ="";
				if(typeof headers[0]=="string"){
					var propsLen = props.length;
					if(propsLen<5){
						var size= (12/propsLen)
						colClass = ' col-md-'+size+' col-sm-'+size;
					}
				}

				angular.forEach(headers, function(hdr,i){
					if(typeof hdr == 'string'){
						hdr = {label:hdr,class:colClass};
					}

					headers[i] = hdr;
				});
				return headers;
			},
			isEqual:function(a,b){
					// Create arrays of property names
				    var aProps = Object.getOwnPropertyNames(a);
				    var bProps = Object.getOwnPropertyNames(b);

				    // If number of properties is different,
				    // objects are not equivalent
				    if (aProps.length != bProps.length) {
				        return false;
				    }

				    for (var i = 0; i < aProps.length; i++) {
				        var propName = aProps[i];

				        // If values of same property are not equal,
				        // objects are not equivalent
				        if (a[propName] !== b[propName]) {
				            return false;
				        }
				    }

			    // If we made it this far, objects
			    // are considered equivalent
			    return true;
			},
			isArrayEqual:function(A,B){
				var isEqual = false;
				for(var i in A){
					var a =  A[i];
					var b =  B[i];
					isEqual = isEqual && aTable.isEqual(a,b);
				}
				for(var i in B){
					var a =  A[i];
					var b =  B[i];
					isEqual = isEqual && aTable.isEqual(a,b);
				}
				return isEqual;
			},
			scroll:function(elem,dir){
				var child = angular.element(elem[0].querySelector('.table-entry-data'))[0];
				var goTo = child.scrollHeight;
				if(typeof dir == "number"){
					var nth =  dir+1;
					var row = angular.element(elem[0].querySelector('.table-entry-data tbody>tr:nth-child('+nth+')'))[0];
					var rH = row.clientHeight;
					var rO = row.offsetTop;
					var sT = child.scrollTop;
					var dy = (rO-sT)>rH?1:-1;

					goTo = sT + (rH*dy);

				}

				return new Promise(function(resolve,reject){
					(function scrollTo(element, to, duration) {

					    var start = element.scrollTop,
					        change = to - start,
					        currentTime = 0,
					        increment = 20;

					    var animateScroll = function(){
					        currentTime += increment;
					        var val = easeInOutQuad(currentTime, start, change, duration);
					        element.scrollTop = val;
					        if(currentTime < duration) {
					            setTimeout(animateScroll, increment);
					        }else{
					        	resolve();
					        }
					    };
					    animateScroll();
					    function easeInOutQuad(t, b, c, d) {
						  t /= d/2;
							if (t < 1) return c/2*t*t + b;
							t--;
							return -c/2 * (t*(t-2) - 1) + b;
						};
					})(child,goTo,300);
				});

			}

		};
		return aTable;
	}]);
	app.register.filter('typeof', function() {
	  return function(obj) {
	    return typeof obj
	  };
	});
});<|endoftext|>


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

let path = require('path');
let fs = require('fs');
let existsSync = fs.existsSync;
let utils = require('utilities');

// Files like jakelib/foobar.jake.js
const JAKELIB_FILE_PAT = /\.jake(\.js)?$/;
const IMPLICIT_JAKEFILE_NAMES = [
  'Jakefile',
  'Gulpfile'
];

let Loader = function () {
  // Load a Jakefile, running the code inside -- this may result in
  // tasks getting defined using the old Jake API, e.g.,
  // `task('foo' ['bar', 'baz']);`, or can also auto-create tasks
  // from any functions exported from the file
  function loadFile(filePath) {
    let exported = require(filePath);
    for (let [key, value] of Object.entries(exported)) {
      let t;
      if (typeof value == 'function') {
          t = jake.task(key, value);
          t.description = '(Exported function)';
      }
    }
  }

  function fileExists(name) {
    // Will later `require`, which handles either with or without .js
    if (existsSync(name) || existsSync(name + '.js')) {
      return true;
    }
  }

  // Recursive
  function findImplicitJakefile() {
    let cwd = process.cwd();
    let names = IMPLICIT_JAKEFILE_NAMES;
    let found = null;
    names.some((name) => {
      let n;
      n = name;
      if (fileExists(n)) {
        found = n;
        return true;
      }
      // Check for all-lowercase as well
      n = name.toLowerCase();
      if (fileExists(n)) {
        found = n;
        return true;
      }
    });
    if (found) {
      return found;
    }
    else {
      process.chdir("..");
      // If we've walked all the way up the directory tree,
      // bail out with no result
      if (cwd === process.cwd()) {
        return null;
      }
      return findImplicitJakefile();
    }
  }

  this.loadFile = function (fileSpecified) {
    let jakefile;
    let origCwd = process.cwd();

    if (fileSpecified) {
      if (existsSync(fileSpecified)) {
        jakefile = fileSpecified;
      }
    }
    else {
      jakefile = findImplicitJakefile();
    }

    if (jakefile) {
      loadFile(utils.file.absolutize(jakefile));
      return true;
    }
    else {
      if (!fileSpecified) {
        // Restore the working directory on failure
        process.chdir(origCwd);
      }
      return false;
    }
  };

  this.loadDirectory = function (d) {
    let dirname = d || 'jakelib';
    let dirlist;
    dirname = utils.file.absolutize(dirname);
    if (existsSync(dirname)) {
      dirlist = fs.readdirSync(dirname);
      dirlist.forEach(function (filePath) {
        if (JAKELIB_FILE_PAT.test(filePath)) {
          loadFile(path.join(dirname, filePath));
        }
      });
      return true;
    }

    return false;
  };
};

module.exports.Loader = Loader;
<commit_msg>Load generic JS files in a jakelib<commit_after>/*
 * Jake JavaScript build tool
 * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/

let path = require('path');
let fs = require('fs');
let existsSync = fs.existsSync;
let utils = require('utilities');

// Files like jakelib/foobar.jake.js
const JAKELIB_FILE_PAT = /\.jake$|\.js$/;
const IMPLICIT_JAKEFILE_NAMES = [
  'Jakefile',
  'Gulpfile'
];

let Loader = function () {
  // Load a Jakefile, running the code inside -- this may result in
  // tasks getting defined using the old Jake API, e.g.,
  // `task('foo' ['bar', 'baz']);`, or can also auto-create tasks
  // from any functions exported from the file
  function loadFile(filePath) {
    let exported = require(filePath);
    for (let [key, value] of Object.entries(exported)) {
      let t;
      if (typeof value == 'function') {
          t = jake.task(key, value);
          t.description = '(Exported function)';
      }
    }
  }

  function fileExists(name) {
    // Will later `require`, which handles either with or without .js
    if (existsSync(name) || existsSync(name + '.js')) {
      return true;
    }
  }

  // Recursive
  function findImplicitJakefile() {
    let cwd = process.cwd();
    let names = IMPLICIT_JAKEFILE_NAMES;
    let found = null;
    names.some((name) => {
      let n;
      n = name;
      if (fileExists(n)) {
        found = n;
        return true;
      }
      // Check for all-lowercase as well
      n = name.toLowerCase();
      if (fileExists(n)) {
        found = n;
        return true;
      }
    });
    if (found) {
      return found;
    }
    else {
      process.chdir("..");
      // If we've walked all the way up the directory tree,
      // bail out with no result
      if (cwd === process.cwd()) {
        return null;
      }
      return findImplicitJakefile();
    }
  }

  this.loadFile = function (fileSpecified) {
    let jakefile;
    let origCwd = process.cwd();

    if (fileSpecified) {
      if (existsSync(fileSpecified)) {
        jakefile = fileSpecified;
      }
    }
    else {
      jakefile = findImplicitJakefile();
    }

    if (jakefile) {
      loadFile(utils.file.absolutize(jakefile));
      return true;
    }
    else {
      if (!fileSpecified) {
        // Restore the working directory on failure
        process.chdir(origCwd);
      }
      return false;
    }
  };

  this.loadDirectory = function (d) {
    let dirname = d || 'jakelib';
    let dirlist;
    dirname = utils.file.absolutize(dirname);
    if (existsSync(dirname)) {
      dirlist = fs.readdirSync(dirname);
      dirlist.forEach(function (filePath) {
        if (JAKELIB_FILE_PAT.test(filePath)) {
          loadFile(path.join(dirname, filePath));
        }
      });
      return true;
    }

    return false;
  };
};

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


<commit_before>/* global $ */

(function (window, document, $) {
    'use strict';

    var LiveEditor = function LiveEditor (params) {
        console.log('Live Editor Init...');
        this.init(params);
        console.log('Live Editor Done...');
    };

    LiveEditor.prototype.init = function (params) {
        var self = this;

        this.initVars(params);
        this.buildTabs(params);
        this.buildIframe(params);
        this.buildButtons();
        this.buildModals(params);
        this.buildPanel(params);
        this.initActions();

        this.$editorIframe.on('load', function() {
            self.domOutlineInit();
            self.bindEvents();
            self.$editorIframe.show();
            self.$spinnerContainer.hide();
            self.saveBody(); // before applyJs
            self.dispatchLoadEvent();
            self.applyJs();
            self.codePanelUpdate(); // TO DO - tests js
            console.log('*** iframe fully loaded! ***');
        });

        // $(this.editors[i]).parent().addClass('tab-pane__' + this.device);
    };

    LiveEditor.prototype.buildTabs = function () {
        this.currentTab = this.tabs[0].toLowerCase();

        // TO DO - test js
        new LiveEditorTabs({
            tabs: this.tabs,
            parent: this.$editor
        });

        for(var i=0; i<=this.tabs.length-1; i++) {
            var name = this.tabs[i].toLowerCase();

            this.experiments[name] = {
                'scriptList': this.js > 1 ? [this.js[i]] : [],
                'undoList': [],
                'goalList': []
            }
        }
    };

    LiveEditor.prototype.saveBody = function () {
        this.$iframeBody = this.$editorIframe.contents().find('body').clone();
    };

    LiveEditor.prototype.changeTab = function () {
        var $body = this.$editorIframe.contents().find('body');
        this.domOutline.stop();
        $body.empty();
        $body.replaceWith(this.$iframeBody.clone());
        this.domOutlineInit();
    };

    LiveEditor.prototype.initVars = function (params) {
        this.device = params.device;
        this.tabs = params.tabs;
        this.id = params.editor.replace('#', '');
        this.url = params.url;
        this.js = params.js;
        this.$editor = $(params.editor);
        this.domOutline = null;
        this.experiments = {};

        // if (params.js) {
        //     this.addToScriptList(params.js)
        // }
    };

    LiveEditor.prototype.buildIframe = function (params) {
        this.$editorIframe = $('');
        this.$spinnerContainer = $(' Loading...');

        this.$editorIframe.attr({
            'src': this.url,
            'width': '100%',
            'height': '100%',
            'frameborder': '0',
            'allowfullscreen': ''
        });

        this.$editor.append(this.$spinnerContainer, this.$editorIframe);
        this.$editor.addClass('live-editor');
    };

    LiveEditor.prototype.initActions = function () {
        this.actions = new LiveEditorActions(this);
    };

    LiveEditor.prototype.buildButtons = function () {
        this.$undoButton = $('<button type="button" class="btn btn-default btn-undo">Undo</button>');
        // this.$redoButton = $('<button type="button" class="btn btn-default btn-redo">Redo</button>');

        this.$editor.parent().append(this.$undoButton); //, this.$redoButton);
    };

    LiveEditor.prototype.buildModals = function (params) {
        this.editHtmlModal = new LiveEditorModal({
            editor: this.id,
            data: {
                name: 'edit-html-modal-' + this.id,
                title: 'Edit HTML',
                field: 'textarea',
                hasAceEditor: true
            }
        });

        this.editTextModal = new LiveEditorModal({
            editor: this.id,
            data: {
                name: 'edit-text-modal-' + this.id,
                title: 'Edit Text',
                field: 'textarea'
            }
        });

        new LiveEditorModal({
            editor: this.id,
            data: {
                name: 'edit-classes-modal-' + this.id,
                title: 'Edit Classes',
                field: 'input'
            }
        });

        this.$editHtmlModal = $('#edit-html-modal-' + this.id);
        this.$editTextModal = $('#edit-text-modal-' + this.id);
        this.$editClassesModal = $('#edit-classes-modal-' + this.id);
    };

    LiveEditor.prototype.buildPanel = function () {
        this.codePanel = new LiveEditorCodePanel({
            editorName: this.id,
            appendTo: this.$editor.parent()
        });
    };

    LiveEditor.prototype.applyJs = function () {
        // TO DO - test
        var iframeWindow = this.$editorIframe[0].contentWindow,
            scriptList = this.experiments[this.currentTab].scriptList;
        // We use replace here to guarantee the jquery been used
        // is from iframe's window.

        for (var i=0, len=scriptList.length; i<=len-1; i++) {
            eval(scriptList[i].replace(/\$/g, 'iframeWindow.$'));
        }
    };

    LiveEditor.prototype.dispatchLoadEvent = function () {
        var _event = new CustomEvent('LiveEditorLoaded', {'detail': {}});
        document.dispatchEvent(_event);
    };

    LiveEditor.prototype.domOutlineInit = function () {
        this.domOutline = new DomOutline({
            realtime: true,
            onClick: this.sendEventOnClick,
            elem: this.$editorIframe.contents().find('html body')
        });
        this.domOutline.start();
    };

    LiveEditor.prototype.modalEvents = function () {
        var self = this;

        // Edit HTML
        this.$editHtmlModal.on('shown.bs.modal', function () {
            var current_without_cache = self.$editorIframe.contents().find(self.currentSelected)[0],
                html = current_without_cache.outerHTML;
            self.editHtmlModal.setValue(html);
        });

        // Edit Text
        this.$editTextModal.on('show.bs.modal', function () {
            var current_without_cache = self.$editorIframe.contents().find(self.currentSelected)[0],
                text = current_without_cache.textContent;
            self.editTextModal.setValue(text);
        });

        // Edit Classes
        this.$editClassesModal.on('show.bs.modal', function () {
            var current_without_cache = self.$editorIframe.contents().find(self.currentSelected),
                classes = current_without_cache.attr('class');
            $(this).find('.modal-body input').val(classes);
        });

        this.bindModalSave(this.$editHtmlModal, 'html');
        this.bindModalSave(this.$editTextModal, 'text');
        this.bindModalSave(this.$editClassesModal, 'classes');
    };

    LiveEditor.prototype.bindModalSave = function ($modal, label) {
        var self = this,
            saveId = 'edit-' + label + '-save';

        $modal.find('.save-btn').on('click', function() {
            self.operationInit(saveId);
            $modal.modal('hide');
        });
    };

    LiveEditor.prototype.bindEvents = function () {
        var self = this;

        this.modalEvents();

        document.addEventListener('domOutlineOnClick', function (e) {
            // same event for all editor. Should better this.
            if (self.$editorIframe.contents().find($(e.detail)).length > 0) {
                self.setCurrentElement(self.domOutline.element);
                self.openCurrentMenu();
                self.domOutline.pause();
                self.$editorIframe.contents().find("html *").on("click", function(e) {
                    e.preventDefault();
                    e.stopPropagation();
                    self.unselectElements();
                });
                console.log('dom clicked!');
            }
        }, false);

        document.addEventListener('floatingMenuItemClicked', function (e) {
            if (self.$editor.attr('id') == e.detail.liveEditor) {
                self.operationInit(e.detail.operation);
            }
        }, false);

        this.$editorIframe.contents().find('html').on('click', function(e) {
            self.unselectElements();
        });

        this.$editorIframe.contents().keyup(function(e) {
            if (e.keyCode == 27) { // Esc
                self.unselectElements();
            }
        });

        this.$undoButton.off().on('click', function () {
            if (self.undoList.length) {
                var script = self.undoList.pop();
                self.scriptList.pop();
                self.codePanelUpdate();
                eval(script);
            }
        });

        $('.nav-tabs a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
            self.currentTab = $(e.target).data('name');
            self.changeTab();
            self.applyJs();
            self.codePanelUpdate();
            console.log('tab!');
        });
    };

    LiveEditor.prototype.unselectElements = function () {
        if (this.$currentSelected && this.currentSelected) {
            this.floatingMenu.close();
            this.domOutline.stop();
            this.domOutline.start();
            this.$currentSelected = null;
            this.currentSelected = null;
            this.$editorIframe.contents().find('html *').off('click');
        }
    };

    LiveEditor.prototype.sendEventOnClick = function (e) {
        var _event = new CustomEvent('domOutlineOnClick', {'detail': e});
        document.dispatchEvent(_event);
    };

    LiveEditor.prototype.setCurrentElement = function ($elem) {
        this.currentSelected = this.getElementPath($elem);
        this.$currentSelected = this.$editorIframe.contents().find(this.currentSelected);
        console.log('Current: ' +  this.currentSelected);
    };

    LiveEditor.prototype.getElementPath = function ($elem) {
        if ($elem.length != 1)
            $elem = $($elem);

        var path, node = $elem;
        while (node.length) {
            var realNode = node[0], name = realNode.localName;
            if (!name) break;
            name = name.toLowerCase();

            var parent = node.parent();

            var siblings = parent.children(name);
            if (siblings.length > 1) {
                name += ':eq(' + siblings.index(realNode) + ')';
            }

            path = name + (path ? '>' + path : '');
            node = parent;
        }

        return path;
    };

    LiveEditor.prototype.getCurrentParentsPath = function () {
        var a = this.$currentSelected[0];
        var els = [];
        while (a) {
            els.unshift(a);
            a = a.parentNode;
        }
        return els;
    };

    LiveEditor.prototype.openCurrentMenu = function () {
        if (this.currentSelected) {
            var $DomOutlineBox = this.$editorIframe.contents().find('.DomOutline_box'),
                top = this.$editorIframe.offset().top,
                left = this.$editorIframe.offset().left,
                scrollTop = this.$editorIframe.contents().scrollTop();

            this.floatingMenu = new FloatingMenu({
                elemId: this.$editor.attr('id'),
                appendTo: this.$editor.parent(),
                data: {
                    value: this.$currentSelected.prop('tagName').toLowerCase(),
                    posLeft: {
                        "iframe_left":left,
                        "box_offset_left": $DomOutlineBox.offset().left,
                        "box_width": $DomOutlineBox.width(),
                        "iframe_width": this.$editorIframe.width(),
                    },
                    posTop: {
                        "iframe_top": top,
                        "box_offset_top": $DomOutlineBox.offset().top,
                        "scroll_top": scrollTop,
                        "iframe_height": this.$editorIframe.height(),
                        "box_heigh": $DomOutlineBox.height(),
                    },
                    container: this.containerFormat()
                }
            });

            this.floatingMenu.open();
        } else {
            console.log('No item has been selected...');
        }
    };

    LiveEditor.prototype.containerFormat = function () {
        var pathList = this.getCurrentParentsPath(),
            _list = [];

        for (var i=0; i <= pathList.length - 1; i++) {
            if (pathList[i].tagName) {
                _list.push({
                    value: pathList[i].tagName.toLowerCase(),
                    attrs: {
                        'value': this.getElementPath(pathList[i])
                    }
                });
            }
        }

        return _list;
    };

    LiveEditor.prototype.operationInit = function (operation) {
        if (operation === 'remove') {
            this.actions.currentSelectedRemove();
            this.unselectElements();
        }

        if (operation === 'add-event-click') {
            this.actions.currentSelectedAddEvent('click');
            this.unselectElements();
        }

        if (operation === 'edit-html-save') {
            this.actions.currentSelectedEditHtml();
        }

        if (operation === 'edit-text-save') {
            this.actions.currentSelectedEditText();
        }

        if (operation === 'edit-classes-save') {
            this.actions.currentSelectedEditClasses();
        }

        this.codePanelUpdate();
    };

    LiveEditor.prototype.addToUndoList = function (str) {
        str = str.replace(new RegExp('\t|\n', 'g'), '');
        this.experiments[this.currentTab].undoList.push(str);
    };

    LiveEditor.prototype.addToScriptList = function (str) {
        str = str.replace(new RegExp('\t|\n', 'g'), '');
        this.experiments[this.currentTab].scriptList.push(str);
    };

    LiveEditor.prototype.codePanelUpdate = function () {
        this.codePanel.aceEditor.aceEditor.setValue(this.experiments[this.currentTab].scriptList.join(' '), -1);
    };

    window.LiveEditor = LiveEditor;
})(window, document, $);
<commit_msg>Undo refactor<commit_after>/* global $ */

(function (window, document, $) {
    'use strict';

    var LiveEditor = function LiveEditor (params) {
        console.log('Live Editor Init...');
        this.init(params);
        console.log('Live Editor Done...');
    };

    LiveEditor.prototype.init = function (params) {
        var self = this;

        this.initVars(params);
        this.buildTabs(params);
        this.buildIframe(params);
        this.buildButtons();
        this.buildModals(params);
        this.buildPanel(params);
        this.initActions();

        this.$editorIframe.on('load', function() {
            self.domOutlineInit();
            self.bindEvents();
            self.$editorIframe.show();
            self.$spinnerContainer.hide();
            self.saveBody(); // before applyJs
            self.dispatchLoadEvent();
            self.applyJs();
            self.codePanelUpdate(); // TO DO - tests js
            console.log('*** iframe fully loaded! ***');
        });

        // $(this.editors[i]).parent().addClass('tab-pane__' + this.device);
    };

    LiveEditor.prototype.buildTabs = function () {
        this.currentTab = this.tabs[0].toLowerCase();

        // TO DO - test js
        new LiveEditorTabs({
            tabs: this.tabs,
            parent: this.$editor
        });

        for(var i=0; i<=this.tabs.length-1; i++) {
            var name = this.tabs[i].toLowerCase();

            this.experiments[name] = {
                'scriptList': this.js > 1 ? [this.js[i]] : [],
                'undoList': [],
                'goalList': []
            }
        }
    };

    LiveEditor.prototype.saveBody = function () {
        this.$iframeBody = this.$editorIframe.contents().find('body').clone();
    };

    LiveEditor.prototype.changeTab = function () {
        var $body = this.$editorIframe.contents().find('body');
        this.domOutline.stop();
        $body.empty();
        $body.replaceWith(this.$iframeBody.clone());
        this.domOutlineInit();
    };

    LiveEditor.prototype.initVars = function (params) {
        this.device = params.device;
        this.tabs = params.tabs;
        this.id = params.editor.replace('#', '');
        this.url = params.url;
        this.js = params.js;
        this.$editor = $(params.editor);
        this.domOutline = null;
        this.experiments = {};

        // if (params.js) {
        //     this.addToScriptList(params.js)
        // }
    };

    LiveEditor.prototype.buildIframe = function (params) {
        this.$editorIframe = $('');
        this.$spinnerContainer = $(' Loading...');

        this.$editorIframe.attr({
            'src': this.url,
            'width': '100%',
            'height': '100%',
            'frameborder': '0',
            'allowfullscreen': ''
        });

        this.$editor.append(this.$spinnerContainer, this.$editorIframe);
        this.$editor.addClass('live-editor');
    };

    LiveEditor.prototype.initActions = function () {
        this.actions = new LiveEditorActions(this);
    };

    LiveEditor.prototype.buildButtons = function () {
        this.$undoButton = $('<button type="button" class="btn btn-default btn-undo">Undo</button>');
        // this.$redoButton = $('<button type="button" class="btn btn-default btn-redo">Redo</button>');

        this.$editor.parent().append(this.$undoButton); //, this.$redoButton);
    };

    LiveEditor.prototype.buildModals = function (params) {
        this.editHtmlModal = new LiveEditorModal({
            editor: this.id,
            data: {
                name: 'edit-html-modal-' + this.id,
                title: 'Edit HTML',
                field: 'textarea',
                hasAceEditor: true
            }
        });

        this.editTextModal = new LiveEditorModal({
            editor: this.id,
            data: {
                name: 'edit-text-modal-' + this.id,
                title: 'Edit Text',
                field: 'textarea'
            }
        });

        new LiveEditorModal({
            editor: this.id,
            data: {
                name: 'edit-classes-modal-' + this.id,
                title: 'Edit Classes',
                field: 'input'
            }
        });

        this.$editHtmlModal = $('#edit-html-modal-' + this.id);
        this.$editTextModal = $('#edit-text-modal-' + this.id);
        this.$editClassesModal = $('#edit-classes-modal-' + this.id);
    };

    LiveEditor.prototype.buildPanel = function () {
        this.codePanel = new LiveEditorCodePanel({
            editorName: this.id,
            appendTo: this.$editor.parent()
        });
    };

    LiveEditor.prototype.applyJs = function () {
        // TO DO - test
        var iframeWindow = this.$editorIframe[0].contentWindow,
            scriptList = this.experiments[this.currentTab].scriptList;
        // We use replace here to guarantee the jquery been used
        // is from iframe's window.

        for (var i=0, len=scriptList.length; i<=len-1; i++) {
            eval(scriptList[i].replace(/\$/g, 'iframeWindow.$'));
        }
    };

    LiveEditor.prototype.dispatchLoadEvent = function () {
        var _event = new CustomEvent('LiveEditorLoaded', {'detail': {}});
        document.dispatchEvent(_event);
    };

    LiveEditor.prototype.domOutlineInit = function () {
        this.domOutline = new DomOutline({
            realtime: true,
            onClick: this.sendEventOnClick,
            elem: this.$editorIframe.contents().find('html body')
        });
        this.domOutline.start();
    };

    LiveEditor.prototype.modalEvents = function () {
        var self = this;

        // Edit HTML
        this.$editHtmlModal.on('shown.bs.modal', function () {
            var current_without_cache = self.$editorIframe.contents().find(self.currentSelected)[0],
                html = current_without_cache.outerHTML;
            self.editHtmlModal.setValue(html);
        });

        // Edit Text
        this.$editTextModal.on('show.bs.modal', function () {
            var current_without_cache = self.$editorIframe.contents().find(self.currentSelected)[0],
                text = current_without_cache.textContent;
            self.editTextModal.setValue(text);
        });

        // Edit Classes
        this.$editClassesModal.on('show.bs.modal', function () {
            var current_without_cache = self.$editorIframe.contents().find(self.currentSelected),
                classes = current_without_cache.attr('class');
            $(this).find('.modal-body input').val(classes);
        });

        this.bindModalSave(this.$editHtmlModal, 'html');
        this.bindModalSave(this.$editTextModal, 'text');
        this.bindModalSave(this.$editClassesModal, 'classes');
    };

    LiveEditor.prototype.bindModalSave = function ($modal, label) {
        var self = this,
            saveId = 'edit-' + label + '-save';

        $modal.find('.save-btn').on('click', function() {
            self.operationInit(saveId);
            $modal.modal('hide');
        });
    };

    LiveEditor.prototype.bindEvents = function () {
        var self = this;

        this.modalEvents();

        document.addEventListener('domOutlineOnClick', function (e) {
            // same event for all editor. Should better this.
            if (self.$editorIframe.contents().find($(e.detail)).length > 0) {
                self.setCurrentElement(self.domOutline.element);
                self.openCurrentMenu();
                self.domOutline.pause();
                self.$editorIframe.contents().find("html *").on("click", function(e) {
                    e.preventDefault();
                    e.stopPropagation();
                    self.unselectElements();
                });
                console.log('dom clicked!');
            }
        }, false);

        document.addEventListener('floatingMenuItemClicked', function (e) {
            if (self.$editor.attr('id') == e.detail.liveEditor) {
                self.operationInit(e.detail.operation);
            }
        }, false);

        this.$editorIframe.contents().find('html').on('click', function(e) {
            self.unselectElements();
        });

        this.$editorIframe.contents().keyup(function(e) {
            if (e.keyCode == 27) { // Esc
                self.unselectElements();
            }
        });

        this.$undoButton.off().on('click', function () {
            if (self.experiments[self.currentTab].undoList.length) {
                var script = self.experiments[self.currentTab].undoList.pop();
                self.experiments[self.currentTab].scriptList.pop();
                self.codePanelUpdate();
                eval(script);
            }
        });

        $('.nav-tabs a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
            self.currentTab = $(e.target).data('name');
            self.changeTab();
            self.applyJs();
            self.codePanelUpdate();
            console.log('tab!');
        });
    };

    LiveEditor.prototype.unselectElements = function () {
        if (this.$currentSelected && this.currentSelected) {
            this.floatingMenu.close();
            this.domOutline.stop();
            this.domOutline.start();
            this.$currentSelected = null;
            this.currentSelected = null;
            this.$editorIframe.contents().find('html *').off('click');
        }
    };

    LiveEditor.prototype.sendEventOnClick = function (e) {
        var _event = new CustomEvent('domOutlineOnClick', {'detail': e});
        document.dispatchEvent(_event);
    };

    LiveEditor.prototype.setCurrentElement = function ($elem) {
        this.currentSelected = this.getElementPath($elem);
        this.$currentSelected = this.$editorIframe.contents().find(this.currentSelected);
        console.log('Current: ' +  this.currentSelected);
    };

    LiveEditor.prototype.getElementPath = function ($elem) {
        if ($elem.length != 1)
            $elem = $($elem);

        var path, node = $elem;
        while (node.length) {
            var realNode = node[0], name = realNode.localName;
            if (!name) break;
            name = name.toLowerCase();

            var parent = node.parent();

            var siblings = parent.children(name);
            if (siblings.length > 1) {
                name += ':eq(' + siblings.index(realNode) + ')';
            }

            path = name + (path ? '>' + path : '');
            node = parent;
        }

        return path;
    };

    LiveEditor.prototype.getCurrentParentsPath = function () {
        var a = this.$currentSelected[0];
        var els = [];
        while (a) {
            els.unshift(a);
            a = a.parentNode;
        }
        return els;
    };

    LiveEditor.prototype.openCurrentMenu = function () {
        if (this.currentSelected) {
            var $DomOutlineBox = this.$editorIframe.contents().find('.DomOutline_box'),
                top = this.$editorIframe.offset().top,
                left = this.$editorIframe.offset().left,
                scrollTop = this.$editorIframe.contents().scrollTop();

            this.floatingMenu = new FloatingMenu({
                elemId: this.$editor.attr('id'),
                appendTo: this.$editor.parent(),
                data: {
                    value: this.$currentSelected.prop('tagName').toLowerCase(),
                    posLeft: {
                        "iframe_left":left,
                        "box_offset_left": $DomOutlineBox.offset().left,
                        "box_width": $DomOutlineBox.width(),
                        "iframe_width": this.$editorIframe.width(),
                    },
                    posTop: {
                        "iframe_top": top,
                        "box_offset_top": $DomOutlineBox.offset().top,
                        "scroll_top": scrollTop,
                        "iframe_height": this.$editorIframe.height(),
                        "box_heigh": $DomOutlineBox.height(),
                    },
                    container: this.containerFormat()
                }
            });

            this.floatingMenu.open();
        } else {
            console.log('No item has been selected...');
        }
    };

    LiveEditor.prototype.containerFormat = function () {
        var pathList = this.getCurrentParentsPath(),
            _list = [];

        for (var i=0; i <= pathList.length - 1; i++) {
            if (pathList[i].tagName) {
                _list.push({
                    value: pathList[i].tagName.toLowerCase(),
                    attrs: {
                        'value': this.getElementPath(pathList[i])
                    }
                });
            }
        }

        return _list;
    };

    LiveEditor.prototype.operationInit = function (operation) {
        if (operation === 'remove') {
            this.actions.currentSelectedRemove();
            this.unselectElements();
        }

        if (operation === 'add-event-click') {
            this.actions.currentSelectedAddEvent('click');
            this.unselectElements();
        }

        if (operation === 'edit-html-save') {
            this.actions.currentSelectedEditHtml();
        }

        if (operation === 'edit-text-save') {
            this.actions.currentSelectedEditText();
        }

        if (operation === 'edit-classes-save') {
            this.actions.currentSelectedEditClasses();
        }

        this.codePanelUpdate();
    };

    LiveEditor.prototype.addToUndoList = function (str) {
        str = str.replace(new RegExp('\t|\n', 'g'), '');
        this.experiments[this.currentTab].undoList.push(str);
    };

    LiveEditor.prototype.addToScriptList = function (str) {
        str = str.replace(new RegExp('\t|\n', 'g'), '');
        this.experiments[this.currentTab].scriptList.push(str);
    };

    LiveEditor.prototype.codePanelUpdate = function () {
        this.codePanel.aceEditor.aceEditor.setValue(this.experiments[this.currentTab].scriptList.join(' '), -1);
    };

    window.LiveEditor = LiveEditor;
})(window, document, $);
<|endoftext|>


<commit_before>$(function() {

    var timer;
    function showThumbnails($img, thumbnails) {
        $img.data('orig', $img.attr('src'));
        var index = 0;
        timer = setInterval(function() {
            $img.attr('src', thumbnails[index % thumbnails.length]);
            index++;
        }, 500);
    }

    var fetched = {};
    var stillover = false;
    $('#content').on('mouseover', 'img[data-eventid]', function() {
        var img = $(this);
        var eventid = img.data('eventid');
        if (fetched[eventid]) {
            showThumbnails(img, fetched[eventid]);
        } else {
            var data = {
                id: eventid,
                width: img.attr('width'),
                height: img.attr('height'),
            };
            $.getJSON($('#content').data('thumbnails-url'), data)
            .then(function(response) {
                if (response.thumbnails) {
                    fetched[eventid] = response.thumbnails;
                    if (stillover) {
                        showThumbnails(img, response.thumbnails);
                    }
                }
            })
            .fail(function() {
                console.error.apply(console, arguments);
            });
        }
        stillover = true;
    });

    $('#content').on('mouseout', 'img[data-eventid]', function() {
        stillover = false;
        var img = $(this);
        if (img.data('orig') && img.attr('src') !== img.data('orig')) {
            img.attr('src', img.data('orig'));
        }
        if (timer) {
            clearInterval(timer);
        }
    })

});
<commit_msg>bug 1192444 - don't start showThumbnails() if there are none<commit_after>$(function() {

    var timer;
    function showThumbnails($img, thumbnails) {
        $img.data('orig', $img.attr('src'));
        var index = 0;
        timer = setInterval(function() {
            $img.attr('src', thumbnails[index % thumbnails.length]);
            index++;
        }, 500);
    }

    var fetched = {};
    var stillover = false;
    $('#content').on('mouseover', 'img[data-eventid]', function() {
        var img = $(this);
        var eventid = img.data('eventid');
        if (fetched[eventid]) {
            showThumbnails(img, fetched[eventid]);
        } else {
            var data = {
                id: eventid,
                width: img.attr('width'),
                height: img.attr('height'),
            };
            $.getJSON($('#content').data('thumbnails-url'), data)
            .then(function(response) {
                if (response.thumbnails.length) {
                    fetched[eventid] = response.thumbnails;
                    if (stillover) {
                        showThumbnails(img, response.thumbnails);
                    }
                }
            })
            .fail(function() {
                console.error.apply(console, arguments);
            });
        }
        stillover = true;
    });

    $('#content').on('mouseout', 'img[data-eventid]', function() {
        stillover = false;
        var img = $(this);
        if (img.data('orig') && img.attr('src') !== img.data('orig')) {
            img.attr('src', img.data('orig'));
        }
        if (timer) {
            clearInterval(timer);
        }
    });

});
<|endoftext|>