<commit_before>#!/usr/bin/env node

module.exports = function(prompts, lineCallback) {
    var initReadline = function() {
        var self = this;
        var jTimer = null;
        var num_re = /\d/;
        var infty = 9999999;

        self.line = "";
        self.cmdStack = "";
        self.insertMode = false;

        // position of cursor in self.line
        // does NOT represent actual position of terminal cursor
        self.cursorPos = 0;

        var getPrompt = function() {
            if(self.insertMode) {
                return prompts.insertPrompt;
            } else {
                return prompts.normalPrompt;
            }
        };

        var getPromptLen = function() {
            if(self.insertMode) {
                return prompts.insertPromptLen;
            } else {
                return prompts.normalPromptLen;
            }
        };

        self.redraw = function() {
            promptClear();

            var fullLine = getPrompt() + self.line;

            // left bound at cursor pos - half terminal width
            var lb = Math.floor((getPromptLen() + self.cursorPos) -
                                process.stdout.columns / 2);
            // is non negative
            lb = Math.max(0, lb);
            // is not further right than line length - terminal width
            lb = Math.min(lb, fullLine.length - process.stdout.columns);
            // is still non negative
            lb = Math.max(0, lb);

            // right bound
            var rb = lb + process.stdout.columns;

            var line = fullLine.substring(lb, rb);

            realCursorReset();
            process.stdout.write(line);
            realCursorToPos(getPromptLen() + self.cursorPos - lb);
            //console.log();
            //console.log(lb, rb, self.cursorPos, process.stdout.columns);
        };

        var cursorLeft = function(cnt) {
            // if we're at beginning of line, no point going further
            if(self.cursorPos === 0)
                return;

            // negative or zero numbers not accepted here
            cnt = Math.max(1, cnt);

            // dont go past beginning of line
            cnt = Math.min(self.cursorPos, cnt);
            //process.stdout.write('\033[' + cnt + 'D'); // move cursor left
            self.cursorPos = Math.max(0, self.cursorPos - cnt);

            self.redraw();
        };

        var cursorRight = function(cnt) {
            // if we're at end of line, no point going further
            if(self.cursorPos === self.line.length)
                return;

            // negative or zero numbers not accepted here
            cnt = Math.max(1, cnt);

            // don't go past end of line
            cnt = Math.min(cnt, self.line.length - self.cursorPos);
            //process.stdout.write('\033[' + cnt + 'C'); // move cursor right
            self.cursorPos = Math.min(self.line.length, self.cursorPos + cnt);

            self.redraw();
        };

        // resets terminal cursor to lower left
        // WARNING: does not change cursorPos
        var realCursorReset = function() {
            process.stdout.write('\033[' + process.stdout.rows + ';0f');
        };

        var realCursorToPos = function(pos) {
            realCursorReset();
            process.stdout.write('\033[' + pos + 'C'); // move real cursor right
        };

        var promptClear = function() {
            realCursorReset();
            process.stdout.write(Array(process.stdout.columns + 1 - getPromptLen()).join(' '));
        };

        var onquit = function() {
            process.exit();
        };

        var flushCmdStack = function() {
            self.cmdStack = "";
        };

        var charIdInString = function(s, c, numSkips) {
            for (var i = 0; i < s.length; i++) {
                if(s[i] === c) {
                    if(s[i+1] === c) // skip consequetive
                        continue;

                    if(!numSkips)
                        return i;
                    else
                        numSkips--;
                }
            }

            return infty;
        };

        // returns false on error and true if motion is undecided,
        // movement otherwise (negative if cursor should move left)
        var getMovement = function(cmd) {
            // no motion given
            if(!cmd) {
                return true;
            }

            // valid motion is: [cnt] motion [args]
            // find first non-digit character
            var motionPos = cmd.search(/\D/);
            var cnt = cmd.substr(0, motionPos);
            var motion = cmd.substr(motionPos, 1);
            var args = cmd.substr(motionPos + 1);

            if (cnt === 0) {
                return 0;
            }
            else if (motion === 'f') {
                // motion is valid, but we need args
                if(!args)
                    return true;

                // TODO: cnt
                var tempLine = self.line.substr(self.cursorPos);

                var numChars = tempLine.indexOf(args);

                if (numChars > 0)
                    return numChars;
            }
            else if (motion === 'F') {
                // motion is valid, but we need args
                if(!args)
                    return true;

                // TODO: cnt
                var tempLine = self.line.substr(0, self.cursorPos);
                tempLine = tempLine.split("").reverse().join("");

                var numChars = tempLine.indexOf(chr);

                if (numChars > -1)
                    return -numChars - 1;
            }
            else if (motion === 'h') {
                return -(cnt || 1);
            }
            else if (motion === 'l') {
                return cnt || 1;
            }
            else if (motion === 'W' || motion === 'w') {
                // TODO: cnt
                var tempLine = self.line.substr(self.cursorPos);

                if(cnt)
                    cnt -= 1;

                var numChars = charIdInString(tempLine, ' ', cnt) + 1;

                if(numChars)
                    return numChars;
            }
            else if (motion === 'B' || motion === 'b') {
                // TODO: cnt
                var tempLine = self.line.substr(0, self.cursorPos - 1);
                tempLine = tempLine.split("").reverse().join("");

                var numChars = 0;

                while(tempLine[0] === ' ') {
                    numChars++;
                    tempLine = tempLine.substr(1);
                }

                numChars += tempLine.indexOf(' ') + 1;

                if(tempLine.indexOf(' ') === -1)
                    numChars = infty;

                return -numChars;
            }
            else if (motion === '$') {
                return infty;
            }
            else if (motion === '0' || motion === '^') {
                return -infty;
            }

            // not a valid motion, let the cmd stack be cleared
            return false;
        };

        var parseCmdStack = function() {
            var cmd = self.cmdStack[0];

            // quit
            if(cmd === 'q') {
                onquit();
                flushCmdStack();
            }

            // replace char
            else if (cmd === 'r') {
                var start = self.line.substr(0, self.cursorPos);
                var end = self.line.substr(self.cursorPos + 1);
                var chr = self.cmdStack[1];

                self.line = start + chr + end;

                flushCmdStack();
                self.redraw();
            }

            // enter insert mode
            else if (cmd === 'i') {
                self.gotoInsertMode();
                flushCmdStack();
            }
            else if (cmd === 'I') {
                cursorLeft(infty);
                self.gotoInsertMode();
                flushCmdStack();
            }
            else if (cmd === 'a') {
                cursorRight(1);
                self.gotoInsertMode();
                flushCmdStack();
            }
            else if (cmd === 'A') {
                cursorRight(infty);
                self.gotoInsertMode();
                flushCmdStack();
            }
            else if (cmd === 'c') {
                var movement = getMovement(self.cmdStack.substr(1));

                if(movement === false) {
                    // invalid motion, flush cmd stack
                    flushCmdStack();
                } else if (movement === true) {
                    return;
                } else {
                    if(movement > 0) {
                        self.line = self.line.slice(0, self.cursorPos) +
                                    self.line.slice(self.cursorPos - 1 + movement);
                    } else if (movement < 0) {
                        self.line = self.line.slice(0, self.cursorPos + movement) +
                                    self.line.slice(self.cursorPos);
                        cursorLeft(-movement);
                    }

                    self.gotoInsertMode();
                    flushCmdStack();
                }
            }
            /*
            else if (cmd === 'd') {
            }
            */

            // motion
            else {
                var movement = getMovement(self.cmdStack);

                if(movement === false) {
                    // invalid motion, flush cmd stack
                    flushCmdStack();
                } else if (movement === true) {
                    return;
                } else if(movement > 0) {
                    cursorRight(movement);
                    flushCmdStack();
                } else if(movement < 0) {
                    cursorLeft(-movement);
                    flushCmdStack();
                }
            }
        };

        self.gotoNormalMode = function() {
            self.insertMode = false;
            cursorLeft(1);

            self.redraw();
        };

        self.gotoInsertMode = function() {
            self.insertMode = true;

            self.redraw();
        };

        var insertAtCursorPos = function(input) {
            var start = self.line.substr(0, self.cursorPos);
            var end = self.line.substr(self.cursorPos);

            self.line = start + input[0] + end;
            cursorRight(1);
            self.redraw();
        };

        var parseInsertCmd = function(input) {
            // escape
            if (input === '1b') {
                self.gotoNormalMode();
                return false;
            }

            // backspace
            else if (input === '7f') {
                self.line = self.line.slice(0, self.cursorPos - 1) +
                            self.line.slice(self.cursorPos);
                cursorLeft(1);
                self.redraw();
                return false;
            }

            // 'jj' works as escape
            else if (input === '6a') {
                var curTime = new Date();

                if (jTimer) {
                    self.gotoNormalMode();
                    clearTimeout(jTimer);
                    jTimer = null;
                } else {
                    jTimer = setTimeout(function() {
                        insertAtCursorPos('j');
                        jTimer = null;
                    }, 500);
                }

                return false;
            }

            return true;
        };

        self.redraw();

        process.stdin.setRawMode(true);

        process.stdin.on('readable', function() {
            var input = process.stdin.read();
            //console.log(input.toString('hex'));

            if(input) {
                // return ("enter key")
                if(input.toString('hex') === '0d') {
                    lineCallback(self.line);

                    // reset state
                    self.line = "";
                    self.cursorPos = 0;
                    clearTimeout(jTimer);
                    jTimer = null;
                    flushCmdStack();

                    self.redraw();
                }

                else if(self.insertMode) {
                    if(parseInsertCmd(input.toString('hex'))) {
                        insertAtCursorPos(input.toString('utf8'));
                    }
                } else {
                    self.cmdStack += input.toString('utf8');
                    parseCmdStack();
                }
            }
        });
    }

    return new initReadline();
};
<commit_msg>handle .search returning -1 in getMovement<commit_after>#!/usr/bin/env node

module.exports = function(prompts, lineCallback) {
    var initReadline = function() {
        var self = this;
        var jTimer = null;
        var num_re = /\d/;
        var infty = 9999999;

        self.line = "";
        self.cmdStack = "";
        self.insertMode = false;

        // position of cursor in self.line
        // does NOT represent actual position of terminal cursor
        self.cursorPos = 0;

        var getPrompt = function() {
            if(self.insertMode) {
                return prompts.insertPrompt;
            } else {
                return prompts.normalPrompt;
            }
        };

        var getPromptLen = function() {
            if(self.insertMode) {
                return prompts.insertPromptLen;
            } else {
                return prompts.normalPromptLen;
            }
        };

        self.redraw = function() {
            promptClear();

            var fullLine = getPrompt() + self.line;

            // left bound at cursor pos - half terminal width
            var lb = Math.floor((getPromptLen() + self.cursorPos) -
                                process.stdout.columns / 2);
            // is non negative
            lb = Math.max(0, lb);
            // is not further right than line length - terminal width
            lb = Math.min(lb, fullLine.length - process.stdout.columns);
            // is still non negative
            lb = Math.max(0, lb);

            // right bound
            var rb = lb + process.stdout.columns;

            var line = fullLine.substring(lb, rb);

            realCursorReset();
            process.stdout.write(line);
            realCursorToPos(getPromptLen() + self.cursorPos - lb);
            //console.log();
            //console.log(lb, rb, self.cursorPos, process.stdout.columns);
        };

        var cursorLeft = function(cnt) {
            // if we're at beginning of line, no point going further
            if(self.cursorPos === 0)
                return;

            // negative or zero numbers not accepted here
            cnt = Math.max(1, cnt);

            // dont go past beginning of line
            cnt = Math.min(self.cursorPos, cnt);
            //process.stdout.write('\033[' + cnt + 'D'); // move cursor left
            self.cursorPos = Math.max(0, self.cursorPos - cnt);

            self.redraw();
        };

        var cursorRight = function(cnt) {
            // if we're at end of line, no point going further
            if(self.cursorPos === self.line.length)
                return;

            // negative or zero numbers not accepted here
            cnt = Math.max(1, cnt);

            // don't go past end of line
            cnt = Math.min(cnt, self.line.length - self.cursorPos);
            //process.stdout.write('\033[' + cnt + 'C'); // move cursor right
            self.cursorPos = Math.min(self.line.length, self.cursorPos + cnt);

            self.redraw();
        };

        // resets terminal cursor to lower left
        // WARNING: does not change cursorPos
        var realCursorReset = function() {
            process.stdout.write('\033[' + process.stdout.rows + ';0f');
        };

        var realCursorToPos = function(pos) {
            realCursorReset();
            process.stdout.write('\033[' + pos + 'C'); // move real cursor right
        };

        var promptClear = function() {
            realCursorReset();
            process.stdout.write(Array(process.stdout.columns + 1 - getPromptLen()).join(' '));
        };

        var onquit = function() {
            process.exit();
        };

        var flushCmdStack = function() {
            self.cmdStack = "";
        };

        var charIdInString = function(s, c, numSkips) {
            for (var i = 0; i < s.length; i++) {
                if(s[i] === c) {
                    if(s[i+1] === c) // skip consequetive
                        continue;

                    if(!numSkips)
                        return i;
                    else
                        numSkips--;
                }
            }

            return infty;
        };

        // returns false on error and true if motion is undecided,
        // movement otherwise (negative if cursor should move left)
        var getMovement = function(cmd) {
            // valid motion is: [cnt] motion [args]
            // find first non-digit character
            var motionPos = cmd.search(/\D/);
            // no motion yet given
            if(motionPos === -1)
                return true;

            var cnt = cmd.substr(0, motionPos);
            var motion = cmd.substr(motionPos, 1);
            var args = cmd.substr(motionPos + 1);

            if (cnt === 0) {
                return 0;
            }
            else if (motion === 'f') {
                // motion is valid, but we need args
                if(!args)
                    return true;

                // TODO: cnt
                var tempLine = self.line.substr(self.cursorPos);

                var numChars = tempLine.indexOf(args);

                if (numChars > 0)
                    return numChars;
            }
            else if (motion === 'F') {
                // motion is valid, but we need args
                if(!args)
                    return true;

                // TODO: cnt
                var tempLine = self.line.substr(0, self.cursorPos);
                tempLine = tempLine.split("").reverse().join("");

                var numChars = tempLine.indexOf(chr);

                if (numChars > -1)
                    return -numChars - 1;
            }
            else if (motion === 'h') {
                return -(cnt || 1);
            }
            else if (motion === 'l') {
                return cnt || 1;
            }
            else if (motion === 'W' || motion === 'w') {
                // TODO: cnt
                var tempLine = self.line.substr(self.cursorPos);

                if(cnt)
                    cnt -= 1;

                var numChars = charIdInString(tempLine, ' ', cnt) + 1;

                if(numChars)
                    return numChars;
            }
            else if (motion === 'B' || motion === 'b') {
                // TODO: cnt
                var tempLine = self.line.substr(0, self.cursorPos - 1);
                tempLine = tempLine.split("").reverse().join("");

                var numChars = 0;

                while(tempLine[0] === ' ') {
                    numChars++;
                    tempLine = tempLine.substr(1);
                }

                numChars += tempLine.indexOf(' ') + 1;

                if(tempLine.indexOf(' ') === -1)
                    numChars = infty;

                return -numChars;
            }
            else if (motion === '$') {
                return infty;
            }
            else if (motion === '0' || motion === '^') {
                return -infty;
            }

            // not a valid motion, let the cmd stack be cleared
            return false;
        };

        var parseCmdStack = function() {
            var cmd = self.cmdStack[0];

            // quit
            if(cmd === 'q') {
                onquit();
                flushCmdStack();
            }

            // replace char
            else if (cmd === 'r') {
                var start = self.line.substr(0, self.cursorPos);
                var end = self.line.substr(self.cursorPos + 1);
                var chr = self.cmdStack[1];

                self.line = start + chr + end;

                flushCmdStack();
                self.redraw();
            }

            // enter insert mode
            else if (cmd === 'i') {
                self.gotoInsertMode();
                flushCmdStack();
            }
            else if (cmd === 'I') {
                cursorLeft(infty);
                self.gotoInsertMode();
                flushCmdStack();
            }
            else if (cmd === 'a') {
                cursorRight(1);
                self.gotoInsertMode();
                flushCmdStack();
            }
            else if (cmd === 'A') {
                cursorRight(infty);
                self.gotoInsertMode();
                flushCmdStack();
            }
            else if (cmd === 'c') {
                var movement = getMovement(self.cmdStack.substr(1));

                if(movement === false) {
                    // invalid motion, flush cmd stack
                    flushCmdStack();
                } else if (movement === true) {
                    return;
                } else {
                    if(movement > 0) {
                        self.line = self.line.slice(0, self.cursorPos) +
                                    self.line.slice(self.cursorPos - 1 + movement);
                    } else if (movement < 0) {
                        self.line = self.line.slice(0, self.cursorPos + movement) +
                                    self.line.slice(self.cursorPos);
                        cursorLeft(-movement);
                    }

                    self.gotoInsertMode();
                    flushCmdStack();
                }
            }
            /*
            else if (cmd === 'd') {
            }
            */

            // motion
            else {
                var movement = getMovement(self.cmdStack);

                if(movement === false) {
                    // invalid motion, flush cmd stack
                    flushCmdStack();
                } else if (movement === true) {
                    return;
                } else if(movement > 0) {
                    cursorRight(movement);
                    flushCmdStack();
                } else if(movement < 0) {
                    cursorLeft(-movement);
                    flushCmdStack();
                }
            }
        };

        self.gotoNormalMode = function() {
            self.insertMode = false;
            cursorLeft(1);

            self.redraw();
        };

        self.gotoInsertMode = function() {
            self.insertMode = true;

            self.redraw();
        };

        var insertAtCursorPos = function(input) {
            var start = self.line.substr(0, self.cursorPos);
            var end = self.line.substr(self.cursorPos);

            self.line = start + input[0] + end;
            cursorRight(1);
            self.redraw();
        };

        var parseInsertCmd = function(input) {
            // escape
            if (input === '1b') {
                self.gotoNormalMode();
                return false;
            }

            // backspace
            else if (input === '7f') {
                self.line = self.line.slice(0, self.cursorPos - 1) +
                            self.line.slice(self.cursorPos);
                cursorLeft(1);
                self.redraw();
                return false;
            }

            // 'jj' works as escape
            else if (input === '6a') {
                var curTime = new Date();

                if (jTimer) {
                    self.gotoNormalMode();
                    clearTimeout(jTimer);
                    jTimer = null;
                } else {
                    jTimer = setTimeout(function() {
                        insertAtCursorPos('j');
                        jTimer = null;
                    }, 500);
                }

                return false;
            }

            return true;
        };

        self.redraw();

        process.stdin.setRawMode(true);

        process.stdin.on('readable', function() {
            var input = process.stdin.read();
            //console.log(input.toString('hex'));

            if(input) {
                // return ("enter key")
                if(input.toString('hex') === '0d') {
                    lineCallback(self.line);

                    // reset state
                    self.line = "";
                    self.cursorPos = 0;
                    clearTimeout(jTimer);
                    jTimer = null;
                    flushCmdStack();

                    self.redraw();
                }

                else if(self.insertMode) {
                    if(parseInsertCmd(input.toString('hex'))) {
                        insertAtCursorPos(input.toString('utf8'));
                    }
                } else {
                    self.cmdStack += input.toString('utf8');
                    parseCmdStack();
                }
            }
        });
    }

    return new initReadline();
};
<|endoftext|>


<commit_before>var cookieTotal = 0
var clickMultiplier = 1
//const counter = 0
//on click, increase cookieTotal * clickMultiplier
function cookieClick() {
  cookieTotal += (1 * clickMultiplier);
  return cookieTotal
}

function oven() {
  if (cookieTotal < 300) {
    return "There aren't enough cookies to purchase this now." + clickMultiplier
  } else {
    clickMultiplier += 1
    return clickMultiplier
  }
}

document.getElementById('savecookie').onclick = cookieClick;

// use localStorage
// module.exports = {cookieTotal};
// module.exports = {clickMultiplier};
<commit_msg>first draft of oven, grandma functions<commit_after>var cookieTotal = 0
var clickMultiplier = 1
var grandmaTotal = 0
//const counter = 0
//on click, increase cookieTotal * clickMultiplier
function cookieClick() {
  cookieTotal += (1 * clickMultiplier);
  return cookieTotal
}

function oven() {
  if (cookieTotal < 300) {
    return "There aren't enough cookies to purchase this now." + clickMultiplier
  } else {
    cookieTotal - 300
    clickMultiplier += 1
    return clickMultiplier
  }
}

function grandmaPurchase() {
  if (cookieTotal < 300) {
    return "There aren't enough cookies to purchase this now." + clickMultiplier
  } else {
    grandmaTotal += 1
    return grandmaTotal
  }
}

function grandmas() {
  for (i = 0; i === grandmaTotal; i++) {
    cookieClick();
  }
}

document.setInterval( grandmas(), 5000 )

document.getElementById('savecookie').onclick = cookieClick;
document.getElementById('oven').onclick = oven;
document.getElementById('grandma').onclick = grandmaPurchase;

// use localStorage
// module.exports = {cookieTotal};
// module.exports = {clickMultiplier};
<|endoftext|>


<commit_before>import { ref, computed, watch } from '@vue/composition-api'
import Vue from 'vue'
import groupBy from 'lodash/groupBy'
import { BridgeEvents, parse, sortByKey, searchDeepInObject, BridgeSubscriptions, isChrome, openInEditor } from '@vue-devtools/shared-utils'
import { getBridge, useBridge } from '../bridge'
import { useRoute, useRouter } from '@front/util/router'
import { putError } from '../error'

const rootInstances = ref([])
let componentsMap = {}
let componentsParent = {}
const treeFilter = ref('')
const selectedComponentId = ref(null)
const selectedComponentData = ref(null)
const selectedComponentStateFilter = ref('')
let selectedComponentPendingId = null
let lastSelectedApp = null
let lastSelectedComponentId = null
// @TODO auto expand to selected component after target page refresh
let lastSelectedComponentPath = []
const expandedMap = ref({})
let resetComponentsQueued = false

export function useComponentRequests () {
  const { bridge } = useBridge()
  const router = useRouter()

  function requestComponentTree (instanceId = null) {
    if (!instanceId) {
      instanceId = '_root'
    }
    if (instanceId === '_root') {
      resetComponentsQueued = true
    }
    bridge.send(BridgeEvents.TO_BACK_COMPONENT_TREE, {
      instanceId,
      filter: treeFilter.value
    })
  }

  function selectComponent (id, replace = false) {
    if (selectedComponentId.value !== id) {
      router[replace ? 'replace' : 'push']({
        params: {
          componentId: id
        }
      })
    } else {
      loadComponent(id)
    }
  }

  return {
    requestComponentTree,
    selectComponent
  }
}

export function useComponents () {
  const { onBridge, subscribe } = useBridge()
  const route = useRoute()
  const {
    requestComponentTree,
    selectComponent
  } = useComponentRequests()

  watch(treeFilter, () => {
    requestComponentTree()
  })

  watch(() => route.value.params.componentId, value => {
    selectedComponentId.value = value
    loadComponent(value)
  }, {
    immediate: true
  })

  function subscribeToSelectedData () {
    let unsub
    watch(selectedComponentId, value => {
      if (unsub) {
        unsub()
        unsub = null
      }

      if (value != null) {
        unsub = subscribe(BridgeSubscriptions.SELECTED_COMPONENT_DATA, {
          instanceId: value
        })
      }
    }, {
      immediate: true
    })
  }

  // We watch for the tree data so that we can auto load the current selected component
  watch(() => componentsMap, () => {
    if (selectedComponentId.value && selectedComponentPendingId !== selectedComponentId.value && !selectedComponentData.value) {
      selectComponent(selectedComponentId.value)
    }
  }, {
    immediate: true,
    deep: true
  })

  onBridge(BridgeEvents.TO_FRONT_APP_SELECTED, ({ id, lastInspectedComponentId }) => {
    requestComponentTree()
    selectedComponentData.value = null
    if (lastSelectedApp !== null) {
      selectLastComponent()
    }
    lastSelectedApp = id
  })

  // Re-select last selected component when switching back to inspector component tab
  function selectLastComponent () {
    if (lastSelectedComponentId) {
      selectComponent(lastSelectedComponentId, true)
    }
  }

  return {
    rootInstances: computed(() => rootInstances.value),
    treeFilter,
    selectedComponentId: computed(() => selectedComponentId.value),
    requestComponentTree,
    selectComponent,
    selectLastComponent,
    subscribeToSelectedData
  }
}

export function useComponent (instance) {
  const { selectComponent, requestComponentTree } = useComponentRequests()
  const { subscribe } = useBridge()

  const isExpanded = computed(() => !!expandedMap.value[instance.value.id])
  const isExpandedUndefined = computed(() => expandedMap.value[instance.value.id] == null)

  function toggleExpand (load = true) {
    if (!instance.value.hasChildren) return
    setComponentOpen(instance.value.id, !isExpanded.value)
    if (load) {
      requestComponentTree(instance.value.id)
    }
  }

  const isSelected = computed(() => selectedComponentId.value === instance.value.id)

  function select () {
    selectComponent(instance.value.id)
  }

  function subscribeToComponentTree () {
    let unsub
    watch(() => instance.value.id, value => {
      if (unsub) {
        unsub()
        unsub = null
      }

      if (value != null) {
        unsub = subscribe(BridgeSubscriptions.COMPONENT_TREE, {
          instanceId: value
        })
      }
    }, {
      immediate: true
    })
  }

  if (isExpanded.value) {
    requestComponentTree(instance.value.id)
  }

  return {
    isExpanded,
    isExpandedUndefined,
    toggleExpand,
    isSelected,
    select,
    subscribeToComponentTree
  }
}

export function setComponentOpen (id, isOpen) {
  Vue.set(expandedMap.value, id, isOpen)
}

export function useSelectedComponent () {
  const data = computed(() => selectedComponentData.value)
  const state = computed(() => selectedComponentData.value ? groupBy(sortByKey(selectedComponentData.value.state.filter(el => {
    return searchDeepInObject({
      [el.key]: el.value
    }, selectedComponentStateFilter.value)
  })), 'type') : ({}))

  const fileIsPath = computed(() => data.value.file && /[/\\]/.test(data.value.file))

  function inspectDOM () {
    if (!data.value) return
    if (isChrome) {
      getBridge().send(BridgeEvents.TO_BACK_COMPONENT_INSPECT_DOM, { instanceId: data.value.id })
    } else {
      window.alert('DOM inspection is not supported in this shell.')
    }
  }

  function openFile () {
    if (!data.value) return
    openInEditor(data.value.file)
  }

  const { bridge } = useBridge()

  function editState (dotPath, payload) {
    bridge.send(BridgeEvents.TO_BACK_COMPONENT_EDIT_STATE, {
      instanceId: data.value.id,
      dotPath,
      ...payload
    })
  }

  return {
    data,
    state,
    stateFilter: selectedComponentStateFilter,
    inspectDOM,
    fileIsPath,
    openFile,
    editState
  }
}

export function resetComponents () {
  resetComponentsQueued = false
  rootInstances.value = []
  componentsMap = {}
  componentsParent = {}
}

export function setupComponentsBridgeEvents (bridge) {
  selectedComponentPendingId = null
  expandedMap.value = {}

  bridge.on(BridgeEvents.TO_FRONT_COMPONENT_TREE, ({ instanceId, treeData, notFound }) => {
    const isRoot = instanceId.endsWith('root')

    // Reset
    if (resetComponentsQueued) {
      resetComponents()
    }

    // Not supported
    if (!treeData) {
      if (isRoot && !notFound) {
        putError('Component tree not supported')
      }
      return
    }

    // Handle tree data
    const data = parse(treeData)
    const instance = componentsMap[instanceId]
    if (instance) {
      for (const key in data) {
        Vue.set(instance, key, data[key])
      }
      addToComponentsMap(instance)
    } else if (Array.isArray(data)) {
      rootInstances.value = data
      data.forEach(i => addToComponentsMap(i))
    } else {
      rootInstances.value = [data]
      addToComponentsMap(data)
    }

    // Try to load selected component again
    if (isRoot && selectedComponentId.value && !selectedComponentData.value && !selectedComponentPendingId) {
      loadComponent(selectedComponentId.value)
    }
  })

  bridge.on(BridgeEvents.TO_FRONT_COMPONENT_SELECTED_DATA, ({ instanceId, data }) => {
    if (instanceId === selectedComponentId.value) {
      selectedComponentData.value = parse(data)
    }
    if (instanceId === selectedComponentPendingId) {
      selectedComponentPendingId = null
    }
  })

  bridge.on(BridgeEvents.TO_FRONT_COMPONENT_INSPECT_DOM, () => {
    chrome.devtools.inspectedWindow.eval('inspect(window.__VUE_DEVTOOLS_INSPECT_TARGET__)')
  })
}

function addToComponentsMap (instance) {
  componentsMap[instance.id] = instance
  if (instance.children) {
    instance.children.forEach(c => {
      componentsParent[c.id] = instance.id
      addToComponentsMap(c)
    })
  }
}

function loadComponent (id) {
  if (!id || selectedComponentPendingId === id) return
  lastSelectedComponentId = id
  selectedComponentPendingId = id
  lastSelectedComponentPath = getPath(id)
  bridge.send(BridgeEvents.TO_BACK_COMPONENT_SELECTED_DATA, id)
}

function getPath (instanceId) {
  const path = [instanceId]
  const parentId = componentsParent[instanceId]
  if (parentId) {
    path.unshift(...getPath(parentId))
  }
  return path
}
<commit_msg>fix(components): expanding component tree not working<commit_after>import { ref, computed, watch } from '@vue/composition-api'
import Vue from 'vue'
import groupBy from 'lodash/groupBy'
import { BridgeEvents, parse, sortByKey, searchDeepInObject, BridgeSubscriptions, isChrome, openInEditor } from '@vue-devtools/shared-utils'
import { getBridge, useBridge } from '../bridge'
import { useRoute, useRouter } from '@front/util/router'
import { putError } from '../error'

const rootInstances = ref([])
let componentsMap = {}
let componentsParent = {}
const treeFilter = ref('')
const selectedComponentId = ref(null)
const selectedComponentData = ref(null)
const selectedComponentStateFilter = ref('')
let selectedComponentPendingId = null
let lastSelectedApp = null
let lastSelectedComponentId = null
// @TODO auto expand to selected component after target page refresh
let lastSelectedComponentPath = []
const expandedMap = ref({})
let resetComponentsQueued = false

export function useComponentRequests () {
  const { bridge } = useBridge()
  const router = useRouter()

  function requestComponentTree (instanceId = null) {
    if (!instanceId) {
      instanceId = '_root'
    }
    if (instanceId === '_root') {
      resetComponentsQueued = true
    }
    bridge.send(BridgeEvents.TO_BACK_COMPONENT_TREE, {
      instanceId,
      filter: treeFilter.value
    })
  }

  function selectComponent (id, replace = false) {
    if (selectedComponentId.value !== id) {
      router[replace ? 'replace' : 'push']({
        params: {
          componentId: id
        }
      })
    } else {
      loadComponent(id)
    }
  }

  return {
    requestComponentTree,
    selectComponent
  }
}

export function useComponents () {
  const { onBridge, subscribe } = useBridge()
  const route = useRoute()
  const {
    requestComponentTree,
    selectComponent
  } = useComponentRequests()

  watch(treeFilter, () => {
    requestComponentTree()
  })

  watch(() => route.value.params.componentId, value => {
    selectedComponentId.value = value
    loadComponent(value)
  }, {
    immediate: true
  })

  function subscribeToSelectedData () {
    let unsub
    watch(selectedComponentId, value => {
      if (unsub) {
        unsub()
        unsub = null
      }

      if (value != null) {
        unsub = subscribe(BridgeSubscriptions.SELECTED_COMPONENT_DATA, {
          instanceId: value
        })
      }
    }, {
      immediate: true
    })
  }

  // We watch for the tree data so that we can auto load the current selected component
  watch(() => componentsMap, () => {
    if (selectedComponentId.value && selectedComponentPendingId !== selectedComponentId.value && !selectedComponentData.value) {
      selectComponent(selectedComponentId.value)
    }
  }, {
    immediate: true,
    deep: true
  })

  onBridge(BridgeEvents.TO_FRONT_APP_SELECTED, ({ id, lastInspectedComponentId }) => {
    requestComponentTree()
    selectedComponentData.value = null
    if (lastSelectedApp !== null) {
      selectLastComponent()
    }
    lastSelectedApp = id
  })

  // Re-select last selected component when switching back to inspector component tab
  function selectLastComponent () {
    if (lastSelectedComponentId) {
      selectComponent(lastSelectedComponentId, true)
    }
  }

  return {
    rootInstances: computed(() => rootInstances.value),
    treeFilter,
    selectedComponentId: computed(() => selectedComponentId.value),
    requestComponentTree,
    selectComponent,
    selectLastComponent,
    subscribeToSelectedData
  }
}

export function useComponent (instance) {
  const { selectComponent, requestComponentTree } = useComponentRequests()
  const { subscribe } = useBridge()

  const isExpanded = computed(() => !!expandedMap.value[instance.value.id])
  const isExpandedUndefined = computed(() => expandedMap.value[instance.value.id] == null)

  function toggleExpand (load = true) {
    if (!instance.value.hasChildren) return
    setComponentOpen(instance.value.id, !isExpanded.value)
    if (load) {
      requestComponentTree(instance.value.id)
    }
  }

  const isSelected = computed(() => selectedComponentId.value === instance.value.id)

  function select () {
    selectComponent(instance.value.id)
  }

  function subscribeToComponentTree () {
    let unsub
    watch(() => instance.value.id, value => {
      if (unsub) {
        unsub()
        unsub = null
      }

      if (value != null) {
        unsub = subscribe(BridgeSubscriptions.COMPONENT_TREE, {
          instanceId: value
        })
      }
    }, {
      immediate: true
    })
  }

  if (isExpanded.value) {
    requestComponentTree(instance.value.id)
  }

  return {
    isExpanded,
    isExpandedUndefined,
    toggleExpand,
    isSelected,
    select,
    subscribeToComponentTree
  }
}

export function setComponentOpen (id, isOpen) {
  Vue.set(expandedMap.value, id, isOpen)
}

export function useSelectedComponent () {
  const data = computed(() => selectedComponentData.value)
  const state = computed(() => selectedComponentData.value ? groupBy(sortByKey(selectedComponentData.value.state.filter(el => {
    return searchDeepInObject({
      [el.key]: el.value
    }, selectedComponentStateFilter.value)
  })), 'type') : ({}))

  const fileIsPath = computed(() => data.value.file && /[/\\]/.test(data.value.file))

  function inspectDOM () {
    if (!data.value) return
    if (isChrome) {
      getBridge().send(BridgeEvents.TO_BACK_COMPONENT_INSPECT_DOM, { instanceId: data.value.id })
    } else {
      window.alert('DOM inspection is not supported in this shell.')
    }
  }

  function openFile () {
    if (!data.value) return
    openInEditor(data.value.file)
  }

  const { bridge } = useBridge()

  function editState (dotPath, payload) {
    bridge.send(BridgeEvents.TO_BACK_COMPONENT_EDIT_STATE, {
      instanceId: data.value.id,
      dotPath,
      ...payload
    })
  }

  return {
    data,
    state,
    stateFilter: selectedComponentStateFilter,
    inspectDOM,
    fileIsPath,
    openFile,
    editState
  }
}

export function resetComponents () {
  resetComponentsQueued = false
  rootInstances.value = []
  componentsMap = {}
  componentsParent = {}
}

export function setupComponentsBridgeEvents (bridge) {
  selectedComponentPendingId = null
  expandedMap.value = {}

  bridge.on(BridgeEvents.TO_FRONT_COMPONENT_TREE, ({ instanceId, treeData, notFound }) => {
    const isRoot = instanceId.endsWith('root')

    // Reset
    if (resetComponentsQueued) {
      resetComponents()
    }

    // Not supported
    if (!treeData) {
      if (isRoot && !notFound) {
        putError('Component tree not supported')
      }
      return
    }

    // Handle tree data
    const data = parse(treeData)
    console.log('component tree:', data, instanceId)
    const instance = componentsMap[instanceId]
    if (instance) {
      for (const item of data) {
        const component = componentsMap[item.id]
        for (const key in item) {
          Vue.set(component, key, item[key])
        }
        addToComponentsMap(component)
      }
    } else if (Array.isArray(data)) {
      rootInstances.value = data
      data.forEach(i => addToComponentsMap(i))
    }

    // Try to load selected component again
    if (isRoot && selectedComponentId.value && !selectedComponentData.value && !selectedComponentPendingId) {
      loadComponent(selectedComponentId.value)
    }
  })

  bridge.on(BridgeEvents.TO_FRONT_COMPONENT_SELECTED_DATA, ({ instanceId, data }) => {
    if (instanceId === selectedComponentId.value) {
      selectedComponentData.value = parse(data)
    }
    if (instanceId === selectedComponentPendingId) {
      selectedComponentPendingId = null
    }
  })

  bridge.on(BridgeEvents.TO_FRONT_COMPONENT_INSPECT_DOM, () => {
    chrome.devtools.inspectedWindow.eval('inspect(window.__VUE_DEVTOOLS_INSPECT_TARGET__)')
  })
}

function addToComponentsMap (instance) {
  componentsMap[instance.id] = instance
  if (instance.children) {
    instance.children.forEach(c => {
      componentsParent[c.id] = instance.id
      addToComponentsMap(c)
    })
  }
}

function loadComponent (id) {
  if (!id || selectedComponentPendingId === id) return
  lastSelectedComponentId = id
  selectedComponentPendingId = id
  lastSelectedComponentPath = getPath(id)
  bridge.send(BridgeEvents.TO_BACK_COMPONENT_SELECTED_DATA, id)
}

function getPath (instanceId) {
  const path = [instanceId]
  const parentId = componentsParent[instanceId]
  if (parentId) {
    path.unshift(...getPath(parentId))
  }
  return path
}
<|endoftext|>


<commit_before>define([
        'config',
        'mustache',
        'text!views/FindAChurch/Search.html'
        ],
        function(config, Mustache, template){

    return Backbone.View.extend({
        initialize: function(){
            _.bindAll(this, 'location_keyup', 'geocode', 'set_distance_unit_preference',
                'is_distance_unit_preference_set', 'get_distance_units', 'location_keyup')
            window.app.geocoder = new google.maps.Geocoder();
        },
        render: function(){
            this.$el.html(Mustache.render(template))

            // Attach search event handler to search button and text box
            this.listenTo(this.$('.search'), 'click', this.geocode)
            this.listenTo(this.$('.location'), 'keyup', this.location_keyup)
            this.listenTo(this.$('.radius'), 'change', this.geocode)
            this.listenTo(this.$('.units'), 'change', this.set_distance_unit_preference)

            // TODO: Improve User Interface:
            // TODO: - Try to be able to guess which unit of distance (Mi or KM) they prefer based on
            //  some input from the user.
            // TODO:   * Event Handler: On page load

            //If the distance unit preference is set,
			// set the distance units in the form based on what is in the preference.
			if ( this.is_distance_unit_preference_set() ){
				this.$('.units').val(localStorage['units_of_measurement']);
			}
            // TODO:     * Else, on page load, before the person searches,
			else {
				// TODO: guess what units they want based on one of the following:
                //            * First try the users' browser's geolocation information. If
                //                  the user is in one of the countries that use miles, select "miles" in the form
                //                  and save it to the preference.

                var thiz=this

                if (navigator.geolocation){
                    // Center the map on the viewer's country by default
                    navigator.geolocation.getCurrentPosition(function(position){
                        window.app.geocoder = new google.maps.Geocoder();
                        window.app.geocoder.geocode( { 'address': position.coords.latitude + "," + position.coords.longitude}, function(results, status) {
                        //	Underscore.js _.filter() method) for results[0].address_components[x].types.short_name == 'US'
                            if (status == google.maps.GeocoderStatus.OK){
                                // Set the user's location's country's distance units in the form
                                thiz.$('.units').val(thiz.get_distance_units(results))
                            }
                        })
                    })
                }else{
                    console.log("Geolocation is not supported by this browser.");
                    // TODO:   * Next, try the browser country or language setting.
                    var userLang = navigator.language || navigator.browserLanguage || navigator.systemLanguage || navigator.userLanguage;
                    console.log(userLang)
                    // Determine if userLang contains a 2-character country code after a hyphen
                    //  (e.g., userLang could equal 'en-US')
                    var country_code = userLang.split('-')[1]
                    var language_code = userLang.split('-')[0]
                    if (country_code !== '' && typeof country_code !== 'undefined'){
                        // Test if this country code uses miles or kilometers.
                        // TODO: Maybe these codes should be put into one data structure with the array used
                        //  below (["United Kingdom", "Liberia", "Myanmar", "United States"]) so we don't
                        //  complicate future maintenance of the application.
                        var country_codes = ['GB', 'LR', 'MM', 'US']
                        // If country_code is in the list, then
                        if (country_codes.indexOf(country_code) !== -1){
                            // Record preference accordingly
                            localStorage('distance_units', 'miles')
                        }else{
                            localStorage('distance_units', 'km')
                        }
                    }else{
                        // Use the language code.  This might help:  http://download.geonames.org/export/dump/countryInfo.txt
                        // These are the languages of the countries that use miles
                        var languages = ['en', 'mm', 'bur', 'mya']
                        // If the country's language is NOT in this set, then the country uses kilometers.
                        if (languages.indexOf(language_code) === -1){
                            localStorage('distance_units', 'km')
                        }else{
                            // If the country does use this language, then we don't know if it uses miles or kilometers.
                        }
                    }
                    // TODO:   * Get their preference from their IP address
                    // TODO:        Load http://freegeoip.net/json/ via JQuery.get()
                    // TODO:        Extract country code
                    // TODO:        Test if this country code uses miles or kilometers.
                    // TODO:        Record preference accordingly
                    this.create_map({coords:this.default_map_center})
                }

			}
        },
		is_distance_unit_preference_set:function(){
			// Get preference here
			localStorage['units_of_measurement']
			if (typeof localStorage['units_of_measurement'] === 'undefined'){
				return false;
			}else{
				return true
			}
		},
		set_distance_unit_preference:function(){
			//  Set preference here
			localStorage['units_of_measurement'] = this.$('.units').val();

		},
        location_keyup:function(event){
            if (event.which == 13){
                this.geocode(event)
            }
        },
		get_distance_units:function(results){
            var country_names_objects = _.filter(results[0].address_components, function(item){
            	//See if it contains countries that use miles (GB, LR, MM, US)
                return ["United Kingdom", "Liberia", "Myanmar", "United States"].indexOf(item.long_name) !== -1
			});
            country_names = _.chain(country_names_objects)
                .pluck('long_name')
                .filter(function(item){ return typeof item !== 'undefined'; })
                .value()
            if (country_names.length >0){
                output = 'miles'
            }else{
                output = 'km'
            }
            return output;
		},
        geocode: function (event){
            event.preventDefault()
            // If user submitted an address, put that address into this.model
            // Use location user entered

            // Convert radius*units to meters
            var radius = $('.radius').val();
            var units = $('.units').val();
            var distance;
            if (units == 'miles'){
                distance = radius * 1609.344;
            } else if (units == 'km') {
                distance = radius * 1000;
            }

            //  TODO: Start here.
            // TODO: Event handler: On search form submission, record the currently-selected distance unit
            //  in a preference, and record there whether they selected it manually or not (NOTE: this second task
            //  is not done yet).
            // Geocode location
        	var thiz=this
            var location = $('.location').val()
            if (location == ''){
                location = thiz.model.get('location')
            }
            window.app.geocoder.geocode( { 'address': location}, function(results, status) {
                if (status == google.maps.GeocoderStatus.OK) {
                    // Put the above variables into this.model in one action so there's only one change event
                    thiz.model.set({
                        location:location,
                        radius:radius,
                        distance:distance,
                        units:units,
                        results:results
                    })
					if (!thiz.is_distance_unit_preference_set()){
    				    // Try figuring the user's distance_unit preference based on the country in which they are searching.
						var distance_units = thiz.get_distance_units(results)
    					// Set the preference to contain 'miles' or 'km'
						var distance_units = thiz.get_distance_units(results)
						localStorage['units_of_measurement'] = distance_units
                        // Set form to display the distance units of the country in which the user searched
                        thiz.$('.units').val(distance_units)
					}

                    // TODO: Send the request to Google; if Google says the location is ambiguous,
                    //  then use one of the location methods above (geolocation, etc.) to send Google
                    //  a hint about in which country the user is attempting to search.
             		// TODO: Event handler: On search results being returned to the browser from the
                    //  Google Maps API, Google's geocode responses state in which country the searched-for
                    //  location is found, so after they search, you can set their distance units for them
                    //  (in a preference and in the form) based on the country, unless (in a preference you can see)
                    //  they have already selected a distance unit manually.
                } else {
                    alert("Geocode was not successful for the following reason: " + status);
                }
            });
		}
    });
});<commit_msg>Updated comments<commit_after>define([
        'config',
        'mustache',
        'text!views/FindAChurch/Search.html'
        ],
        function(config, Mustache, template){

    return Backbone.View.extend({
        initialize: function(){
            _.bindAll(this, 'location_keyup', 'geocode', 'set_distance_unit_preference',
                'is_distance_unit_preference_set', 'get_distance_units', 'location_keyup')
            window.app.geocoder = new google.maps.Geocoder();
        },
        render: function(){
            this.$el.html(Mustache.render(template))

            // Attach search event handler to search button and text box
            this.listenTo(this.$('.search'), 'click', this.geocode)
            this.listenTo(this.$('.location'), 'keyup', this.location_keyup)
            this.listenTo(this.$('.radius'), 'change', this.geocode)
            this.listenTo(this.$('.units'), 'change', this.set_distance_unit_preference)

            // TODO: Improve User Interface:
            // TODO: - Try to be able to guess which unit of distance (Mi or KM) they prefer based on
            //  some input from the user.
            // TODO:   * Event Handler: On page load

            //If the distance unit preference is set,
			// set the distance units in the form based on what is in the preference.
			if ( this.is_distance_unit_preference_set() ){
				this.$('.units').val(localStorage['units_of_measurement']);
			}
            // TODO:     * Else, on page load, before the person searches,
			else {
				// Guess what units they want based on one of the following:
                //            * First try the users' browser's geolocation information. If
                //                  the user is in one of the countries that use miles, select "miles" in the form
                //                  and save it to the preference.

                var thiz=this

                if (navigator.geolocation){
                    // Center the map on the viewer's country by default
                    navigator.geolocation.getCurrentPosition(function(position){
                        window.app.geocoder = new google.maps.Geocoder();
                        window.app.geocoder.geocode( { 'address': position.coords.latitude + "," + position.coords.longitude}, function(results, status) {
                        //	Underscore.js _.filter() method) for results[0].address_components[x].types.short_name == 'US'
                            if (status == google.maps.GeocoderStatus.OK){
                                // Set the user's location's country's distance units in the form
                                thiz.$('.units').val(thiz.get_distance_units(results))
                            }
                        })
                    })
                }else{
                    console.log("Geolocation is not supported by this browser.");
                    // TODO:   * Next, try the browser country or language setting.
                    var userLang = navigator.language || navigator.browserLanguage || navigator.systemLanguage || navigator.userLanguage;
                    console.log(userLang)
                    // Determine if userLang contains a 2-character country code after a hyphen
                    //  (e.g., userLang could equal 'en-US')
                    var country_code = userLang.split('-')[1]
                    var language_code = userLang.split('-')[0]
                    if (country_code !== '' && typeof country_code !== 'undefined'){
                        // Test if this country code uses miles or kilometers.
                        // TODO: Maybe these codes should be put into one data structure with the array used
                        //  below (["United Kingdom", "Liberia", "Myanmar", "United States"]) so we don't
                        //  complicate future maintenance of the application.
                        var country_codes = ['GB', 'LR', 'MM', 'US']
                        // If country_code is in the list, then
                        if (country_codes.indexOf(country_code) !== -1){
                            // Record preference accordingly
                            localStorage('distance_units', 'miles')
                        }else{
                            localStorage('distance_units', 'km')
                        }
                    }else{
                        // Use the language code.  This might help:  http://download.geonames.org/export/dump/countryInfo.txt
                        // These are the languages of the countries that use miles
                        var languages = ['en', 'mm', 'bur', 'mya']
                        // If the country's language is NOT in this set, then the country uses kilometers.
                        if (languages.indexOf(language_code) === -1){
                            localStorage('distance_units', 'km')
                        }else{
                            // If the country does use this language, then we don't know if it uses miles or kilometers.
                        }
                    }
                    // TODO:   * Get their preference from their IP address
                    // TODO:        Load http://freegeoip.net/json/ via JQuery.get()
                    // TODO:        Extract country code
                    // TODO:        Test if this country code uses miles or kilometers.
                    // TODO:        Record preference accordingly

                    // Create the map
                    this.create_map({coords:this.default_map_center})
                }

			}
        },

		is_distance_unit_preference_set:function(){
			// Get preference here
			localStorage['units_of_measurement']
			if (typeof localStorage['units_of_measurement'] === 'undefined'){
				return false;
			}else{
				return true
			}
		},
		set_distance_unit_preference:function(){
			//  Set preference here
			localStorage['units_of_measurement'] = this.$('.units').val();

		},
        location_keyup:function(event){
            if (event.which == 13){
                this.geocode(event)
            }
        },
		get_distance_units:function(results){
            var country_names_objects = _.filter(results[0].address_components, function(item){
            	//See if it contains countries that use miles (GB, LR, MM, US)
                return ["United Kingdom", "Liberia", "Myanmar", "United States"].indexOf(item.long_name) !== -1
			});
            country_names = _.chain(country_names_objects)
                .pluck('long_name')
                .filter(function(item){ return typeof item !== 'undefined'; })
                .value()
            if (country_names.length >0){
                output = 'miles'
            }else{
                output = 'km'
            }
            return output;
		},
        geocode: function (event){
            event.preventDefault()
            // If user submitted an address, put that address into this.model
            // Use location user entered

            // Convert radius*units to meters
            var radius = $('.radius').val();
            var units = $('.units').val();
            var distance;
            if (units == 'miles'){
                distance = radius * 1609.344;
            } else if (units == 'km') {
                distance = radius * 1000;
            }

            //  TODO: Start here.
            // TODO: Event handler: On search form submission, record the currently-selected distance unit
            //  in a preference, and record there whether they selected it manually or not (NOTE: this second task
            //  is not done yet).
            // Geocode location
        	var thiz=this
            var location = $('.location').val()
            if (location == ''){
                location = thiz.model.get('location')
            }
            window.app.geocoder.geocode( { 'address': location}, function(results, status) {
                if (status == google.maps.GeocoderStatus.OK) {
                    // Put the above variables into this.model in one action so there's only one change event
                    thiz.model.set({
                        location:location,
                        radius:radius,
                        distance:distance,
                        units:units,
                        results:results
                    })
					if (!thiz.is_distance_unit_preference_set()){
    				    // Try figuring the user's distance_unit preference based on the country in which they are searching.
						var distance_units = thiz.get_distance_units(results)
    					// Set the preference to contain 'miles' or 'km'
						var distance_units = thiz.get_distance_units(results)
						localStorage['units_of_measurement'] = distance_units
                        // Set form to display the distance units of the country in which the user searched
                        thiz.$('.units').val(distance_units)
					}

                    // TODO: Send the request to Google; if Google says the location is ambiguous,
                    //  then use one of the location methods above (geolocation, etc.) to send Google
                    //  a hint about in which country the user is attempting to search.
             		// TODO: Event handler: On search results being returned to the browser from the
                    //  Google Maps API, Google's geocode responses state in which country the searched-for
                    //  location is found, so after they search, you can set their distance units for them
                    //  (in a preference and in the form) based on the country, unless (in a preference you can see)
                    //  they have already selected a distance unit manually.
                } else {
                    alert("Geocode was not successful for the following reason: " + status);
                }
            });
		}
    });
});<|endoftext|>


<commit_before>/* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
ABOUT THIS NODE.JS EXAMPLE: This example works with the AWS SDK for JavaScript version 3 (v3),
which is available at https://github.com/aws/aws-sdk-js-v3. This example is in the 'AWS SDK for JavaScript v3 Developer Guide' at
https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/s3-example-creating-buckets.html.

Purpose:
s3_put_presignedURL.js creates a presigned URL to upload a file to an Amazon Simple Storage Service (Amazon S3) bucket.

Note: This example immediately deletes the object and bucket.

Running the code:
nodes3_put_presignedURL.js

[Outputs | Returns]:
Uploads the specified file to the specified bucket.
*/

// snippet-start:[s3.JavaScript.buckets.presignedurlv3]
// Import the required AWS SDK clients and commands for Node.js
import {
  CreateBucketCommand,
  DeleteObjectCommand,
  PutObjectCommand,
  DeleteBucketCommand }
from "@aws-sdk/client-s3";
import { s3Client } from "./libs/s3Client.js"; // Helper function that creates an Amazon S3 service client module.
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import fetch from "node-fetch";

// Set parameters
// Create a random name for the Amazon Simple Storage Service (Amazon S3) bucket and key
export const bucketParams = {
  Bucket: `test-bucket-${Math.ceil(Math.random() * 10 ** 10)}`,
  Key: `test-object-${Math.ceil(Math.random() * 10 ** 10)}`,
  Body: "BODY"
};
export const run = async () => {
  try {
    // Create an S3 bucket.
    console.log(`Creating bucket ${bucketParams.Bucket}`);
    await s3Client.send(new CreateBucketCommand({ Bucket: bucketParams.Bucket }));
    console.log(`Waiting for "${bucketParams.Bucket}" bucket creation...`);
  } catch (err) {
    console.log("Error creating bucket", err);
  }
  try {
    // Create a command to put the object in the S3 bucket.
    const command = new PutObjectCommand(bucketParams);
    // Create the presigned URL.
    const signedUrl = await getSignedUrl(s3Client, command, {
      expiresIn: 3600,
    });
    console.log(
      `\nPutting "${bucketParams.Key}" using signedUrl with body "${bucketParams.Body}" in v3`
    );
    console.log(signedUrl);
    const response = await fetch(signedUrl, {method: 'PUT', body: bucketParams.Body});
    console.log(
      `\nResponse returned by signed URL: ${await response.text()}\n`
    );
  } catch (err) {
    console.log("Error creating presigned URL", err);
  }
  try {
    // Delete the object.
    console.log(`\nDeleting object "${bucketParams.Key}"} from bucket`);
    await s3Client.send(
      new DeleteObjectCommand({ Bucket: bucketParams.Bucket, Key: bucketParams.Key })
    );
  } catch (err) {
    console.log("Error deleting object", err);
  }
  try {
    // Delete the S3 bucket.
    console.log(`\nDeleting bucket ${bucketParams.Bucket}`);
    await s3.send(new DeleteBucketCommand({ Bucket: bucketParams.Bucket }));
  } catch (err) {
    console.log("Error deleting bucket", err);
  }
};
run();
// snippet-end:[s3.JavaScript.buckets.presignedurlv3]
// For unit testing only.
// module.exports ={run, bucketParams};
<commit_msg>Fix wrong variable name<commit_after>/* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
ABOUT THIS NODE.JS EXAMPLE: This example works with the AWS SDK for JavaScript version 3 (v3),
which is available at https://github.com/aws/aws-sdk-js-v3. This example is in the 'AWS SDK for JavaScript v3 Developer Guide' at
https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/s3-example-creating-buckets.html.

Purpose:
s3_put_presignedURL.js creates a presigned URL to upload a file to an Amazon Simple Storage Service (Amazon S3) bucket.

Note: This example immediately deletes the object and bucket.

Running the code:
nodes3_put_presignedURL.js

[Outputs | Returns]:
Uploads the specified file to the specified bucket.
*/

// snippet-start:[s3.JavaScript.buckets.presignedurlv3]
// Import the required AWS SDK clients and commands for Node.js
import {
  CreateBucketCommand,
  DeleteObjectCommand,
  PutObjectCommand,
  DeleteBucketCommand }
from "@aws-sdk/client-s3";
import { s3Client } from "./libs/s3Client.js"; // Helper function that creates an Amazon S3 service client module.
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import fetch from "node-fetch";

// Set parameters
// Create a random name for the Amazon Simple Storage Service (Amazon S3) bucket and key
export const bucketParams = {
  Bucket: `test-bucket-${Math.ceil(Math.random() * 10 ** 10)}`,
  Key: `test-object-${Math.ceil(Math.random() * 10 ** 10)}`,
  Body: "BODY"
};
export const run = async () => {
  try {
    // Create an S3 bucket.
    console.log(`Creating bucket ${bucketParams.Bucket}`);
    await s3Client.send(new CreateBucketCommand({ Bucket: bucketParams.Bucket }));
    console.log(`Waiting for "${bucketParams.Bucket}" bucket creation...`);
  } catch (err) {
    console.log("Error creating bucket", err);
  }
  try {
    // Create a command to put the object in the S3 bucket.
    const command = new PutObjectCommand(bucketParams);
    // Create the presigned URL.
    const signedUrl = await getSignedUrl(s3Client, command, {
      expiresIn: 3600,
    });
    console.log(
      `\nPutting "${bucketParams.Key}" using signedUrl with body "${bucketParams.Body}" in v3`
    );
    console.log(signedUrl);
    const response = await fetch(signedUrl, {method: 'PUT', body: bucketParams.Body});
    console.log(
      `\nResponse returned by signed URL: ${await response.text()}\n`
    );
  } catch (err) {
    console.log("Error creating presigned URL", err);
  }
  try {
    // Delete the object.
    console.log(`\nDeleting object "${bucketParams.Key}"} from bucket`);
    await s3Client.send(
      new DeleteObjectCommand({ Bucket: bucketParams.Bucket, Key: bucketParams.Key })
    );
  } catch (err) {
    console.log("Error deleting object", err);
  }
  try {
    // Delete the S3 bucket.
    console.log(`\nDeleting bucket ${bucketParams.Bucket}`);
    await s3Client.send(
      new DeleteBucketCommand({ Bucket: bucketParams.Bucket })
    );
  } catch (err) {
    console.log("Error deleting bucket", err);
  }
};
run();
// snippet-end:[s3.JavaScript.buckets.presignedurlv3]
// For unit testing only.
// module.exports ={run, bucketParams};
<|endoftext|>


<commit_before>/* Copyright 2009, 2010 Paperpile

   This file is part of Paperpile

   Paperpile is free software: you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   Paperpile is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.  You should have received a
   copy of the GNU General Public License along with Paperpile.  If
   not, see http://www.gnu.org/licenses. */

Paperpile.QueueList = Ext.extend(Ext.grid.GridPanel, {

  constructor: function(queuePanel, config) {
    this.queuePanel = queuePanel;
    Paperpile.QueueList.superclass.constructor.call(this, config);
  },

  onContextClick: function(grid, index, e) {
    e.stopEvent();
    var record = this.store.getAt(index);
    if (!this.isSelected(index)) {
      this.select(index, false, true);
    }

    if (this.context == null) {
      this.createContextMenu();
    }

    this.context.items.each(function(item, index, length) {
      item.enable();
      this.updateContextItem(item, record);
    },
    this);

    (function() {
      this.context.showAt(e.getXY());
      this.updateButtons();
    }).defer(20, this);
  },

  getContextMenu: function() {
    if (this.context != null) {
      return this.context;
    }
    this.context = new Ext.menu.Menu({
      itemId: 'context'
    });
    var c = this.context;
    return c;
  },

  getToolbar: function() {
    if (this._tbar != null) {
      return this._tbar;
    }

    var tbar = new Ext.Toolbar({
      itemId: 'toolbar'
    });
    tbar.insert(0, this.actions['REMOVE']);
    tbar.insert(0, this.actions['RETRY']);
    tbar.insert(0, this.actions['TB_FILL']);

    this._tbar = tbar;
    return tbar;
  },

  updateToolbar: function() {
    var sel = this.getSelectedRecords();
    var tbar = this.getToolbar();

    if (sel.length == 0) {
      tbar.items.each(function(item, index, length) {
        item.disable();
      },
      this);
    } else {
      tbar.items.each(function(item, index, length) {
        item.enable();
      },
      this);
    }
  },

  renderData: function(value, meta, record) {

    var data = record.data;

    if (data.size && data.downloaded && data.status === 'RUNNING') {
      data.message = 'Downloading (' + Math.round((data.downloaded / data.size) * 100) + '%)';
    }

    if (data.authors) {
      data.shortAuthors = this.shortAuthors(data.authors_display);
    } else {
      data.shortAuthors = null;
    }

    if (data.title) {
      data.shortTitle = Ext.util.Format.ellipsis(data.title, 65, true);
    } else {
      data.shortTitle = null;
    }

    data.publisherLink = null;

    if (data.doi){
      data.publisherLink = 'http://dx.doi.org/'+data.doi;
    } else {
      if (data.linkout){
        data.publisherLink = data.linkout;
      }
    }

    data.errorReportInfo = data.title + ' | ' + data.authors + ' | ';
    data.errorReportInfo += data.citation + ' | ' + data.doi + ' | ' + data.linkout;

    data.gridID=this.id;

    return this.dataTemplate.apply(data);
  },

  renderType: function(value, meta, record) {
    var data = record.data;
    return this.typeTemplate.apply(data);
  },

  renderStatus: function(value, meta, record) {
    var data = record.data;
    return this.statusTemplate.apply(data);
  },

  initComponent: function() {

    this.actions = {
      'RETRY': new Ext.Action({
        text: 'Retry Tasks',
        tooltip: 'Run selected tasks again',
        handler: function() {
          this.queuePanel.retryJobs();
        },
        scope: this,
        iconCls: 'pp-icon-retry'
      }),
      'REMOVE': new Ext.Action({
        text: 'Cancel Tasks',
        tooltip: 'Cancel selected tasks',
        handler: function() {
          this.queuePanel.cancelJobs();
        },
        scope: this,
        cls: 'x-btn-text-icon',
        iconCls: 'pp-icon-cancel'
      }),
      'TB_FILL': new Ext.Toolbar.Fill({
        width: '10px',
        itemId: 'search_tb_fill'
      })
    };

    this._store = new Ext.data.JsonStore({
      storeId: 'queue_store',
      autoDestroy: true,
      url: Paperpile.Url('/ajax/queue/grid'),
      method: 'GET',
      baseParams: {
        limit: 100
      }
    });
    this.pager = new Ext.PagingToolbar({
      pageSize: 100,
      store: this._store,
      displayInfo: true,
      displayMsg: 'Tasks {0} - {1} of {2}',
      emptyMsg: "No tasks"
    });

    this.dataTemplate = new Ext.XTemplate(
      '
',
      '  <tpl if="type==\'PDF_SEARCH\'">',
      '
',
      '
{shortAuthors} {shortTitle} {journal}
',
      '
',
      '        {message}',
      '
',
      '      <tpl if="status==\'ERROR\'">',
      '
',
      '           <tpl if="publisherLink">',
      '             Go to publisher site | ',
      '           </tpl>',
      '           Send Error Report',
      '
 ',
      '      </tpl>',
      '
',
      '  </tpl>',
      '  <tpl if="type==\'PDF_IMPORT\'">',
      '
',
      '
',
      '      <tpl if="status!=\'DONE\'">{pdf} </tpl> ',
      '      <tpl if="status!=\'ERROR\'">',
      '        <tpl if="shortAuthors">{shortAuthors} </tpl> <tpl if="shortTitle">{shortTitle}</tpl> <tpl if="journal">{journal}</tpl>',
      '      </tpl> ',
      '
',
      '
',
      '      {message}',
      '
',
      '      <tpl if="status==\'ERROR\'">',
      '
',
      '          Insert data manually | ',
      '          Send Error Report',
      '
',
      '      </tpl>',
      '
',
      '  </tpl>',
      '  <tpl if="type==\'METADATA_UPDATE\'">',
      '
',
      '
{shortAuthors} {shortTitle} {journal}
',
      '
',
      '        {message}',
      '
',
      '      <tpl if="status==\'ERROR\'">',
      '
',
      '           <tpl if="publisherLink">',
      '             Go to publisher site | ',
      '           </tpl>',
      '           Send Error Report',
      '
 ',
      '      </tpl>',
      '
',
      '  </tpl>',

      '
').compile();

    this.typeTemplate = new Ext.XTemplate(
      '
',
      '  <tpl if="type==\'PDF_SEARCH\'">',
      '    Search PDF',
      '  </tpl>',
      '  <tpl if="type==\'PDF_IMPORT\'">',
      '    Match PDF',
      '  </tpl>',
      '  <tpl if="type==\'METADATA_UPDATE\'">',
      '    Update Metadata',
      '  </tpl>',
      '
').compile();

    this.statusTemplate = new Ext.XTemplate(
      '
<tpl if="status==\'PENDING\'">Waiting</tpl>').compile();

    Ext.apply(this, {
      store: this._store,
      bbar: this.pager,
      tbar: this.getToolbar(),
      multiSelect: true,
      cm: new Ext.grid.ColumnModel({
        defaults: {
          menuDisabled: true,
          sortable: false
        },
        columns: [{
          header: "Task",
          id: 'type',
          dataIndex: 'type',
          renderer: this.renderType.createDelegate(this),
          sortable: false,
          resizable: false
        },
        {
          header: "Data",
          id: 'title',
          dataIndex: 'title',
          renderer: this.renderData.createDelegate(this),
          sortable: false,
          resizable: false
        },
        {
          header: "Icon",
          id: 'status',
          dataIndex: 'status',
          renderer: this.renderStatus.createDelegate(this),
          sortable: false,
          resizable: false
        },
        ]
      }),
      autoExpandColumn: 'title',
      hideHeaders: true
    });
    this.store.load();

    Paperpile.QueueList.superclass.initComponent.call(this);

    this.on('afterrender', function() {
      this.getSelectionModel().on('afterselectionchange', this.selChanged, this);
      this.selChanged();
    },
    this);

  },

  getSelectedRecords: function() {
    return this.getSelectionModel().getSelections();
  },

  getSelectedIds: function() {
    var sel = this.getSelectedRecords();
    var ids = [];
    for (var i = 0; i < sel.length; i++) {
      ids.push(sel[i].data.id);
    }
    return ids;
  },

  selChanged: function(selections) {
    this.updateToolbar();
  },

  // onUpdate function for the Queue grid view.
  onUpdate: function(data) {
    var jobs = data.jobs;
    if (!jobs) {
      return;
    }

    for (var id in jobs) {
      var index = this.store.find('id', id);
      var record = this.store.getAt(index);
      if (!record) {
        continue;
      }

      var needsUpdating = false;
      var update = jobs[id];
      record.editing = true;
      for (var field in update) {
        record.set(field, update[field]);
      }
      record.set('size', update.info.size);
      record.set('downloaded', update.info.downloaded);

      record.editing = false;
      if (record.dirty) {
        needsUpdating = true;
      }
      if (needsUpdating) {
        this.store.fireEvent('update', this.store, record, Ext.data.Record.EDIT);
      }
    }
  },

  shortAuthors: function(names) {
    var list = names.split(',');
    if (list.length > 1) {
      return list[0] + " et al.";
    } else {
      return names;
    }
  }

});<commit_msg>Fix double load of the queue grid.<commit_after>/* Copyright 2009, 2010 Paperpile

   This file is part of Paperpile

   Paperpile is free software: you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   Paperpile is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.  You should have received a
   copy of the GNU General Public License along with Paperpile.  If
   not, see http://www.gnu.org/licenses. */

Paperpile.QueueList = Ext.extend(Ext.grid.GridPanel, {

  constructor: function(queuePanel, config) {
    this.queuePanel = queuePanel;
    Paperpile.QueueList.superclass.constructor.call(this, config);
  },

  onContextClick: function(grid, index, e) {
    e.stopEvent();
    var record = this.store.getAt(index);
    if (!this.isSelected(index)) {
      this.select(index, false, true);
    }

    if (this.context == null) {
      this.createContextMenu();
    }

    this.context.items.each(function(item, index, length) {
      item.enable();
      this.updateContextItem(item, record);
    },
    this);

    (function() {
      this.context.showAt(e.getXY());
      this.updateButtons();
    }).defer(20, this);
  },

  getContextMenu: function() {
    if (this.context != null) {
      return this.context;
    }
    this.context = new Ext.menu.Menu({
      itemId: 'context'
    });
    var c = this.context;
    return c;
  },

  getToolbar: function() {
    if (this._tbar != null) {
      return this._tbar;
    }

    var tbar = new Ext.Toolbar({
      itemId: 'toolbar'
    });
    tbar.insert(0, this.actions['REMOVE']);
    tbar.insert(0, this.actions['RETRY']);
    tbar.insert(0, this.actions['TB_FILL']);

    this._tbar = tbar;
    return tbar;
  },

  updateToolbar: function() {
    var sel = this.getSelectedRecords();
    var tbar = this.getToolbar();

    if (sel.length == 0) {
      tbar.items.each(function(item, index, length) {
        item.disable();
      },
      this);
    } else {
      tbar.items.each(function(item, index, length) {
        item.enable();
      },
      this);
    }
  },

  renderData: function(value, meta, record) {

    var data = record.data;

    if (data.size && data.downloaded && data.status === 'RUNNING') {
      data.message = 'Downloading (' + Math.round((data.downloaded / data.size) * 100) + '%)';
    }

    if (data.authors) {
      data.shortAuthors = this.shortAuthors(data.authors_display);
    } else {
      data.shortAuthors = null;
    }

    if (data.title) {
      data.shortTitle = Ext.util.Format.ellipsis(data.title, 65, true);
    } else {
      data.shortTitle = null;
    }

    data.publisherLink = null;

    if (data.doi){
      data.publisherLink = 'http://dx.doi.org/'+data.doi;
    } else {
      if (data.linkout){
        data.publisherLink = data.linkout;
      }
    }

    data.errorReportInfo = data.title + ' | ' + data.authors + ' | ';
    data.errorReportInfo += data.citation + ' | ' + data.doi + ' | ' + data.linkout;

    data.gridID=this.id;

    return this.dataTemplate.apply(data);
  },

  renderType: function(value, meta, record) {
    var data = record.data;
    return this.typeTemplate.apply(data);
  },

  renderStatus: function(value, meta, record) {
    var data = record.data;
    return this.statusTemplate.apply(data);
  },

  initComponent: function() {

    this.actions = {
      'RETRY': new Ext.Action({
        text: 'Retry Tasks',
        tooltip: 'Run selected tasks again',
        handler: function() {
          this.queuePanel.retryJobs();
        },
        scope: this,
        iconCls: 'pp-icon-retry'
      }),
      'REMOVE': new Ext.Action({
        text: 'Cancel Tasks',
        tooltip: 'Cancel selected tasks',
        handler: function() {
          this.queuePanel.cancelJobs();
        },
        scope: this,
        cls: 'x-btn-text-icon',
        iconCls: 'pp-icon-cancel'
      }),
      'TB_FILL': new Ext.Toolbar.Fill({
        width: '10px',
        itemId: 'search_tb_fill'
      })
    };

    this._store = new Ext.data.JsonStore({
      storeId: 'queue_store',
      autoDestroy: true,
      url: Paperpile.Url('/ajax/queue/grid'),
      method: 'GET',
      baseParams: {
        limit: 100
      }
    });
    this.pager = new Ext.PagingToolbar({
      pageSize: 100,
      store: this._store,
      displayInfo: true,
      displayMsg: 'Tasks {0} - {1} of {2}',
      emptyMsg: "No tasks"
    });

    this.dataTemplate = new Ext.XTemplate(
      '
',
      '  <tpl if="type==\'PDF_SEARCH\'">',
      '
',
      '
{shortAuthors} {shortTitle} {journal}
',
      '
',
      '        {message}',
      '
',
      '      <tpl if="status==\'ERROR\'">',
      '
',
      '           <tpl if="publisherLink">',
      '             Go to publisher site | ',
      '           </tpl>',
      '           Send Error Report',
      '
 ',
      '      </tpl>',
      '
',
      '  </tpl>',
      '  <tpl if="type==\'PDF_IMPORT\'">',
      '
',
      '
',
      '      <tpl if="status!=\'DONE\'">{pdf} </tpl> ',
      '      <tpl if="status!=\'ERROR\'">',
      '        <tpl if="shortAuthors">{shortAuthors} </tpl> <tpl if="shortTitle">{shortTitle}</tpl> <tpl if="journal">{journal}</tpl>',
      '      </tpl> ',
      '
',
      '
',
      '      {message}',
      '
',
      '      <tpl if="status==\'ERROR\'">',
      '
',
      '          Insert data manually | ',
      '          Send Error Report',
      '
',
      '      </tpl>',
      '
',
      '  </tpl>',
      '  <tpl if="type==\'METADATA_UPDATE\'">',
      '
',
      '
{shortAuthors} {shortTitle} {journal}
',
      '
',
      '        {message}',
      '
',
      '      <tpl if="status==\'ERROR\'">',
      '
',
      '           <tpl if="publisherLink">',
      '             Go to publisher site | ',
      '           </tpl>',
      '           Send Error Report',
      '
 ',
      '      </tpl>',
      '
',
      '  </tpl>',

      '
').compile();

    this.typeTemplate = new Ext.XTemplate(
      '
',
      '  <tpl if="type==\'PDF_SEARCH\'">',
      '    Search PDF',
      '  </tpl>',
      '  <tpl if="type==\'PDF_IMPORT\'">',
      '    Match PDF',
      '  </tpl>',
      '  <tpl if="type==\'METADATA_UPDATE\'">',
      '    Update Metadata',
      '  </tpl>',
      '
').compile();

    this.statusTemplate = new Ext.XTemplate(
      '
<tpl if="status==\'PENDING\'">Waiting</tpl>').compile();

    Ext.apply(this, {
      store: this._store,
      bbar: this.pager,
      tbar: this.getToolbar(),
      multiSelect: true,
      cm: new Ext.grid.ColumnModel({
        defaults: {
          menuDisabled: true,
          sortable: false
        },
        columns: [{
          header: "Task",
          id: 'type',
          dataIndex: 'type',
          renderer: this.renderType.createDelegate(this),
          sortable: false,
          resizable: false
        },
        {
          header: "Data",
          id: 'title',
          dataIndex: 'title',
          renderer: this.renderData.createDelegate(this),
          sortable: false,
          resizable: false
        },
        {
          header: "Icon",
          id: 'status',
          dataIndex: 'status',
          renderer: this.renderStatus.createDelegate(this),
          sortable: false,
          resizable: false
        },
        ]
      }),
      autoExpandColumn: 'title',
      hideHeaders: true
    });
    //this.store.load();

    Paperpile.QueueList.superclass.initComponent.call(this);

    this.on('afterrender', function() {
      this.getSelectionModel().on('afterselectionchange', this.selChanged, this);
      this.selChanged();
    },
    this);

  },

  getSelectedRecords: function() {
    return this.getSelectionModel().getSelections();
  },

  getSelectedIds: function() {
    var sel = this.getSelectedRecords();
    var ids = [];
    for (var i = 0; i < sel.length; i++) {
      ids.push(sel[i].data.id);
    }
    return ids;
  },

  selChanged: function(selections) {
    this.updateToolbar();
  },

  // onUpdate function for the Queue grid view.
  onUpdate: function(data) {
    var jobs = data.jobs;
    if (!jobs) {
      return;
    }

    for (var id in jobs) {
      var index = this.store.find('id', id);
      var record = this.store.getAt(index);
      if (!record) {
        continue;
      }

      var needsUpdating = false;
      var update = jobs[id];
      record.editing = true;
      for (var field in update) {
        record.set(field, update[field]);
      }
      record.set('size', update.info.size);
      record.set('downloaded', update.info.downloaded);

      record.editing = false;
      if (record.dirty) {
        needsUpdating = true;
      }
      if (needsUpdating) {
        this.store.fireEvent('update', this.store, record, Ext.data.Record.EDIT);
      }
    }
  },

  shortAuthors: function(names) {
    var list = names.split(',');
    if (list.length > 1) {
      return list[0] + " et al.";
    } else {
      return names;
    }
  }

});<|endoftext|>