<commit_before>snack.ready(function() {

    // if it finds no element, it finds a NodeList, resulting in an error which breaks all following js
    if (snack.wrap('.edit')[0].addEventListener != undefined) {
        snack.wrap('.edit').attach('click', function(evt) {
            snack.preventDefault(evt)

            var options = {
                method: 'get',
                url: evt.target.parentNode.href,
            }
            snack.request(options, function(err, res){
                if (err) {
                    alert('error fetching option: ' + err);
                    return;
                }
                var parent = getParent(evt.target, 'container')

                if (navigator.userAgent.match(/.*Firefox.*/)) {
                    // detect firefox here, because in firefox you cant create an empty element and chrome can't add the form as inner/outerhtml without errors
                    var form = document.createElement("form");
                } else {
                    var form = document.createElement();
                }
                form.innerHTML = res;
                form.querySelector('form').className += ' highlight';

                var cancelButton = document.createElement('button');
                cancelButton.innerHTML = "Cancel";
                cancelButton.setAttribute('type', 'button');
                cancelButton.className = "cancel";
                form.querySelector('.editorSubmitButtons').parentNode.insertBefore(cancelButton, form.querySelector('button').parentNode);
                snack.wrap(cancelButton).attach('click', function(evt) {
                    form.parentNode.replaceChild(parent, form);
                });

                parent.parentNode.replaceChild(form, parent);
            });
        });
    }

    if (snack.wrap('.delete')[0].addEventListener != undefined) {
        snack.wrap('.delete').attach('click', function(evt) {
            snack.preventDefault(evt)

            var options = {
                method: 'post',
                url: evt.target.parentNode.action,
            }
            snack.request(options, function(err, res) {
                if (err) {
                    alert('error deleting entry: ' + err);
                    return;
                }
                var parent = getParent(evt.target, 'entry')

                events = ["animationend", "webkitAnimationEnd", "oanimationend", "MSAnimationEnd"];
                events.forEach(function(event) {
                    snack.wrap(parent).addClass("fadeout").attach(event, function() {
                        parent.parentNode.removeChild(parent);
                    });
                });

            });
        });
    }

    // editor is loaded
    var publish;
    if ((publish = document.querySelector('.entryPublish')) != null) {
        // preview-button
        var previewButton = document.createElement('button');
        previewButton.innerHTML = "Preview";
        previewButton['id'] = "previewButton";
        previewButton.setAttribute('type', 'button');
        publish.parentNode.insertBefore(previewButton, publish);
        snack.wrap(previewButton).attach('click', function(evt) {
            var options = {
                method: 'post',
                url: '/preview',
                data: {
                    body: document.querySelector('.entryInput').value,
                    title: document.querySelector('.entryTitleInput').value,
                    tags: document.querySelector('.entryTagInput').value
                }
            }
            snack.request(options, function (err, res) {
                if (err) {
                    alert('error getting preview: ' + err);
                    return;
                }
                var entry = document.createElement('div');
                entry.innerHTML = res;
                entry['id'] = "preview";
                entry.className += ' entry highlight';
                var preview;
                if ((preview = document.getElementById("preview")) != null) {
                    publish.parentNode.parentNode.replaceChild(entry, preview);
                } else {
                    publish.parentNode.parentNode.appendChild(entry);
                }
                preview = document.getElementById("preview");
                var adminOptions = preview.querySelectorAll('.adminOptions')[0];
                adminOptions.parentNode.removeChild(adminOptions);
            });
        });
        // tags autocomplete
        var tagInput = document.querySelector('.entryTagInput');
        var rawTags = document.querySelector('#tags').cloneNode();
        var tags = document.querySelector('#tags');
        var oldTagInputValue = "";
        tagInput.oninput = function(evt) {
            var autocompleteOccured = false;
            if (tagInput.value.length - oldTagInputValue.length > 1) {
                autocompleteOccured = true;
                tagInput.value += ", ";
            }
            if (tagInput.value.substr(-1) == ',' || autocompleteOccured) {
                tagOptions = [];
                for (var i=0;i < rawTags.options.length;i++) {
                    var newTag = document.createElement('option');
                    if (autocompleteOccured) {
                        newTag.value = tagInput.value + rawTags.options[i].value;
                    } else {
                        newTag.value = tagInput.value + " " + rawTags.options[i].value;
                    }
                    tagOptions[i] = newTag;
                }
                while (tags.hasChildNodes()) {
                    tags.removeChild(tags.lastChild);
                }
                console.log(tagOptions.length);
                for (var i=0;i < tagOptions.length;i++) {
                    tags.appendChild(tagOptions[i]);
                }
            }

            if (tagInput.value == "") {
                // user has deleted all tags
                tags.parentNode.appendChild(rawTags);
                tags.parentNode.removeChild(tags);
                tags =  document.querySelector('#tags');
                rawTags = document.querySelector('#tags').cloneNode();
            }
            oldTagInputValue = tagInput.value;
        };
    }

    // support for the hover-menu, dont vanish directly
    if (snack.wrap('.adminOptionsMoreSign').length > 1) {
        // start this only on a page with comments, else snack throws errors
        var fadeouts = {};
        snack.wrap('.adminOptionsMoreSign').attach('mouseover', function(evt) {
            var parent = evt.target.parentNode.querySelectorAll(".adminOptionsMoreOptions")[0];
            parent.style["display"] = "block";
            snack.wrap(parent).removeClass("fadeout");
            clearTimeout(fadeouts[parent.innerHTML]);
            if (! navigator.userAgent.match(/.*Firefox.*/)) {
                // detect firefox here, because in firefox the animation leads to the menu vanishing immediately
                snack.wrap(evt.target.parentNode.querySelectorAll(".adminOptionsMoreOptions")[0]).addClass("fadein");
            }
        });

        snack.wrap('.adminOptionsMoreSign').attach('mouseout', function(evt) {
            var parent = evt.target.parentNode.querySelectorAll(".adminOptionsMoreOptions")[0];
            fadeOutMenut(parent);
        });

        snack.wrap('.adminOptionsMoreOptions').attach('mouseout', function(evt) {
            var parent = getParent(evt.target, 'adminOptionsMoreOptions');
            fadeOutMenut(parent);
        });

        function fadeOutMenut(parent) {
            var fadeout = setTimeout(function() {
                                    snack.wrap(parent).removeClass("fadein");
                                    snack.wrap(parent).addClass("fadeout");
                                    clearTimeout(fadeout);
                                }, 300);
            fadeouts[parent.innerHTML] = fadeout
        }

        snack.wrap('.adminOptionsMoreOptions').attach('mouseover', function(evt) {
            var parent = getParent(evt.target, 'adminOptionsMoreOptions');
            clearTimeout(fadeouts[parent.innerHTML]);
        });
    }

    // markup-buttons
    if (document.querySelector('.entryInput') != null) {
        var editor = document.querySelector('.entryInput');
        var buttonBar = document.createElement("div");
        buttonBar.className = "buttonBar";
        var boldButton = document.createElement("span");
        boldButton.innerHTML = "B";
        boldButton.className = "markupButton boldButton";
        var italicButton = document.createElement("span");
        italicButton.innerHTML = "I";
        italicButton.className = "markupButton italicButton";
        var linkButton = document.createElement("img");
        linkButton["alt"] = "Link";
        linkButton["src"] = "/img/link.png";
        linkButton.className = "markupButton linkButton";
        var imgButton = document.createElement("span");
        imgButton.innerHTML = "IMG";
        imgButton.className = "markupButton imgButton";
        var imgButtonInput = document.createElement("input");
        imgButtonInput.className = "imgButtonInput";
        imgButtonInput.type = "file";
        imgButtonInput.multiple = "multiple";
        imgButtonInput.names = "images";
        imgButtonInput.accept = "image";
        buttonBar.innerHTML += boldButton.outerHTML;
        buttonBar.innerHTML += italicButton.outerHTML;
        buttonBar.innerHTML += linkButton.outerHTML;
        buttonBar.innerHTML += imgButton.outerHTML;
        buttonBar.innerHTML += imgButtonInput.outerHTML;
        editor.parentNode.insertBefore(buttonBar, editor);
        snack.wrap('.boldButton').attach('click', function(evt) {
            var sel = getTextSelection(editor);
            var replace = "**"+sel+"**";
            replaceSelection(editor, replace);
        });
        snack.wrap('.italicButton').attach('click', function(evt) {
            var sel = getTextSelection(editor);
            var replace = "*"+sel+"*";
            replaceSelection(editor, replace);
        });
        snack.wrap('.linkButton').attach('click', function(evt) {
            var sel = getTextSelection(editor);
            var replace = '[http:// '+sel+']';
            replaceSelection(editor, replace);
        });
        snack.wrap('.imgButton').attach('click', function(evt) {
            imgButtonInput.click();
        });
        snack.wrap(imgButtonInput).attach('change', function() {
            var files = imgButtonInput.files;
            for (var i = 0, f; f = files[i]; i++) {
                var reader = new FileReader();
                var file = f;
                reader.onload = function(event) {
                    object = {};
                    object.filename = file.name;
                    object.data = event.target.result;
                    var options = {
                        method: 'post',
                        url: '/file',
                        data: object
                    }
                    snack.request(options, function (err, res) {
                        if (err) {
                            alert("error uploading file: " + err);
                        }
                        replaceSelection(editor, "[["+res+"]]");
                    });
                };
                reader.readAsDataURL(f);

            }
        });

    }

    /* toggle-slider-button */
    if  (document.getElementById('entryModerationToggle') != null) {
        var moderationForm = document.getElementById('entryModerationToggle');
        var saveButton = moderationForm.querySelectorAll('.save')[0];
        var toggle = moderationForm.querySelectorAll('input[type="checkbox"]')[0];
        toggle.parentNode.insertBefore(document.createElement('span'), toggle.nextSibling);
        // the css will make a toggle-button out of the span, so the checkbox is no longer needed, as a click on the span triggers the label which triggers the checkbox
        toggle.style['display'] = 'none';

        snack.wrap(toggle).attach('change', function() {
                                                var options = {
                                                    method: 'post',
                                                    url: moderationForm.action,
                                                    data: {
                                                        value: toggle.checked
                                                    }

                                                }
                                                snack.request(options, function (err, res) {
                                                    if (err) {
                                                        alert('error setting moderation: ' + err);
                                                        return;
                                                    }
                                                });
        });
        moderationForm.removeChild(saveButton);
    }

    function getTextSelection(textarea){
        var startPos = textarea.selectionStart;
        var endPos = textarea.selectionEnd;
        var field_value = textarea.value;
        var selectedText = field_value.substring(startPos,endPos);
        return selectedText;
    }

    function replaceSelection(editor, text) {
        var start = editor.selectionStart;
        var end = editor.selectionEnd;
        var len = editor.value.length;
        editor.value =  editor.value.substring(0,start) + text + editor.value.substring(end,len);
        editor.selectionEnd = start + text.length;
    }

    function getParent(start, classname) {
        while (! start.className.match(new RegExp('\\b'+classname+'\\b'))) {
            var start = start.parentNode;
        }
        return start
    }

});<commit_msg>drag&drop image upload<commit_after>snack.ready(function() {

    // if it finds no element, it finds a NodeList, resulting in an error which breaks all following js
    if (snack.wrap('.edit')[0].addEventListener != undefined) {
        snack.wrap('.edit').attach('click', function(evt) {
            snack.preventDefault(evt)

            var options = {
                method: 'get',
                url: evt.target.parentNode.href,
            }
            snack.request(options, function(err, res){
                if (err) {
                    alert('error fetching option: ' + err);
                    return;
                }
                var parent = getParent(evt.target, 'container')

                if (navigator.userAgent.match(/.*Firefox.*/)) {
                    // detect firefox here, because in firefox you cant create an empty element and chrome can't add the form as inner/outerhtml without errors
                    var form = document.createElement("form");
                } else {
                    var form = document.createElement();
                }
                form.innerHTML = res;
                form.querySelector('form').className += ' highlight';

                var cancelButton = document.createElement('button');
                cancelButton.innerHTML = "Cancel";
                cancelButton.setAttribute('type', 'button');
                cancelButton.className = "cancel";
                form.querySelector('.editorSubmitButtons').parentNode.insertBefore(cancelButton, form.querySelector('button').parentNode);
                snack.wrap(cancelButton).attach('click', function(evt) {
                    form.parentNode.replaceChild(parent, form);
                });

                parent.parentNode.replaceChild(form, parent);
            });
        });
    }

    if (snack.wrap('.delete')[0].addEventListener != undefined) {
        snack.wrap('.delete').attach('click', function(evt) {
            snack.preventDefault(evt)

            var options = {
                method: 'post',
                url: evt.target.parentNode.action,
            }
            snack.request(options, function(err, res) {
                if (err) {
                    alert('error deleting entry: ' + err);
                    return;
                }
                var parent = getParent(evt.target, 'entry')

                events = ["animationend", "webkitAnimationEnd", "oanimationend", "MSAnimationEnd"];
                events.forEach(function(event) {
                    snack.wrap(parent).addClass("fadeout").attach(event, function() {
                        parent.parentNode.removeChild(parent);
                    });
                });

            });
        });
    }

    // editor is loaded
    var publish;
    if ((publish = document.querySelector('.entryPublish')) != null) {
        // preview-button
        var previewButton = document.createElement('button');
        previewButton.innerHTML = "Preview";
        previewButton['id'] = "previewButton";
        previewButton.setAttribute('type', 'button');
        publish.parentNode.insertBefore(previewButton, publish);
        snack.wrap(previewButton).attach('click', function(evt) {
            var options = {
                method: 'post',
                url: '/preview',
                data: {
                    body: document.querySelector('.entryInput').value,
                    title: document.querySelector('.entryTitleInput').value,
                    tags: document.querySelector('.entryTagInput').value
                }
            }
            snack.request(options, function (err, res) {
                if (err) {
                    alert('error getting preview: ' + err);
                    return;
                }
                var entry = document.createElement('div');
                entry.innerHTML = res;
                entry['id'] = "preview";
                entry.className += ' entry highlight';
                var preview;
                if ((preview = document.getElementById("preview")) != null) {
                    publish.parentNode.parentNode.replaceChild(entry, preview);
                } else {
                    publish.parentNode.parentNode.appendChild(entry);
                }
                preview = document.getElementById("preview");
                var adminOptions = preview.querySelectorAll('.adminOptions')[0];
                adminOptions.parentNode.removeChild(adminOptions);
            });
        });
        // tags autocomplete
        var tagInput = document.querySelector('.entryTagInput');
        var rawTags = document.querySelector('#tags').cloneNode();
        var tags = document.querySelector('#tags');
        var oldTagInputValue = "";
        tagInput.addEventListener('input', function(evt) {
            var autocompleteOccured = false;
            if (tagInput.value.length - oldTagInputValue.length > 1) {
                autocompleteOccured = true;
                tagInput.value += ", ";
            }
            if (tagInput.value.substr(-1) == ',' || autocompleteOccured) {
                tagOptions = [];
                for (var i=0;i < rawTags.options.length;i++) {
                    var newTag = document.createElement('option');
                    if (autocompleteOccured) {
                        newTag.value = tagInput.value + rawTags.options[i].value;
                    } else {
                        newTag.value = tagInput.value + " " + rawTags.options[i].value;
                    }
                    tagOptions[i] = newTag;
                }
                while (tags.hasChildNodes()) {
                    tags.removeChild(tags.lastChild);
                }
                for (var i=0;i < tagOptions.length;i++) {
                    tags.appendChild(tagOptions[i]);
                }
            }

            if (tagInput.value == "") {
                // user has deleted all tags
                tags.parentNode.appendChild(rawTags);
                tags.parentNode.removeChild(tags);
                tags =  document.querySelector('#tags');
                rawTags = document.querySelector('#tags').cloneNode();
            }
            oldTagInputValue = tagInput.value;
        });

        // file upload using drag & drop
        document.querySelector('.entryInput').addEventListener("dragenter", function(evt) {
            evt.stopPropagation();
            evt.preventDefault();
        });
        document.querySelector('.entryInput').addEventListener("dragover", function(evt) {
            evt.stopPropagation();
            evt.preventDefault();
        });
        document.querySelector('.entryInput').addEventListener("drop", function(evt) {
            evt.stopPropagation();
            evt.preventDefault();

            var dt = evt.dataTransfer;
            uploadFiles(dt.files);
        });
    }

    // support for the hover-menu, dont vanish directly
    if (snack.wrap('.adminOptionsMoreSign').length > 1) {
        // start this only on a page with comments, else snack throws errors
        var fadeouts = {};
        snack.wrap('.adminOptionsMoreSign').attach('mouseover', function(evt) {
            var parent = evt.target.parentNode.querySelectorAll(".adminOptionsMoreOptions")[0];
            parent.style["display"] = "block";
            snack.wrap(parent).removeClass("fadeout");
            clearTimeout(fadeouts[parent.innerHTML]);
            if (! navigator.userAgent.match(/.*Firefox.*/)) {
                // detect firefox here, because in firefox the animation leads to the menu vanishing immediately
                snack.wrap(evt.target.parentNode.querySelectorAll(".adminOptionsMoreOptions")[0]).addClass("fadein");
            }
        });

        snack.wrap('.adminOptionsMoreSign').attach('mouseout', function(evt) {
            var parent = evt.target.parentNode.querySelectorAll(".adminOptionsMoreOptions")[0];
            fadeOutMenut(parent);
        });

        snack.wrap('.adminOptionsMoreOptions').attach('mouseout', function(evt) {
            var parent = getParent(evt.target, 'adminOptionsMoreOptions');
            fadeOutMenut(parent);
        });

        function fadeOutMenut(parent) {
            var fadeout = setTimeout(function() {
                                    snack.wrap(parent).removeClass("fadein");
                                    snack.wrap(parent).addClass("fadeout");
                                    clearTimeout(fadeout);
                                }, 300);
            fadeouts[parent.innerHTML] = fadeout
        }

        snack.wrap('.adminOptionsMoreOptions').attach('mouseover', function(evt) {
            var parent = getParent(evt.target, 'adminOptionsMoreOptions');
            clearTimeout(fadeouts[parent.innerHTML]);
        });
    }

    // markup-buttons
    if (document.querySelector('.entryInput') != null) {
        var editor = document.querySelector('.entryInput');
        var buttonBar = document.createElement("div");
        buttonBar.className = "buttonBar";
        var boldButton = document.createElement("span");
        boldButton.innerHTML = "B";
        boldButton.className = "markupButton boldButton";
        var italicButton = document.createElement("span");
        italicButton.innerHTML = "I";
        italicButton.className = "markupButton italicButton";
        var linkButton = document.createElement("img");
        linkButton["alt"] = "Link";
        linkButton["src"] = "/img/link.png";
        linkButton.className = "markupButton linkButton";
        var imgButton = document.createElement("span");
        imgButton.innerHTML = "IMG";
        imgButton.className = "markupButton imgButton";
        var imgButtonInput = document.createElement("input");
        imgButtonInput.className = "imgButtonInput";
        imgButtonInput.type = "file";
        imgButtonInput.multiple = "multiple";
        imgButtonInput.names = "images";
        imgButtonInput.accept = "image";
        buttonBar.innerHTML += boldButton.outerHTML;
        buttonBar.innerHTML += italicButton.outerHTML;
        buttonBar.innerHTML += linkButton.outerHTML;
        buttonBar.innerHTML += imgButton.outerHTML;
        buttonBar.innerHTML += imgButtonInput.outerHTML;
        editor.parentNode.insertBefore(buttonBar, editor);
        snack.wrap('.boldButton').attach('click', function(evt) {
            var sel = getTextSelection(editor);
            var replace = "**"+sel+"**";
            replaceSelection(editor, replace);
        });
        snack.wrap('.italicButton').attach('click', function(evt) {
            var sel = getTextSelection(editor);
            var replace = "*"+sel+"*";
            replaceSelection(editor, replace);
        });
        snack.wrap('.linkButton').attach('click', function(evt) {
            var sel = getTextSelection(editor);
            var replace = '[http:// '+sel+']';
            replaceSelection(editor, replace);
        });
        document.querySelector('.imgButton').addEventListener('click', function(evt) {
            imgButtonInput.click();
        });
        imgButtonInput.addEventListener('change', function() {
            uploadFiles(imgButtonInput.files);
        });

    }

    /* toggle-slider-button */
    if  (document.getElementById('entryModerationToggle') != null) {
        var moderationForm = document.getElementById('entryModerationToggle');
        var saveButton = moderationForm.querySelectorAll('.save')[0];
        var toggle = moderationForm.querySelectorAll('input[type="checkbox"]')[0];
        toggle.parentNode.insertBefore(document.createElement('span'), toggle.nextSibling);
        // the css will make a toggle-button out of the span, so the checkbox is no longer needed, as a click on the span triggers the label which triggers the checkbox
        toggle.style['display'] = 'none';

        snack.wrap(toggle).attach('change', function() {
                                                var options = {
                                                    method: 'post',
                                                    url: moderationForm.action,
                                                    data: {
                                                        value: toggle.checked
                                                    }

                                                }
                                                snack.request(options, function (err, res) {
                                                    if (err) {
                                                        alert('error setting moderation: ' + err);
                                                        return;
                                                    }
                                                });
        });
        moderationForm.removeChild(saveButton);
    }

    function getTextSelection(textarea){
        var startPos = textarea.selectionStart;
        var endPos = textarea.selectionEnd;
        var field_value = textarea.value;
        var selectedText = field_value.substring(startPos,endPos);
        return selectedText;
    }

    function replaceSelection(editor, text) {
        var start = editor.selectionStart;
        var end = editor.selectionEnd;
        var len = editor.value.length;
        editor.value =  editor.value.substring(0,start) + text + editor.value.substring(end,len);
        editor.selectionEnd = start + text.length;
    }

    function getParent(start, classname) {
        while (! start.className.match(new RegExp('\\b'+classname+'\\b'))) {
            var start = start.parentNode;
        }
        return start
    }

    function uploadFiles(files) {
        for (var i = 0, f; f = files[i]; i++) {
            var reader = new FileReader();
            var file = f;
            reader.onload = function(event) {
                object = {};
                object.filename = file.name;
                object.data = event.target.result;
                var options = {
                    method: 'post',
                    url: '/file',
                    data: object
                }
                snack.request(options, function (err, res) {
                    if (err) {
                        alert("error uploading file: " + err);
                    }
                    replaceSelection(editor, "[["+res+"]]");
                });
            };
            reader.readAsDataURL(f);
        }
    }

});<|endoftext|>


<commit_before>var app = require('app');  // Module to control application life.
var request = require('request');
var BrowserWindow = require('browser-window');  // Module to create native browser window.
var Menu = require('menu');
var Dialog = require('dialog');
var Fs = require('fs');
var XLSX = require('xlsx');
var ipc = require('ipc');

// Report crashes to our server.
require('crash-reporter').start();

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is GCed.
var mainWindow = null;

// 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() {
  var template = [
    {
      label: 'Electron',
      submenu: [
        {
          label: 'About Electron',
          selector: 'orderFrontStandardAboutPanel:'
        },
        {
          type: 'separator'
        },
        {
          label: 'Services',
          submenu: []
        },
        {
          type: 'separator'
        },
        {
          label: 'Hide Electron',
          accelerator: 'CmdOrCtrl+H',
          selector: 'hide:'
        },
        {
          label: 'Hide Others',
          accelerator: 'CmdOrCtrl+Shift+H',
          selector: 'hideOtherApplications:'
        },
        {
          label: 'Show All',
          selector: 'unhideAllApplications:'
        },
        {
          type: 'separator'
        },
        {
          label: 'Quit',
          accelerator: 'CmdOrCtrl+Q',
          selector: 'terminate:'
        },
      ]
    },
    {
      label: 'File',
      submenu: [
        {
          label: 'New',
          accelerator: 'CmdOrCtrl+N',
          click: function() { createWindow(); }
        },
        {
          label: 'Open..',
          accelerator: 'CmdOrCtrl+O',
          click: function() { openFile(); }
        },
        {
          label: 'Import Excel file',
          click: function() { importExcel(); }
        },
        {
          label: 'Save As..',
          accelerator: 'Shift+CmdOrCtrl+S',
          click: function() { saveFile(); }
        },
        {
          label: 'Validate',
          accelerator: 'CmdOrCtrl+V',
          click: function() { validateFile(); }
        }
      ]
    },
    {
      label: 'Edit',
      submenu: [
        {
          label: 'Undo',
          accelerator: 'CmdOrCtrl+Z',
          selector: 'undo:'
        },
        {
          label: 'Redo',
          accelerator: 'Shift+CmdOrCtrl+Z',
          selector: 'redo:'
        },
        {
          type: 'separator'
        },
        {
          label: 'Cut',
          accelerator: 'CmdOrCtrl+X',
          selector: 'cut:'
        },
        {
          label: 'Copy',
          accelerator: 'CmdOrCtrl+C',
          selector: 'copy:'
        },
        {
          label: 'Paste',
          accelerator: 'CmdOrCtrl+V',
          selector: 'paste:'
        },
        {
          label: 'Select All',
          accelerator: 'CmdOrCtrl+A',
          selector: 'selectAll:'
        }
      ]
    },
    {
      label: 'View',
      submenu: [
        {
          label: 'Reload',
          accelerator: 'CmdOrCtrl+R',
          click: function() { BrowserWindow.getFocusedWindow().reload(); }
        },
        {
          label: 'Toggle DevTools',
          accelerator: 'Alt+CmdOrCtrl+I',
          click: function() { BrowserWindow.getFocusedWindow().toggleDevTools(); }
        },
      ]
    },
    {
      label: 'Window',
      submenu: [
        {
          label: 'Minimize',
          accelerator: 'CmdOrCtrl+M',
          selector: 'performMiniaturize:'
        },
        {
          label: 'Close',
          accelerator: 'CmdOrCtrl+W',
          selector: 'performClose:'
        },
        {
          type: 'separator'
        },
        {
          label: 'Bring All to Front',
          selector: 'arrangeInFront:'
        }
      ]
    },
    {
      label: 'Help',
      submenu: []
    }
  ];

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

  // Create the browser window.
  createWindow();
});

function createWindow(data, title) {
  data = typeof data !== 'undefined' ? data : '"","",""';
  title = typeof title !== 'undefined' ? title : "Untitled.csv";

  mainWindow = new BrowserWindow({width: 800, height: 600});

  // and load the index.html of the app.
  mainWindow.loadUrl('file://' + __dirname + '/index.html');

  // Open the devtools.
  mainWindow.openDevTools();

  mainWindow.webContents.on('did-finish-load', function() {
    mainWindow.setTitle(title);
    mainWindow.webContents.send('loadData', data);
  });

  // 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;
  });
}

function openFile() {
    Dialog.showOpenDialog(
        { filters: [
            { name: 'csv files', extensions: ['csv'] },
            { name: 'json schemas', extensions: ['json'] }
        ]}, function (fileNames) {
            if (fileNames === undefined) {
                return;
            } else {
                console.log("the file processed = "+JSON.stringify(fileNames));
                var fileName = fileNames[0];
                Fs.readFile(fileName, 'utf-8', function (err, data) {
                    createWindow(data, fileName);
                });
            }
        });
}

function saveFile() {
  window = BrowserWindow.getFocusedWindow();
  Dialog.showSaveDialog({ filters: [
    { name: 'text', extensions: ['csv'] }
  ]}, function (fileName) {
    if (fileName === undefined) return;
    window.webContents.send('saveData', fileName);
  });
}

function importExcel() {
  Dialog.showOpenDialog({ filters: [
    { name: 'text', extensions: ['xlsx', 'xls'] }
  ]}, function (fileNames) {
    if (fileNames === undefined) return;
    var fileName = fileNames[0];
    var workbook = XLSX.readFile(fileName);
    var first_sheet_name = workbook.SheetNames[0];
    var worksheet = workbook.Sheets[first_sheet_name];

    popup = new BrowserWindow({width: 300, height: 150, 'always-on-top': true});
    popup.loadUrl('file://' + __dirname + '/select_worksheet.html');

    popup.webContents.on('did-finish-load', function() {
      popup.webContents.send('loadSheets', workbook.SheetNames);

      ipc.on('worksheetSelected', function(e, sheet_name) {
        data = XLSX.utils.sheet_to_csv(workbook.Sheets[sheet_name]);
        popup.close();
        createWindow(data);
      });

      ipc.on('worksheetCanceled', function() {
        popup.close();
      });
    });

    popup.on('closed', function() {
      popup = null;
    });
  });
}

function validateFile() {
  window = BrowserWindow.getFocusedWindow();
  window.webContents.send('validate');
}
<commit_msg>Change ipc.on to ipc.once<commit_after>var app = require('app');  // Module to control application life.
var request = require('request');
var BrowserWindow = require('browser-window');  // Module to create native browser window.
var Menu = require('menu');
var Dialog = require('dialog');
var Fs = require('fs');
var XLSX = require('xlsx');
var ipc = require('ipc');

// Report crashes to our server.
require('crash-reporter').start();

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is GCed.
var mainWindow = null;

// 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() {
  var template = [
    {
      label: 'Electron',
      submenu: [
        {
          label: 'About Electron',
          selector: 'orderFrontStandardAboutPanel:'
        },
        {
          type: 'separator'
        },
        {
          label: 'Services',
          submenu: []
        },
        {
          type: 'separator'
        },
        {
          label: 'Hide Electron',
          accelerator: 'CmdOrCtrl+H',
          selector: 'hide:'
        },
        {
          label: 'Hide Others',
          accelerator: 'CmdOrCtrl+Shift+H',
          selector: 'hideOtherApplications:'
        },
        {
          label: 'Show All',
          selector: 'unhideAllApplications:'
        },
        {
          type: 'separator'
        },
        {
          label: 'Quit',
          accelerator: 'CmdOrCtrl+Q',
          selector: 'terminate:'
        },
      ]
    },
    {
      label: 'File',
      submenu: [
        {
          label: 'New',
          accelerator: 'CmdOrCtrl+N',
          click: function() { createWindow(); }
        },
        {
          label: 'Open..',
          accelerator: 'CmdOrCtrl+O',
          click: function() { openFile(); }
        },
        {
          label: 'Import Excel file',
          click: function() { importExcel(); }
        },
        {
          label: 'Save As..',
          accelerator: 'Shift+CmdOrCtrl+S',
          click: function() { saveFile(); }
        },
        {
          label: 'Validate',
          accelerator: 'CmdOrCtrl+V',
          click: function() { validateFile(); }
        }
      ]
    },
    {
      label: 'Edit',
      submenu: [
        {
          label: 'Undo',
          accelerator: 'CmdOrCtrl+Z',
          selector: 'undo:'
        },
        {
          label: 'Redo',
          accelerator: 'Shift+CmdOrCtrl+Z',
          selector: 'redo:'
        },
        {
          type: 'separator'
        },
        {
          label: 'Cut',
          accelerator: 'CmdOrCtrl+X',
          selector: 'cut:'
        },
        {
          label: 'Copy',
          accelerator: 'CmdOrCtrl+C',
          selector: 'copy:'
        },
        {
          label: 'Paste',
          accelerator: 'CmdOrCtrl+V',
          selector: 'paste:'
        },
        {
          label: 'Select All',
          accelerator: 'CmdOrCtrl+A',
          selector: 'selectAll:'
        }
      ]
    },
    {
      label: 'View',
      submenu: [
        {
          label: 'Reload',
          accelerator: 'CmdOrCtrl+R',
          click: function() { BrowserWindow.getFocusedWindow().reload(); }
        },
        {
          label: 'Toggle DevTools',
          accelerator: 'Alt+CmdOrCtrl+I',
          click: function() { BrowserWindow.getFocusedWindow().toggleDevTools(); }
        },
      ]
    },
    {
      label: 'Window',
      submenu: [
        {
          label: 'Minimize',
          accelerator: 'CmdOrCtrl+M',
          selector: 'performMiniaturize:'
        },
        {
          label: 'Close',
          accelerator: 'CmdOrCtrl+W',
          selector: 'performClose:'
        },
        {
          type: 'separator'
        },
        {
          label: 'Bring All to Front',
          selector: 'arrangeInFront:'
        }
      ]
    },
    {
      label: 'Help',
      submenu: []
    }
  ];

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

  // Create the browser window.
  createWindow();
});

function createWindow(data, title) {
  data = typeof data !== 'undefined' ? data : '"","",""';
  title = typeof title !== 'undefined' ? title : "Untitled.csv";

  mainWindow = new BrowserWindow({width: 800, height: 600});

  // and load the index.html of the app.
  mainWindow.loadUrl('file://' + __dirname + '/index.html');

  // Open the devtools.
  mainWindow.openDevTools();

  mainWindow.webContents.on('did-finish-load', function() {
    mainWindow.setTitle(title);
    mainWindow.webContents.send('loadData', data);
  });

  // 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;
  });
}

function openFile() {
    Dialog.showOpenDialog(
        { filters: [
            { name: 'csv files', extensions: ['csv'] },
            { name: 'json schemas', extensions: ['json'] }
        ]}, function (fileNames) {
            if (fileNames === undefined) {
                return;
            } else {
                console.log("the file processed = "+JSON.stringify(fileNames));
                var fileName = fileNames[0];
                Fs.readFile(fileName, 'utf-8', function (err, data) {
                    createWindow(data, fileName);
                });
            }
        });
}

function saveFile() {
  window = BrowserWindow.getFocusedWindow();
  Dialog.showSaveDialog({ filters: [
    { name: 'text', extensions: ['csv'] }
  ]}, function (fileName) {
    if (fileName === undefined) return;
    window.webContents.send('saveData', fileName);
  });
}

function importExcel() {
  Dialog.showOpenDialog({ filters: [
    { name: 'text', extensions: ['xlsx', 'xls'] }
  ]}, function (fileNames) {
    if (fileNames === undefined) return;
    var fileName = fileNames[0];
    var workbook = XLSX.readFile(fileName);
    var first_sheet_name = workbook.SheetNames[0];
    var worksheet = workbook.Sheets[first_sheet_name];

    popup = new BrowserWindow({width: 300, height: 150, 'always-on-top': true});
    popup.loadUrl('file://' + __dirname + '/select_worksheet.html');

    popup.webContents.on('did-finish-load', function() {
      popup.webContents.send('loadSheets', workbook.SheetNames);

      ipc.once('worksheetSelected', function(e, sheet_name) {
        data = XLSX.utils.sheet_to_csv(workbook.Sheets[sheet_name]);
        popup.close();
        createWindow(data);
      });

      ipc.once('worksheetCanceled', function() {
        popup.close();
      });
    });

    popup.on('closed', function() {
      popup = null;
    });
  });
}

function validateFile() {
  window = BrowserWindow.getFocusedWindow();
  window.webContents.send('validate');
}
<|endoftext|>


<commit_before>
var noteManager = function (options) {
    this.parameters = options;
    this.defaults = {
        option: 'value'
    };
};

noteManager.prototype = {

    init: function() {
      // merge defaults options and user's ones
      this.options = $.extend({}, this.defaults, this.parameters);
      this.plugEvents();
    },

    plugEvents: function() {

      this.displayNotes();
      var that = this;

      $('button[type="submit"]').on('click', function(e){
        e.preventDefault();

        var note = that.getNote($(this).closest('form'));
        that.saveNote(note);
        that.clearForm();
        that.displaySingleNote(note);
      });

      $('#dropbox').on('click', $.proxy(function(){

        var id = this.createJSONfile($.parseJSON(localStorage.getItem("WebNotes")));
        // FIXME permission denied on univ server
        Dropbox.save("https://etudiant.univ-mlv.fr/~odaire/WebNotes/temp/WebNotes-"+id+".json", "WebNotes");

      }, this));

      $('#delete').on('click', $.proxy(function(){
        this.deleteNotes();
      }, this));

      $(document).on('click', '.note button.delete', function(){
        var id = $(this).parent().attr('id');
        id = id.substr(5, id.length);
        that.deleteSingleNote(id);
      });

      $(document).on('click', '.note button.edit', function(){
        var id = $(this).parent().attr('id');
        id = id.substr(5, id.length);
        that.editNote(id);
      });

      // Only when editing a note
      $(document).on('click', '.note button[type="submit"]', function(e){
        e.preventDefault();
        var note = that.getNote($(this).closest('form'));
        that.saveNote(note);

        var id = $(this).closest('form').parent().attr('id');
        id = id.substr(5, id.length);
        that.deleteSingleNote(id);

        that.displaySingleNote(note);
      });

    },

    /******************
      CORE FUNCTIONS
    ******************/

    /**
     * Get form data and return it as a JSON object
     * @param {HTML Object} form   The form generating this note
     * @return {JSON}   Note as JSON object
     */
    getNote: function(form) {
      var title = form.find('.title').val(),
          content = form.find('textarea').val(),
          url = this.containsURL(content);
          today = this.formatDate(),
          date = today[0],
          time = today[1],
          tags = this.formatTags(form.find('.tags').val());

      var note = this.formatNote(title, content, date, time, tags, url);
      return note;
    },

    /**
     * Format note to JSON with form data
     * @param  {string} title     Note title
     * @param  {string} content   Note text content
     * @param  {string} date      Note date
     * @param  {string} time      Note hour
     * @param  {string} tags      Note tags
     * @param  {string} url       URL contained in note
     * @return {JSON}             Note as JSON object
     */
    formatNote: function(title, content, date, time, tags, url) {
      var note = {
            "title": title,
            "content": content,
            "date": date,
            "time": time,
            "tags": tags,
            "url": url,
          };

      return note;
    },

    /**
     * If notes already exist in local storage, add the new one and save
     * else create the object and save it in local storage.
     * @param  {JSON}
     */
    saveNote: function(note) {
      var notes;
      if (localStorage.getItem("WebNotes") === null) {
        notes = [ note ];
        notes = JSON.stringify(notes);
        localStorage.setItem("WebNotes", notes);
      }else{
        notes = $.parseJSON(localStorage.getItem("WebNotes"));
        notes.push(note);
        notes = JSON.stringify(notes);
        localStorage.setItem("WebNotes", notes);
      }
    },

    /**
     * Display all notes
     */
    displayNotes: function() {
      if (localStorage.getItem("WebNotes") !== null) {

        var notes = $.parseJSON(localStorage.getItem("WebNotes"));
        var tags;

        for (var i = notes.length-1 ; i >= 0; i--) {
          for (var j = 0; j < notes[i].tags.length; j++) {
            if (j === 0 ) {
              tags = '' + notes[i].tags[j] + '';
            }else{
              tags = tags + ' ' + notes[i].tags[j] + '';
            }
          }

          $('#main').append(
            '
'+
              '
'+notes[i].title+'
'+
              '
'+notes[i].content+'
'+
              '
'+ tags +'
'+
              ''+notes[i].date+' - '+notes[i].time+''+
              '<button class="edit">Edit</button>'+
              '<button class="delete">Delete</button>'+
            '
'
          );

          if (notes[i].url) {
            this.generateWidget(i, notes[i].url);
          }
        }

      }
    },

    /**
     * Display a single note
     * @param {JSON} note
     */
    displaySingleNote: function(note) {
      var tags;
      var notesLength = $.parseJSON(localStorage.getItem("WebNotes")).length;
      notesLength = notesLength-1; // Number of next note

      for (var j = 0; j < note.tags.length; j++) {
          if (j === 0 ) {
            tags = '' + note.tags[j] + '';
          }else{
            tags = tags + ' ' + note.tags[j] + '';
          }
      }

       $('#main').prepend(
            '
'+
              '
'+note.title+'
'+
              '
'+note.content+'
'+
              '
'+ tags +'
'+
              ''+note.date+' - '+note.time+''+
              '<button class="edit">Edit</button>'+
              '<button class="delete">Delete</button>'+
            '
'
        );

      if (note.url) {
        this.generateWidget(notesLength, note.url);
      }
    },

    /**
     * Delete all notes
     */
    deleteNotes: function() {
      localStorage.removeItem("WebNotes");
    },

    /**
     * Delete a single note
     * @param  {int} id  ID of the note to be deleted
     */
    deleteSingleNote: function(id) {
      var notes = $.parseJSON(localStorage.getItem("WebNotes"));
      if (id != -1) {
        notes.splice(id, 1);
        notes = JSON.stringify(notes);
        localStorage.setItem("WebNotes", notes);
        $('#note-'+id+'').remove();
      }
    },

    /**
     * Edit a note
     * @param  {int} id ID of the note to be edited
     */
    editNote: function(id) {
      var notes = $.parseJSON(localStorage.getItem("WebNotes"));
      var note = notes[id];

     $('#note-'+id).html(
        '<form>'+
          '<input type="text" class="title" value="'+note.title+'">'+
          '<textarea>'+note.content+'</textarea>'+
          '<input name="tags" class="tags" value="'+note.tags+'" />'+
          '<button type="submit">Save</button>'+
        '</form>'
      );

     $('.tags').tagsInput();
    },

    /******************
      TOOLS FUNCTIONS
    ******************/

    clearForm: function() {
      $('form input.title, form textarea').val('');
      $('div.tagsinput span').remove();
    },

    /**
     * Get current date and time and format it
     * @return {array}    containing current date and hour
     */
    formatDate: function() {
      var today = new Date();
      var dd = today.getDate();
      var mm = today.getMonth()+1; //January is 0!
      var yyyy = today.getFullYear();

      if(dd<10){
          dd='0'+dd;
      }
      if(mm<10){
          mm='0'+mm;
      }

      var h = today.getHours();
      var m = today.getMinutes();
      var s = today.getSeconds();

      today = dd+'/'+mm+'/'+yyyy;

      var time = h + 'h' + m + 'min' + s + 's';

      return [today, time];
    },

    /**
     * Convert a list of tags into an array
     * @param  {string} tags    list of tags
     * @return {array}          array of tags
     */
    formatTags: function(tags) {
      var tagsArray = tags.split(',');

      return tagsArray;
    },

    /**
     * Ajax request to generate a JSON file through a PHP script
     * @param  {JSON} notes    object containing all notes
     */
    createJSONfile: function(notes) {
      // TODO GET unique ID from php and return it
      $.ajax({
        type : "POST",
        url : "json.php",
        dataType : 'json',
        data : {
            json : JSON.stringify(notes)
        },
        error: function(data){
          console.log('Error generating JSON');
          console.log(data);
        },
        success: function(data){
          return data;
        }
      });
    },

    /**
     * Check for an URL inside a string
     * @param  {string} s
     * @return {string}   Matched URL
     */
    containsURL: function(s) {
      var regexp = /(http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
      if (regexp.exec(s)) {
        return regexp.exec(s)[0];
      }
    },

    /**
     * Test for compatible websites and return a widget or a picture
     * @param  {int}    id ID of the note containing the url
     * @param  {string} s  URL to be tested
     * @return {string}    HTML tags or picture URL
     */
    generateWidget: function(id, s) {
      var regexp = /(youtube\.com|youtu\.be|soundcloud\.com|imdb\.com|allocine\.fr)/;

      switch (regexp.exec(s)[0]) {
        case 'youtube.com':
        case 'youtu.be':

        break;
        case 'soundcloud.com':
          // FIXME Fidn out about the client id, mandatory ?
          SC.initialize({
            client_id: 'YOUR_CLIENT_ID'
          });

          var track_url = s;
          SC.oEmbed(track_url, { auto_play: false }, function(oEmbed) {
            $('#note-'+id).prepend(oEmbed.html);
          });
        break;
        case 'imdb.com':

        break;
        case 'allocine.fr':

        break;
      }
    },

};
<commit_msg>Youtube iframe generation<commit_after>
var noteManager = function (options) {
    this.parameters = options;
    this.defaults = {
        option: 'value'
    };
};

noteManager.prototype = {

    init: function() {
      // merge defaults options and user's ones
      this.options = $.extend({}, this.defaults, this.parameters);
      this.plugEvents();
    },

    plugEvents: function() {

      this.displayNotes();
      var that = this;

      $('button[type="submit"]').on('click', function(e){
        e.preventDefault();

        var note = that.getNote($(this).closest('form'));
        that.saveNote(note);
        that.clearForm();
        that.displaySingleNote(note);
      });

      $('#dropbox').on('click', $.proxy(function(){

        var id = this.createJSONfile($.parseJSON(localStorage.getItem("WebNotes")));
        // FIXME permission denied on univ server
        Dropbox.save("https://etudiant.univ-mlv.fr/~odaire/WebNotes/temp/WebNotes-"+id+".json", "WebNotes");

      }, this));

      $('#delete').on('click', $.proxy(function(){
        this.deleteNotes();
      }, this));

      $(document).on('click', '.note button.delete', function(){
        var id = $(this).parent().attr('id');
        id = id.substr(5, id.length);
        that.deleteSingleNote(id);
      });

      $(document).on('click', '.note button.edit', function(){
        var id = $(this).parent().attr('id');
        id = id.substr(5, id.length);
        that.editNote(id);
      });

      // Only when editing a note
      $(document).on('click', '.note button[type="submit"]', function(e){
        e.preventDefault();
        var note = that.getNote($(this).closest('form'));
        that.saveNote(note);

        var id = $(this).closest('form').parent().attr('id');
        id = id.substr(5, id.length);
        that.deleteSingleNote(id);

        that.displaySingleNote(note);
      });

    },

    /******************
      CORE FUNCTIONS
    ******************/

    /**
     * Get form data and return it as a JSON object
     * @param {HTML Object} form   The form generating this note
     * @return {JSON}   Note as JSON object
     */
    getNote: function(form) {
      var title = form.find('.title').val(),
          content = form.find('textarea').val(),
          url = this.containsURL(content);
          today = this.formatDate(),
          date = today[0],
          time = today[1],
          tags = this.formatTags(form.find('.tags').val());

      var note = this.formatNote(title, content, date, time, tags, url);
      return note;
    },

    /**
     * Format note to JSON with form data
     * @param  {string} title     Note title
     * @param  {string} content   Note text content
     * @param  {string} date      Note date
     * @param  {string} time      Note hour
     * @param  {string} tags      Note tags
     * @param  {string} url       URL contained in note
     * @return {JSON}             Note as JSON object
     */
    formatNote: function(title, content, date, time, tags, url) {
      var note = {
            "title": title,
            "content": content,
            "date": date,
            "time": time,
            "tags": tags,
            "url": url,
          };

      return note;
    },

    /**
     * If notes already exist in local storage, add the new one and save
     * else create the object and save it in local storage.
     * @param  {JSON}
     */
    saveNote: function(note) {
      var notes;
      if (localStorage.getItem("WebNotes") === null) {
        notes = [ note ];
        notes = JSON.stringify(notes);
        localStorage.setItem("WebNotes", notes);
      }else{
        notes = $.parseJSON(localStorage.getItem("WebNotes"));
        notes.push(note);
        notes = JSON.stringify(notes);
        localStorage.setItem("WebNotes", notes);
      }
    },

    /**
     * Display all notes
     */
    displayNotes: function() {
      if (localStorage.getItem("WebNotes") !== null) {

        var notes = $.parseJSON(localStorage.getItem("WebNotes"));
        var tags;

        for (var i = notes.length-1 ; i >= 0; i--) {
          for (var j = 0; j < notes[i].tags.length; j++) {
            if (j === 0 ) {
              tags = '' + notes[i].tags[j] + '';
            }else{
              tags = tags + ' ' + notes[i].tags[j] + '';
            }
          }

          $('#main').append(
            '
'+
              '
'+notes[i].title+'
'+
              '
'+notes[i].content+'
'+
              '
'+ tags +'
'+
              ''+notes[i].date+' - '+notes[i].time+''+
              '<button class="edit">Edit</button>'+
              '<button class="delete">Delete</button>'+
            '
'
          );

          if (notes[i].url) {
            this.generateWidget(i, notes[i].url);
          }
        }

      }
    },

    /**
     * Display a single note
     * @param {JSON} note
     */
    displaySingleNote: function(note) {
      var tags;
      var notesLength = $.parseJSON(localStorage.getItem("WebNotes")).length;
      notesLength = notesLength-1; // Number of next note

      for (var j = 0; j < note.tags.length; j++) {
          if (j === 0 ) {
            tags = '' + note.tags[j] + '';
          }else{
            tags = tags + ' ' + note.tags[j] + '';
          }
      }

       $('#main').prepend(
            '
'+
              '
'+note.title+'
'+
              '
'+note.content+'
'+
              '
'+ tags +'
'+
              ''+note.date+' - '+note.time+''+
              '<button class="edit">Edit</button>'+
              '<button class="delete">Delete</button>'+
            '
'
        );

      if (note.url) {
        this.generateWidget(notesLength, note.url);
      }
    },

    /**
     * Delete all notes
     */
    deleteNotes: function() {
      localStorage.removeItem("WebNotes");
    },

    /**
     * Delete a single note
     * @param  {int} id  ID of the note to be deleted
     */
    deleteSingleNote: function(id) {
      var notes = $.parseJSON(localStorage.getItem("WebNotes"));
      if (id != -1) {
        notes.splice(id, 1);
        notes = JSON.stringify(notes);
        localStorage.setItem("WebNotes", notes);
        $('#note-'+id+'').remove();
      }
    },

    /**
     * Edit a note
     * @param  {int} id ID of the note to be edited
     */
    editNote: function(id) {
      var notes = $.parseJSON(localStorage.getItem("WebNotes"));
      var note = notes[id];

     $('#note-'+id).html(
        '<form>'+
          '<input type="text" class="title" value="'+note.title+'">'+
          '<textarea>'+note.content+'</textarea>'+
          '<input name="tags" class="tags" value="'+note.tags+'" />'+
          '<button type="submit">Save</button>'+
        '</form>'
      );

     $('.tags').tagsInput();
    },

    /******************
      TOOLS FUNCTIONS
    ******************/

    clearForm: function() {
      $('form input.title, form textarea').val('');
      $('div.tagsinput span').remove();
    },

    /**
     * Get current date and time and format it
     * @return {array}    containing current date and hour
     */
    formatDate: function() {
      var today = new Date();
      var dd = today.getDate();
      var mm = today.getMonth()+1; //January is 0!
      var yyyy = today.getFullYear();

      if(dd<10){
          dd='0'+dd;
      }
      if(mm<10){
          mm='0'+mm;
      }

      var h = today.getHours();
      var m = today.getMinutes();
      var s = today.getSeconds();

      today = dd+'/'+mm+'/'+yyyy;

      var time = h + 'h' + m + 'min' + s + 's';

      return [today, time];
    },

    /**
     * Convert a list of tags into an array
     * @param  {string} tags    list of tags
     * @return {array}          array of tags
     */
    formatTags: function(tags) {
      var tagsArray = tags.split(',');

      return tagsArray;
    },

    /**
     * Ajax request to generate a JSON file through a PHP script
     * @param  {JSON} notes    object containing all notes
     */
    createJSONfile: function(notes) {
      // TODO GET unique ID from php and return it
      $.ajax({
        type : "POST",
        url : "json.php",
        dataType : 'json',
        data : {
            json : JSON.stringify(notes)
        },
        error: function(data){
          console.log('Error generating JSON');
          console.log(data);
        },
        success: function(data){
          return data;
        }
      });
    },

    /**
     * Check for an URL inside a string
     * @param  {string} s
     * @return {string}   Matched URL
     */
    containsURL: function(s) {
      var regexp = /(http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
      if (regexp.exec(s)) {
        return regexp.exec(s)[0];
      }
    },

    /**
     * Test for compatible websites and return a widget or a picture
     * @param  {int}    id ID of the note containing the url
     * @param  {string} s  URL to be tested
     * @return {string}    HTML tags or picture URL
     */
    generateWidget: function(id, s) {
      var regexp = /(youtube\.com|youtu\.be|soundcloud\.com|imdb\.com|allocine\.fr)/;

      switch (regexp.exec(s)[0]) {
        case 'youtube.com':
        case 'youtu.be':
          //http://www.youtube.com/embed/M7lc1UVf-VE?autoplay=1&origin=http://example.com
          console.log(s);
          s = this.getYoutubeId(s);
          var iframe = '';
          $('#note-'+id).prepend(iframe);
        break;

        case 'soundcloud.com':
          SC.initialize({
            client_id: '174356325111c05f60352760c8f377b2'
          });

          var track_url = s;
          SC.oEmbed(track_url, { auto_play: false, show_comments: false }, function(oEmbed) {
            $('#note-'+id).prepend(oEmbed.html);
          });
        break;

        case 'imdb.com':

        break;

        case 'allocine.fr':

        break;
      }
    },

    getYoutubeId: function(url){
      var ID = '';
      url = url.replace(/(>|<)/gi,'').split(/(vi\/|v=|\/v\/|youtu\.be\/|\/embed\/)/);
      if(url[2] !== undefined) {
        ID = url[2].split(/[^0-9a-z_]/i);
        ID = ID[0];
      }
      else {
        ID = url;
      }
        return ID;
    },

};
<|endoftext|>


<commit_before>/**
 * @file AutoCompleteFilter component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {findDOMNode} from 'react-dom';

import TextField from '../TextField';
import Popup from '../Popup';
import List from '../List';
import DynamicRenderList from '../DynamicRenderList';
import Theme from '../Theme';

import Util from '../_vendors/Util';
import DropdownCalculation from '../_vendors/DropdownCalculation';

export default class AutoCompleteFilter extends Component {

    static Theme = Theme;

    static propTypes = {

        /**
         * The CSS class name of the root element.
         */
        className: PropTypes.string,

        /**
         * The CSS class name of the popup element.
         */
        popupClassName: PropTypes.string,

        /**
         * Override the styles of the root element.
         */
        style: PropTypes.object,

        /**
         * Override the styles of the popup element.
         */
        popupStyle: PropTypes.object,

        /**
         * The theme.
         */
        theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

        /**
         * The theme.
         */
        popupTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),

        /**
         * The name of the auto complete.
         */
        name: PropTypes.string,

        /**
         * The placeholder of the field.
         */
        placeholder: PropTypes.string,

        /**
         * Children passed into the List.
         */
        data: PropTypes.oneOfType([

            // not grouped
            PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.shape({

                /**
                 * The CSS class name of the list button.
                 */
                className: PropTypes.string,

                /**
                 * Override the styles of the list button.
                 */
                style: PropTypes.object,

                /**
                 * The theme of the list button.
                 */
                theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

                /**
                 * The text value of the list button.Type can be string or number.
                 */
                value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

                /**
                 * The desc value of the list button. Type can be string or number.
                 */
                desc: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

                /**
                 * If true, the list button will be disabled.
                 */
                disabled: PropTypes.bool,

                /**
                 * If true,the button will be have loading effect.
                 */
                isLoading: PropTypes.bool,

                /**
                 * If true,the element's ripple effect will be disabled.
                 */
                disableTouchRipple: PropTypes.bool,

                /**
                 * Use this property to display an icon. It will display on the left.
                 */
                iconCls: PropTypes.string,

                /**
                 * Use this property to display an icon. It will display on the right.
                 */
                rightIconCls: PropTypes.string,

                /**
                 * You can create a complicated renderer callback instead of value and desc prop.
                 */
                itemRenderer: PropTypes.func,

                /**
                 * Callback function fired when a list item touch-tapped.
                 */
                onTouchTap: PropTypes.func

            }), PropTypes.string, PropTypes.number])),

            // grouped
            PropTypes.array

        ]).isRequired,

        /**
         * If true, the auto complete will be disabled.
         */
        disabled: PropTypes.bool,

        /**
         * The value field name in data. (default: "value")
         */
        valueField: PropTypes.string,

        /**
         * The display field name in data. (default: "text")
         */
        displayField: PropTypes.string,

        /**
         * The description field name in data. (default: "desc")
         */
        descriptionField: PropTypes.string,

        /**
         * If true, the popup list automatically closed after selection.
         */
        autoClose: PropTypes.bool,

        /**
         * Callback function fired when filter changed.
         */
        filterCallback: PropTypes.func,

        /**
         * Use this property to display an icon.
         */
        iconCls: PropTypes.string,

        /**
         * Use this property to display an icon.
         */
        rightIconCls: PropTypes.string,

        /**
         * The visiblity of no matched popup.
         */
        noMatchedPopupVisible: PropTypes.bool,

        /**
         * The message of no matched value.
         */
        noMatchedMsg: PropTypes.string,

        popupChildren: PropTypes.any,

        filterInitValue: PropTypes.string,

        useDynamicRenderList: PropTypes.bool,
        listHeight: PropTypes.number,
        itemHeight: PropTypes.number,
        scrollBuffer: PropTypes.number,

        /**
         * You can create a complicated renderer callback instead of value and desc prop.
         */
        renderer: PropTypes.func,

        /**
         * The function that trigger when filter changes.
         */
        onFilterChange: PropTypes.func,

        /**
         * The function that trigger when press enter.
         */
        onFilterPressEnter: PropTypes.func,

        /**
         * The function that trigger when filter cleared.
         */
        onFilterClear: PropTypes.func,

        /**
         * The function that trigger when value changed.
         */
        onChange: PropTypes.func,

        /**
         * The function that trigger when touch-tap the list items.
         */
        onItemTouchTap: PropTypes.func,

        /**
         * Callback function fired when AutoCompleteFilter get focus.
         */
        onFocus: PropTypes.func,

        /**
         * Callback function fired when AutoCompleteFilter lose focus.
         */
        onBlur: PropTypes.func,

        onTriggerMouseOver: PropTypes.func,
        onTriggerMouseOut: PropTypes.func,
        onPopupClosed: PropTypes.func

    };

    static defaultProps = {

        className: null,
        popupClassName: null,
        style: null,
        popupStyle: null,
        theme: Theme.DEFAULT,
        popupTheme: Theme.DEFAULT,

        name: null,
        placeholder: null,
        data: [],
        disabled: false,
        valueField: 'value',
        displayField: 'text',
        descriptionField: 'desc',
        autoClose: false,
        iconCls: null,
        rightIconCls: 'fa fa-search',
        noMatchedPopupVisible: true,
        noMatchedMsg: null,
        filterInitValue: '',

        popupChildren: null,

        useDynamicRenderList: false

    };

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.triggerEl = null;

        this.state = {
            value: props.value,
            filter: props.filterInitValue,
            popupVisible: false,
            isAbove: false
        };

        this.filterData = ::this.filterData;
        this.filterFocusHandler = ::this.filterFocusHandler;
        this.filterBlurHandler = ::this.filterBlurHandler;
        this.filterPressEnterHandler = ::this.filterPressEnterHandler;
        this.filterChangeHandler = ::this.filterChangeHandler;
        this.closePopup = ::this.closePopup;
        this.popupRenderHandler = ::this.popupRenderHandler;
        this.changeHandler = ::this.changeHandler;

    }

    filterData(filter = this.state.filter, data = this.props.data) {

        if (!filter) {
            return data;
        }

        const {valueField, displayField, renderer, filterCallback} = this.props;

        if (filterCallback) {
            return filterCallback(filter, data);
        }

        return data.filter(item => {

            if (!item) {
                return false;
            }

            if (renderer) {
                return renderer(item).toString().toUpperCase().includes(filter.toUpperCase());
            }

            if (typeof item === 'object') {

                const itemDisplay = Util.getTextByDisplayField(item, displayField, valueField);

                if (itemDisplay) {
                    return itemDisplay.toString().toUpperCase().includes(filter.toUpperCase());
                }

            }

            return item.toString().toUpperCase().includes(filter.toUpperCase());

        });

    }

    filterFocusHandler(...args) {

        const {disabled, onFocus} = this.props,
            {filter} = this.state;

        onFocus && onFocus(...args);

        !disabled && filter && this.setState({
            popupVisible: true
        });

    }

    filterBlurHandler(...args) {
        const {disabled, onBlur} = this.props;
        !disabled && onBlur && onBlur(...args);
    }

    filterPressEnterHandler(e, filter) {

        const {autoClose} = this.props,
            callback = () => {
                const {onFilterPressEnter} = this.props;
                onFilterPressEnter && onFilterPressEnter(filter);
            };

        if (autoClose) {
            this.setState({
                popupVisible: false
            }, () => {
                callback();
            });
        } else {
            callback();
        }

    }

    filterChangeHandler(filter) {

        const value = this.state.value,
            state = {
                filter,
                popupVisible: !!filter
            };

        if (!filter) {
            state.value = undefined;
        }

        this.setState(state, () => {
            const {onFilterChange} = this.props;
            onFilterChange && onFilterChange(filter);
        });

    }

    closePopup() {
        this.setState({
            popupVisible: false
        }, () => {
            const {onPopupClosed} = this.props;
            onPopupClosed && onPopupClosed();
        });
    }

    popupRenderHandler(popupEl) {

        const isAbove = DropdownCalculation.isAbove(this.localAutoCompleteEl, this.triggerEl, findDOMNode(popupEl));

        if (isAbove !== this.state.isAbove) {
            this.setState({
                isAbove
            });
        }

    }

    changeHandler(value) {

        const {autoClose, valueField, displayField, renderer} = this.props,
            state = {
                value,
                filter: renderer ? renderer(value) : Util.getTextByDisplayField(value, displayField, valueField)
            };

        if (autoClose) {
            state.popupVisible = false;
        }

        this.setState(state, () => {
            const {onChange} = this.props;
            onChange && onChange(value);
        });

    }

    componentDidMount() {
        this.localAutoCompleteEl = this.refs.localAutoComplete;
        this.triggerEl = findDOMNode(this.refs.trigger);
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.state.value) {
            this.setState({
                value: nextProps.value
            });
        }
    }

    render() {

        const {
                className, popupClassName, style, popupStyle, theme, popupTheme, name, placeholder,
                disabled, iconCls, rightIconCls, valueField, displayField, descriptionField,
                noMatchedPopupVisible, noMatchedMsg, popupChildren, renderer,
                useDynamicRenderList, listHeight, itemHeight, scrollBuffer,
                onItemTouchTap, onFilterClear, onTriggerMouseOver, onTriggerMouseOut
            } = this.props,
            {isAbove, value, filter, popupVisible} = this.state,

            emptyEl = [{
                itemRenderer() {
                    return (

                            {
                                noMatchedMsg ?
                                    noMatchedMsg
                                    :

                                        No matched value.

                            }

                    );
                },
                disableTouchRipple: true
            }],

            listData = this.filterData(),
            isEmpty = listData.length < 1,

            wrapperClassName = (className ? ' ' + className : ''),
            triggerClassName = (isEmpty && !noMatchedPopupVisible ? '' : (popupVisible ? ' activated' : ''))
                + (isAbove ? ' above' : ' blow'),
            autoCompletePopupClassName = (isAbove ? ' above' : ' blow') + (popupClassName ? ' ' + popupClassName : ''),
            autoCompletePopupStyle = Object.assign({
                width: this.triggerEl && getComputedStyle(this.triggerEl).width
            }, popupStyle);

        return (

                {
                    name ?
                        <input type="hidden"
                               name={name}
                               value={Util.getValueByValueField(value, valueField, displayField)}/>
                        :
                        null
                }

                <TextField ref="trigger"
                           className={'auto-complete-filter-trigger' + triggerClassName}
                           theme={theme}
                           value={filter}
                           placeholder={placeholder}
                           disabled={disabled}
                           iconCls={iconCls}
                           rightIconCls={rightIconCls}
                           onFocus={this.filterFocusHandler}
                           onBlur={this.filterBlurHandler}
                           onMouseOver={onTriggerMouseOver}
                           onMouseOut={onTriggerMouseOut}
                           onChange={this.filterChangeHandler}
                           onPressEnter={this.filterPressEnterHandler}
                           onClear={onFilterClear}
                           onRightIconTouchTap={this.filterPressEnterHandler}/>

                {
                    isEmpty && !noMatchedPopupVisible ?
                        null
                        :
                        <Popup className={'auto-complete-filter-popup' + autoCompletePopupClassName}
                               style={autoCompletePopupStyle}
                               theme={popupTheme}
                               visible={popupVisible}
                               triggerEl={this.triggerEl}
                               hasTriangle={false}
                               triggerMode={Popup.TriggerMode.OPEN}
                               position={isAbove ? Popup.Position.TOP_LEFT : Popup.Position.BOTTOM_LEFT}
                               onRender={this.popupRenderHandler}
                               onRequestClose={this.closePopup}>

                            {
                                isEmpty ?
                                    <List className="auto-complete-filter-list"
                                          theme={popupTheme}
                                          data={emptyEl}/>
                                    :
                                    (
                                        useDynamicRenderList ?
                                            <DynamicRenderList className="auto-complete-filter-list"
                                                               theme={popupTheme}
                                                               value={value}
                                                               data={listData}
                                                               valueField={valueField}
                                                               displayField={displayField}
                                                               descriptionField={descriptionField}
                                                               renderer={renderer}
                                                               listHeight={listHeight}
                                                               itemHeight={itemHeight}
                                                               scrollBuffer={scrollBuffer}
                                                               onItemTouchTap={onItemTouchTap}
                                                               onChange={this.changeHandler}/>
                                            :
                                            <List className="auto-complete-filter-list"
                                                  theme={popupTheme}
                                                  value={value}
                                                  data={listData}
                                                  valueField={valueField}
                                                  displayField={displayField}
                                                  descriptionField={descriptionField}
                                                  renderer={renderer}
                                                  onItemTouchTap={onItemTouchTap}
                                                  onChange={this.changeHandler}/>
                                    )
                            }

                            {popupChildren}

                        </Popup>
                }

        );
    }

};<commit_msg>update AutoCompleteFilter<commit_after>/**
 * @file AutoCompleteFilter component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {findDOMNode} from 'react-dom';

import TextField from '../TextField';
import Popup from '../Popup';
import List from '../List';
import DynamicRenderList from '../DynamicRenderList';
import Theme from '../Theme';

import Util from '../_vendors/Util';
import DropdownCalculation from '../_vendors/DropdownCalculation';

export default class AutoCompleteFilter extends Component {

    static Theme = Theme;
    static propTypes = {

        /**
         * The CSS class name of the root element.
         */
        className: PropTypes.string,

        /**
         * The CSS class name of the popup element.
         */
        popupClassName: PropTypes.string,

        /**
         * Override the styles of the root element.
         */
        style: PropTypes.object,

        /**
         * Override the styles of the popup element.
         */
        popupStyle: PropTypes.object,

        /**
         * The theme.
         */
        theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

        /**
         * The theme.
         */
        popupTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),

        /**
         * The name of the auto complete.
         */
        name: PropTypes.string,

        /**
         * The placeholder of the field.
         */
        placeholder: PropTypes.string,

        /**
         * Children passed into the List.
         */
        data: PropTypes.oneOfType([

            // not grouped
            PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.shape({

                /**
                 * The CSS class name of the list button.
                 */
                className: PropTypes.string,

                /**
                 * Override the styles of the list button.
                 */
                style: PropTypes.object,

                /**
                 * The theme of the list button.
                 */
                theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

                /**
                 * The text value of the list button.Type can be string or number.
                 */
                value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

                /**
                 * The desc value of the list button. Type can be string or number.
                 */
                desc: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

                /**
                 * If true, the list button will be disabled.
                 */
                disabled: PropTypes.bool,

                /**
                 * If true,the button will be have loading effect.
                 */
                isLoading: PropTypes.bool,

                /**
                 * If true,the element's ripple effect will be disabled.
                 */
                disableTouchRipple: PropTypes.bool,

                /**
                 * Use this property to display an icon. It will display on the left.
                 */
                iconCls: PropTypes.string,

                /**
                 * Use this property to display an icon. It will display on the right.
                 */
                rightIconCls: PropTypes.string,

                /**
                 * You can create a complicated renderer callback instead of value and desc prop.
                 */
                itemRenderer: PropTypes.func,

                /**
                 * Callback function fired when a list item touch-tapped.
                 */
                onTouchTap: PropTypes.func

            }), PropTypes.string, PropTypes.number])),

            // grouped
            PropTypes.array

        ]).isRequired,

        /**
         * If true, the auto complete will be disabled.
         */
        disabled: PropTypes.bool,

        /**
         * The value field name in data. (default: "value")
         */
        valueField: PropTypes.string,

        /**
         * The display field name in data. (default: "text")
         */
        displayField: PropTypes.string,

        /**
         * The description field name in data. (default: "desc")
         */
        descriptionField: PropTypes.string,

        /**
         * If true, the popup list automatically closed after selection.
         */
        autoClose: PropTypes.bool,

        /**
         * Callback function fired when filter changed.
         */
        filterCallback: PropTypes.func,

        /**
         * Use this property to display an icon.
         */
        iconCls: PropTypes.string,

        /**
         * Use this property to display an icon.
         */
        rightIconCls: PropTypes.string,

        /**
         * The visiblity of no matched popup.
         */
        noMatchedPopupVisible: PropTypes.bool,

        /**
         * The message of no matched value.
         */
        noMatchedMsg: PropTypes.string,

        popupChildren: PropTypes.any,

        filterInitValue: PropTypes.string,

        useDynamicRenderList: PropTypes.bool,
        listHeight: PropTypes.number,
        itemHeight: PropTypes.number,
        scrollBuffer: PropTypes.number,

        /**
         * You can create a complicated renderer callback instead of value and desc prop.
         */
        renderer: PropTypes.func,

        /**
         * The function that trigger when filter changes.
         */
        onFilterChange: PropTypes.func,

        /**
         * The function that trigger when press enter.
         */
        onFilterPressEnter: PropTypes.func,

        /**
         * The function that trigger when filter cleared.
         */
        onFilterClear: PropTypes.func,

        /**
         * The function that trigger when value changed.
         */
        onChange: PropTypes.func,

        /**
         * The function that trigger when touch-tap the list items.
         */
        onItemTouchTap: PropTypes.func,

        /**
         * Callback function fired when AutoCompleteFilter get focus.
         */
        onFocus: PropTypes.func,

        /**
         * Callback function fired when AutoCompleteFilter lose focus.
         */
        onBlur: PropTypes.func,

        onTriggerMouseOver: PropTypes.func,
        onTriggerMouseOut: PropTypes.func,
        onPopupClosed: PropTypes.func

    };
    static defaultProps = {

        className: null,
        popupClassName: null,
        style: null,
        popupStyle: null,
        theme: Theme.DEFAULT,
        popupTheme: Theme.DEFAULT,

        name: null,
        placeholder: null,
        data: [],
        disabled: false,
        valueField: 'value',
        displayField: 'text',
        descriptionField: 'desc',
        autoClose: false,
        iconCls: null,
        rightIconCls: 'fa fa-search',
        noMatchedPopupVisible: true,
        noMatchedMsg: null,
        filterInitValue: '',

        popupChildren: null,

        useDynamicRenderList: false

    };

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.triggerEl = null;

        this.state = {
            value: props.value,
            filter: props.filterInitValue,
            popupVisible: false,
            isAbove: false
        };

        this.filterData = ::this.filterData;
        this.filterFocusHandler = ::this.filterFocusHandler;
        this.filterBlurHandler = ::this.filterBlurHandler;
        this.filterPressEnterHandler = ::this.filterPressEnterHandler;
        this.filterChangeHandler = ::this.filterChangeHandler;
        this.closePopup = ::this.closePopup;
        this.popupRenderHandler = ::this.popupRenderHandler;
        this.changeHandler = ::this.changeHandler;

    }

    filterData(filter = this.state.filter, data = this.props.data) {

        if (!filter) {
            return data;
        }

        const {valueField, displayField, renderer, filterCallback} = this.props;

        if (filterCallback) {
            return filterCallback(filter, data);
        }

        return data.filter(item => {

            if (!item) {
                return false;
            }

            if (renderer) {
                return renderer(item).toString().toUpperCase().includes(filter.toUpperCase());
            }

            if (typeof item === 'object') {

                const itemDisplay = Util.getTextByDisplayField(item, displayField, valueField);

                if (itemDisplay) {
                    return itemDisplay.toString().toUpperCase().includes(filter.toUpperCase());
                }

            }

            return item.toString().toUpperCase().includes(filter.toUpperCase());

        });

    }

    filterFocusHandler(...args) {

        const {disabled, onFocus} = this.props,
            {filter} = this.state;

        onFocus && onFocus(...args);

        !disabled && filter && this.setState({
            popupVisible: true
        });

    }

    filterBlurHandler(...args) {
        const {disabled, onBlur} = this.props;
        !disabled && onBlur && onBlur(...args);
    }

    filterPressEnterHandler(e, filter) {

        const {autoClose} = this.props,
            callback = () => {
                const {onFilterPressEnter} = this.props;
                onFilterPressEnter && onFilterPressEnter(filter);
            };

        if (autoClose) {
            this.setState({
                popupVisible: false
            }, () => {
                callback();
            });
        } else {
            callback();
        }

    }

    filterChangeHandler(filter) {

        const value = this.state.value,
            state = {
                filter,
                popupVisible: !!filter
            };

        if (!filter) {
            state.value = undefined;
        }

        this.setState(state, () => {
            const {onFilterChange} = this.props;
            onFilterChange && onFilterChange(filter);
        });

    }

    closePopup() {
        this.setState({
            popupVisible: false
        }, () => {
            const {onPopupClosed} = this.props;
            onPopupClosed && onPopupClosed();
        });
    }

    popupRenderHandler(popupEl) {

        const isAbove = DropdownCalculation.isAbove(this.localAutoCompleteEl, this.triggerEl, findDOMNode(popupEl));

        if (isAbove !== this.state.isAbove) {
            this.setState({
                isAbove
            });
        }

    }

    changeHandler(value) {

        const {autoClose, valueField, displayField, renderer} = this.props,
            state = {
                value,
                filter: renderer ? renderer(value) : Util.getTextByDisplayField(value, displayField, valueField)
            };

        if (autoClose) {
            state.popupVisible = false;
        }

        this.setState(state, () => {
            const {onChange} = this.props;
            onChange && onChange(value);
        });

    }

    componentDidMount() {
        this.localAutoCompleteEl = this.refs.localAutoComplete;
        this.triggerEl = findDOMNode(this.refs.trigger);
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.value !== this.state.value) {
            this.setState({
                value: nextProps.value
            });
        }
    }

    render() {

        const {
                className, popupClassName, style, popupStyle, theme, popupTheme, name, placeholder,
                disabled, iconCls, rightIconCls, valueField, displayField, descriptionField,
                noMatchedPopupVisible, noMatchedMsg, popupChildren, renderer,
                useDynamicRenderList, listHeight, itemHeight, scrollBuffer,
                onItemTouchTap, onFilterClear, onTriggerMouseOver, onTriggerMouseOut
            } = this.props,
            {isAbove, value, filter, popupVisible} = this.state,

            emptyEl = [{
                itemRenderer() {
                    return (

                            {
                                noMatchedMsg ?
                                    noMatchedMsg
                                    :

                                        No matched value.

                            }

                    );
                },
                disableTouchRipple: true
            }],

            listData = this.filterData(),
            isEmpty = listData.length < 1,

            wrapperClassName = (className ? ' ' + className : ''),
            triggerClassName = (isEmpty && !noMatchedPopupVisible ? '' : (popupVisible ? ' activated' : ''))
                + (isAbove ? ' above' : ' blow'),
            autoCompletePopupClassName = (isAbove ? ' above' : ' blow') + (popupClassName ? ' ' + popupClassName : ''),
            autoCompletePopupStyle = Object.assign({
                width: this.triggerEl && getComputedStyle(this.triggerEl).width
            }, popupStyle);

        return (

                {
                    name ?
                        <input type="hidden"
                               name={name}
                               value={Util.getValueByValueField(value, valueField, displayField)}/>
                        :
                        null
                }

                <TextField ref="trigger"
                           className={'auto-complete-filter-trigger' + triggerClassName}
                           theme={theme}
                           value={filter}
                           placeholder={placeholder}
                           disabled={disabled}
                           iconCls={iconCls}
                           rightIconCls={rightIconCls}
                           onFocus={this.filterFocusHandler}
                           onBlur={this.filterBlurHandler}
                           onMouseOver={onTriggerMouseOver}
                           onMouseOut={onTriggerMouseOut}
                           onChange={this.filterChangeHandler}
                           onPressEnter={this.filterPressEnterHandler}
                           onClear={onFilterClear}
                           onRightIconTouchTap={this.filterPressEnterHandler}/>

                {
                    isEmpty && !noMatchedPopupVisible ?
                        null
                        :
                        <Popup className={'auto-complete-filter-popup' + autoCompletePopupClassName}
                               style={autoCompletePopupStyle}
                               theme={popupTheme}
                               visible={popupVisible}
                               triggerEl={this.triggerEl}
                               hasTriangle={false}
                               triggerMode={Popup.TriggerMode.OPEN}
                               position={isAbove ? Popup.Position.TOP_LEFT : Popup.Position.BOTTOM_LEFT}
                               onRender={this.popupRenderHandler}
                               onRequestClose={this.closePopup}>

                            {
                                isEmpty ?
                                    <List className="auto-complete-filter-list"
                                          theme={popupTheme}
                                          data={emptyEl}/>
                                    :
                                    (
                                        useDynamicRenderList ?
                                            <DynamicRenderList className="auto-complete-filter-list"
                                                               theme={popupTheme}
                                                               value={value}
                                                               data={listData}
                                                               valueField={valueField}
                                                               displayField={displayField}
                                                               descriptionField={descriptionField}
                                                               renderer={renderer}
                                                               listHeight={listHeight}
                                                               itemHeight={itemHeight}
                                                               scrollBuffer={scrollBuffer}
                                                               onItemTouchTap={onItemTouchTap}
                                                               onChange={this.changeHandler}/>
                                            :
                                            <List className="auto-complete-filter-list"
                                                  theme={popupTheme}
                                                  value={value}
                                                  data={listData}
                                                  valueField={valueField}
                                                  displayField={displayField}
                                                  descriptionField={descriptionField}
                                                  renderer={renderer}
                                                  onItemTouchTap={onItemTouchTap}
                                                  onChange={this.changeHandler}/>
                                    )
                            }

                            {popupChildren}

                        </Popup>
                }

        );
    }

};<|endoftext|>