<commit_before>"use strict";
var oulipo = false;

$(window).load(function() {
    // Returns a function, that, as long as it continues to be invoked, will not
    // be triggered. The function will be called after it stops being called for
    // N milliseconds. If `immediate` is passed, trigger the function on the
    // leading edge, instead of the trailing.
    function debounce(func, wait, immediate) {
        var timeout;
        return function() {
            $('#saveEditButton').removeClass()
            $('#saveEditButton').text("Editing");
            var context = this,
                args = arguments;
            var later = function() {
                timeout = null;
                if (!immediate) func.apply(context, args);
            };
            var callNow = immediate && !timeout;
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
            if (callNow) func.apply(context, args);
        };
    };

    // This will apply the debounce effect on the keyup event
    // And it only fires 500ms or half a second after the user stopped typing
    var prevText = $('#userInput').val();
    console.log("debounce: " + window.cowyo.debounceMS)
    $('#userInput').on('keyup', debounce(function() {
        if (prevText == $('#userInput').val()) {
            return // no changes
        }
        prevText = $('#userInput').val();

        if (oulipo) {
            $('#userInput').val($('#userInput').val().replace(/e/g,""));
        }
        $('#saveEditButton').removeClass()
        $('#saveEditButton').text("Saving")
        upload();
    }, window.cowyo.debounceMS));

    var latestUpload = null, needAnother = false;
    function upload() {
        // Prevent concurrent uploads
        if (latestUpload != null) {
            needAnother = true;
            return
        }
        latestUpload = $.ajax({
            type: 'POST',
            url: '/update',
            data: JSON.stringify({
                new_text: $('#userInput').val(),
                page: window.cowyo.pageName,
                fetched_at: window.lastFetch,
            }),
            success: function(data) {
                latestUpload = null;

                $('#saveEditButton').removeClass()
                if (data.success == true) {
                    $('#saveEditButton').addClass("success");
                    window.lastFetch = data.unix_time;

                    if (needAnother) {
                        upload();
                    };
                } else {
                    $('#saveEditButton').addClass("failure");
                }
                $('#saveEditButton').text(data.message);
                needAnother = false;
            },
            error: function(xhr, error) {
                latestUpload = null;
                needAnother = false;
                $('#saveEditButton').removeClass()
                $('#saveEditButton').addClass("failure");
                $('#saveEditButton').text(error);
            },
            contentType: "application/json",
            dataType: 'json'
        });
    }

    function primeForSelfDestruct() {
        $.ajax({
            type: 'POST',
            url: '/prime',
            data: JSON.stringify({
                page: window.cowyo.pageName,
            }),
            success: function(data) {
                $('#saveEditButton').removeClass()
                if (data.success == true) {
                    $('#saveEditButton').addClass("success");
                } else {
                    $('#saveEditButton').addClass("failure");
                }
                $('#saveEditButton').text(data.message);
            },
            error: function(xhr, error) {
                $('#saveEditButton').removeClass()
                $('#saveEditButton').addClass("failure");
                $('#saveEditButton').text(error);
            },
            contentType: "application/json",
            dataType: 'json'
        });
    }

    function lockPage(passphrase) {
        $.ajax({
            type: 'POST',
            url: '/lock',
            data: JSON.stringify({
                page: window.cowyo.pageName,
                passphrase: passphrase
            }),
            success: function(data) {
                $('#saveEditButton').removeClass()
                if (data.success == true) {
                    $('#saveEditButton').addClass("success");
                } else {
                    $('#saveEditButton').addClass("failure");
                }
                $('#saveEditButton').text(data.message);
                if (data.success == true && $('#lockPage').text() == "Lock") {
                    window.location = "/" + window.cowyo.pageName + "/view";
                }
                if (data.success == true && $('#lockPage').text() == "Unlock") {
                    window.location = "/" + window.cowyo.pageName + "/edit";
                }
            },
            error: function(xhr, error) {
                $('#saveEditButton').removeClass()
                $('#saveEditButton').addClass("failure");
                $('#saveEditButton').text(error);
            },
            contentType: "application/json",
            dataType: 'json'
        });
    }

    function publishPage() {
        $.ajax({
            type: 'POST',
            url: '/publish',
            data: JSON.stringify({
                page: window.cowyo.pageName,
                publish: $('#publishPage').text() == "Publish"
            }),
            success: function(data) {
                $('#saveEditButton').removeClass()
                if (data.success == true) {
                    $('#saveEditButton').addClass("success");
                } else {
                    $('#saveEditButton').addClass("failure");
                }
                $('#saveEditButton').text(data.message);
                if (data.message == "Unpublished") {
                    $('#publishPage').text("Publish");
                } else {
                    $('#publishPage').text("Unpublish");
                }
            },
            error: function(xhr, error) {
                $('#saveEditButton').removeClass()
                $('#saveEditButton').addClass("failure");
                $('#saveEditButton').text(error);
            },
            contentType: "application/json",
            dataType: 'json'
        });
    }

    function encryptPage(passphrase) {
        $.ajax({
            type: 'POST',
            url: '/encrypt',
            data: JSON.stringify({
                page: window.cowyo.pageName,
                passphrase: passphrase
            }),
            success: function(data) {
                $('#saveEditButton').removeClass()
                if (data.success == true) {
                    $('#saveEditButton').addClass("success");
                } else {
                    $('#saveEditButton').addClass("failure");
                }
                $('#saveEditButton').text(data.message);

                if (data.success == true && $('#encryptPage').text() == "Encrypt") {
                    window.location = "/" + window.cowyo.pageName + "/view";
                }
                if (data.success == true && $('#encryptPage').text() == "Decrypt") {
                    window.location = "/" + window.cowyo.pageName + "/edit";
                }
            },
            error: function(xhr, error) {
                $('#saveEditButton').removeClass()
                $('#saveEditButton').addClass("failure");
                $('#saveEditButton').text(error);
            },
            contentType: "application/json",
            dataType: 'json'
        });
    }

    function clearOld() {
        $.ajax({
            type: 'DELETE',
            url: '/oldlist',
            data: JSON.stringify({
                page: window.cowyo.pageName
            }),
            success: function(data) {
                $('#saveEditButton').removeClass()
                if (data.success == true) {
                    $('#saveEditButton').addClass("success");
                } else {
                    $('#saveEditButton').addClass("failure");
                }
                $('#saveEditButton').text(data.message);
                if (data.success == true) {
                    window.location = "/" + window.cowyo.pageName + "/list";
                }
            },
            error: function(xhr, error) {
                $('#saveEditButton').removeClass();
                $('#saveEditButton').addClass("failure");
                $('#saveEditButton').text(error);
            },
            contentType: "application/json",
            dataType: 'json'
        });
    }

    $("#encryptPage").click(function(e) {
        e.preventDefault();
        var passphrase = prompt("Please enter a passphrase. Note: Encrypting will remove all previous history.", "");
        if (passphrase != null) {
            encryptPage(passphrase);
        }
    });

    $("#erasePage").click(function(e) {
        e.preventDefault();
        var r = confirm("Are you sure you want to erase?");
        if (r == true) {
            window.location = "/" + window.cowyo.pageName + "/erase";
        } else {
            x = "You pressed Cancel!";
        }
    });

    $("#selfDestructPage").click(function(e) {
        e.preventDefault();
        var r = confirm("This will erase the page the next time it is opened, are you sure you want to do that?");
        if (r == true) {
            primeForSelfDestruct();
        } else {
            x = "You pressed Cancel!";
        }
    });

    $("#lockPage").click(function(e) {
        e.preventDefault();
        var passphrase = prompt("Please enter a passphrase to lock", "");
        if (passphrase != null) {
            if ($('#lockPage').text() == "Lock") {
                $('#saveEditButton').removeClass();
                $("#saveEditButton").text("Locking");
            } else {
                $('#saveEditButton').removeClass();
                $("#saveEditButton").text("Unlocking");
            }
            lockPage(passphrase);
            // POST encrypt page
            // reload page
        }
    });

    $("#publishPage").click(function(e) {
        e.preventDefault();
        var message = " This will add your page to the sitemap.xml so it will be indexed by search engines.";
        if ($('#publishPage').text() == "Unpublish") {
            message = "";
        }
        var confirmed = confirm("Are you sure?" + message);
        if (confirmed == true) {
            if ($('#publishPage').text() == "Unpublish") {
                $('#saveEditButton').removeClass();
                $("#saveEditButton").text("Unpublishing");
            } else {
                $('#saveEditButton').removeClass();
                $("#saveEditButton").text("Publishing");
            }
            publishPage();
        }
    });

    $("#clearOld").click(function(e) {
        e.preventDefault();
        var r = confirm("This will erase all cleared list items, are you sure you want to do that? (Versions will stay in history).");
        if (r == true) {
            clearOld()
        } else {
            x = "You pressed Cancel!";
        }
    });

    $("textarea").keydown(function(e) {
        if(e.keyCode === 9) { // tab was pressed
            // get caret position/selection
            var start = this.selectionStart;
            var end = this.selectionEnd;

            var $this = $(this);
            var value = $this.val();

            // set textarea value to: text before caret + tab + text after caret
            $this.val(value.substring(0, start)
                        + "\t"
                        + value.substring(end));

            // put caret at right position again (add one for the tab)
            this.selectionStart = this.selectionEnd = start + 1;

            // prevent the focus lose
            e.preventDefault();
        }
    });

    $('.deletable').click(function(event) {
      event.preventDefault();
      var lineNum = $(this).attr('id');

      $.ajax({
        url: "/listitem" + '?' + $.param({
          "lineNum": lineNum,
          "page": window.cowyo.pageName
        }),
        type: 'DELETE',
        success: function() {
          window.location.reload(true);
        }
      });
      event.target.classList.add('deleting');
    });
});

// TODO: Avoid uploading the same thing twice (check if it's already present while allowing failed uploads to be overwritten?)
function onUploadFinished(file) {
    this.removeFile(file);
    var cursorPos = $('#userInput').prop('selectionStart');
    var cursorEnd = $('#userInput').prop('selectionEnd');
    var v = $('#userInput').val();
    var textBefore = v.substring(0,  cursorPos);
    var textAfter  = v.substring(cursorPos, v.length);
    var message = 'uploaded file';
    if (cursorEnd > cursorPos) {
        message = v.substring(cursorPos, cursorEnd);
        textAfter = v.substring(cursorEnd, v.length);
    }
    var prefix = '';
    if (file.type.startsWith("image")) {
        prefix = '!';
    }
    var extraText = prefix+'['+message+'](' +
        file.xhr.getResponseHeader("Location") +
        ')';

    $('#userInput').val(
        textBefore +
        extraText +
        textAfter
    );

    // Select the newly-inserted link
    $('#userInput').prop('selectionStart', cursorPos);
    $('#userInput').prop('selectionEnd', cursorPos + extraText.length);
    $('#userInput').trigger('keyup'); // trigger a save
}
<commit_msg>display the filename when uploading<commit_after>"use strict";
var oulipo = false;

$(window).load(function() {
    // Returns a function, that, as long as it continues to be invoked, will not
    // be triggered. The function will be called after it stops being called for
    // N milliseconds. If `immediate` is passed, trigger the function on the
    // leading edge, instead of the trailing.
    function debounce(func, wait, immediate) {
        var timeout;
        return function() {
            $('#saveEditButton').removeClass()
            $('#saveEditButton').text("Editing");
            var context = this,
                args = arguments;
            var later = function() {
                timeout = null;
                if (!immediate) func.apply(context, args);
            };
            var callNow = immediate && !timeout;
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
            if (callNow) func.apply(context, args);
        };
    };

    // This will apply the debounce effect on the keyup event
    // And it only fires 500ms or half a second after the user stopped typing
    var prevText = $('#userInput').val();
    console.log("debounce: " + window.cowyo.debounceMS)
    $('#userInput').on('keyup', debounce(function() {
        if (prevText == $('#userInput').val()) {
            return // no changes
        }
        prevText = $('#userInput').val();

        if (oulipo) {
            $('#userInput').val($('#userInput').val().replace(/e/g,""));
        }
        $('#saveEditButton').removeClass()
        $('#saveEditButton').text("Saving")
        upload();
    }, window.cowyo.debounceMS));

    var latestUpload = null, needAnother = false;
    function upload() {
        // Prevent concurrent uploads
        if (latestUpload != null) {
            needAnother = true;
            return
        }
        latestUpload = $.ajax({
            type: 'POST',
            url: '/update',
            data: JSON.stringify({
                new_text: $('#userInput').val(),
                page: window.cowyo.pageName,
                fetched_at: window.lastFetch,
            }),
            success: function(data) {
                latestUpload = null;

                $('#saveEditButton').removeClass()
                if (data.success == true) {
                    $('#saveEditButton').addClass("success");
                    window.lastFetch = data.unix_time;

                    if (needAnother) {
                        upload();
                    };
                } else {
                    $('#saveEditButton').addClass("failure");
                }
                $('#saveEditButton').text(data.message);
                needAnother = false;
            },
            error: function(xhr, error) {
                latestUpload = null;
                needAnother = false;
                $('#saveEditButton').removeClass()
                $('#saveEditButton').addClass("failure");
                $('#saveEditButton').text(error);
            },
            contentType: "application/json",
            dataType: 'json'
        });
    }

    function primeForSelfDestruct() {
        $.ajax({
            type: 'POST',
            url: '/prime',
            data: JSON.stringify({
                page: window.cowyo.pageName,
            }),
            success: function(data) {
                $('#saveEditButton').removeClass()
                if (data.success == true) {
                    $('#saveEditButton').addClass("success");
                } else {
                    $('#saveEditButton').addClass("failure");
                }
                $('#saveEditButton').text(data.message);
            },
            error: function(xhr, error) {
                $('#saveEditButton').removeClass()
                $('#saveEditButton').addClass("failure");
                $('#saveEditButton').text(error);
            },
            contentType: "application/json",
            dataType: 'json'
        });
    }

    function lockPage(passphrase) {
        $.ajax({
            type: 'POST',
            url: '/lock',
            data: JSON.stringify({
                page: window.cowyo.pageName,
                passphrase: passphrase
            }),
            success: function(data) {
                $('#saveEditButton').removeClass()
                if (data.success == true) {
                    $('#saveEditButton').addClass("success");
                } else {
                    $('#saveEditButton').addClass("failure");
                }
                $('#saveEditButton').text(data.message);
                if (data.success == true && $('#lockPage').text() == "Lock") {
                    window.location = "/" + window.cowyo.pageName + "/view";
                }
                if (data.success == true && $('#lockPage').text() == "Unlock") {
                    window.location = "/" + window.cowyo.pageName + "/edit";
                }
            },
            error: function(xhr, error) {
                $('#saveEditButton').removeClass()
                $('#saveEditButton').addClass("failure");
                $('#saveEditButton').text(error);
            },
            contentType: "application/json",
            dataType: 'json'
        });
    }

    function publishPage() {
        $.ajax({
            type: 'POST',
            url: '/publish',
            data: JSON.stringify({
                page: window.cowyo.pageName,
                publish: $('#publishPage').text() == "Publish"
            }),
            success: function(data) {
                $('#saveEditButton').removeClass()
                if (data.success == true) {
                    $('#saveEditButton').addClass("success");
                } else {
                    $('#saveEditButton').addClass("failure");
                }
                $('#saveEditButton').text(data.message);
                if (data.message == "Unpublished") {
                    $('#publishPage').text("Publish");
                } else {
                    $('#publishPage').text("Unpublish");
                }
            },
            error: function(xhr, error) {
                $('#saveEditButton').removeClass()
                $('#saveEditButton').addClass("failure");
                $('#saveEditButton').text(error);
            },
            contentType: "application/json",
            dataType: 'json'
        });
    }

    function encryptPage(passphrase) {
        $.ajax({
            type: 'POST',
            url: '/encrypt',
            data: JSON.stringify({
                page: window.cowyo.pageName,
                passphrase: passphrase
            }),
            success: function(data) {
                $('#saveEditButton').removeClass()
                if (data.success == true) {
                    $('#saveEditButton').addClass("success");
                } else {
                    $('#saveEditButton').addClass("failure");
                }
                $('#saveEditButton').text(data.message);

                if (data.success == true && $('#encryptPage').text() == "Encrypt") {
                    window.location = "/" + window.cowyo.pageName + "/view";
                }
                if (data.success == true && $('#encryptPage').text() == "Decrypt") {
                    window.location = "/" + window.cowyo.pageName + "/edit";
                }
            },
            error: function(xhr, error) {
                $('#saveEditButton').removeClass()
                $('#saveEditButton').addClass("failure");
                $('#saveEditButton').text(error);
            },
            contentType: "application/json",
            dataType: 'json'
        });
    }

    function clearOld() {
        $.ajax({
            type: 'DELETE',
            url: '/oldlist',
            data: JSON.stringify({
                page: window.cowyo.pageName
            }),
            success: function(data) {
                $('#saveEditButton').removeClass()
                if (data.success == true) {
                    $('#saveEditButton').addClass("success");
                } else {
                    $('#saveEditButton').addClass("failure");
                }
                $('#saveEditButton').text(data.message);
                if (data.success == true) {
                    window.location = "/" + window.cowyo.pageName + "/list";
                }
            },
            error: function(xhr, error) {
                $('#saveEditButton').removeClass();
                $('#saveEditButton').addClass("failure");
                $('#saveEditButton').text(error);
            },
            contentType: "application/json",
            dataType: 'json'
        });
    }

    $("#encryptPage").click(function(e) {
        e.preventDefault();
        var passphrase = prompt("Please enter a passphrase. Note: Encrypting will remove all previous history.", "");
        if (passphrase != null) {
            encryptPage(passphrase);
        }
    });

    $("#erasePage").click(function(e) {
        e.preventDefault();
        var r = confirm("Are you sure you want to erase?");
        if (r == true) {
            window.location = "/" + window.cowyo.pageName + "/erase";
        } else {
            x = "You pressed Cancel!";
        }
    });

    $("#selfDestructPage").click(function(e) {
        e.preventDefault();
        var r = confirm("This will erase the page the next time it is opened, are you sure you want to do that?");
        if (r == true) {
            primeForSelfDestruct();
        } else {
            x = "You pressed Cancel!";
        }
    });

    $("#lockPage").click(function(e) {
        e.preventDefault();
        var passphrase = prompt("Please enter a passphrase to lock", "");
        if (passphrase != null) {
            if ($('#lockPage').text() == "Lock") {
                $('#saveEditButton').removeClass();
                $("#saveEditButton").text("Locking");
            } else {
                $('#saveEditButton').removeClass();
                $("#saveEditButton").text("Unlocking");
            }
            lockPage(passphrase);
            // POST encrypt page
            // reload page
        }
    });

    $("#publishPage").click(function(e) {
        e.preventDefault();
        var message = " This will add your page to the sitemap.xml so it will be indexed by search engines.";
        if ($('#publishPage').text() == "Unpublish") {
            message = "";
        }
        var confirmed = confirm("Are you sure?" + message);
        if (confirmed == true) {
            if ($('#publishPage').text() == "Unpublish") {
                $('#saveEditButton').removeClass();
                $("#saveEditButton").text("Unpublishing");
            } else {
                $('#saveEditButton').removeClass();
                $("#saveEditButton").text("Publishing");
            }
            publishPage();
        }
    });

    $("#clearOld").click(function(e) {
        e.preventDefault();
        var r = confirm("This will erase all cleared list items, are you sure you want to do that? (Versions will stay in history).");
        if (r == true) {
            clearOld()
        } else {
            x = "You pressed Cancel!";
        }
    });

    $("textarea").keydown(function(e) {
        if(e.keyCode === 9) { // tab was pressed
            // get caret position/selection
            var start = this.selectionStart;
            var end = this.selectionEnd;

            var $this = $(this);
            var value = $this.val();

            // set textarea value to: text before caret + tab + text after caret
            $this.val(value.substring(0, start)
                        + "\t"
                        + value.substring(end));

            // put caret at right position again (add one for the tab)
            this.selectionStart = this.selectionEnd = start + 1;

            // prevent the focus lose
            e.preventDefault();
        }
    });

    $('.deletable').click(function(event) {
      event.preventDefault();
      var lineNum = $(this).attr('id');

      $.ajax({
        url: "/listitem" + '?' + $.param({
          "lineNum": lineNum,
          "page": window.cowyo.pageName
        }),
        type: 'DELETE',
        success: function() {
          window.location.reload(true);
        }
      });
      event.target.classList.add('deleting');
    });
});

// TODO: Avoid uploading the same thing twice (check if it's already present while allowing failed uploads to be overwritten?)
function onUploadFinished(file) {
    this.removeFile(file);
    var cursorPos = $('#userInput').prop('selectionStart');
    var cursorEnd = $('#userInput').prop('selectionEnd');
    var v = $('#userInput').val();
    var textBefore = v.substring(0,  cursorPos);
    var textAfter  = v.substring(cursorPos, v.length);
    var message = 'uploaded file';
    if (cursorEnd > cursorPos) {
        message = v.substring(cursorPos, cursorEnd);
        textAfter = v.substring(cursorEnd, v.length);
    }
    var prefix = '';
    if (file.type.startsWith("image")) {
        prefix = '!';
    }
    var extraText = prefix+'['+file.xhr.getResponseHeader("Location").split('filename=')[1]+'](' +
        file.xhr.getResponseHeader("Location") +
        ')';

    $('#userInput').val(
        textBefore +
        extraText +
        textAfter
    );

    // Select the newly-inserted link
    $('#userInput').prop('selectionStart', cursorPos);
    $('#userInput').prop('selectionEnd', cursorPos + extraText.length);
    $('#userInput').trigger('keyup'); // trigger a save
}
<|endoftext|>


<commit_before>// Graph Constants
const max_graph_age = 60 * 60 * 24 * 1000; // Seconds (1 day; can probably be longer)
const rukmal_data_graph_url = 'https://raw.githubusercontent.com/rukmal/Resume/gh-pages/personal_data.rdf';

// Store-specific things
const store_mimeType = 'application/rdf+xml';
const store_base_uri = 'http://precis.rukmal.me/'

/**
 * Function to load graph data.
 *
 * This data is loaded either from the existing browser storage, or from GitHub. This
 * decision is made dynamically, first checking if browser storage exists, then if the
 * graph data exists, before finally checking if the graph data is not too old (as set by)
 * `max_graph_age`. If any of these conditions are not satisfied, `downloadGraphData` is
 * called, and the data is downloaded asynchronously from GitHub.
 *
 * Note that the "asynchronous" feature here does little to nothing, as all content
 * is coming from the graph; this is effectively a blocking call, hence the callback to
 * populate page data.
 *
 * @param {Callback} graphHandler Callback function to handle the build graph store.
 */
function loadGraphData(graphHandler) {
    // This loop misses the case of web storage NOT existing, and the variable not existing
    if(typeof(Storage) !== 'undefined') {
        // HTML5 Web storage exists, check for store
        if (typeof(sessionStorage.rukmal_data_graph) === 'undefined') {
            // Browser storage exists, but graph data does not
            // Download graph data
            downloadGraphData(buildGraph, graphHandler)
        } else {
            if ((Number(sessionStorage.rukmal_data_graph_last_updated) -
                new Date().getTime()) > max_graph_age) {
                // Graph data exists, but it is too old
                downloadGraphData(buildGraph, graphHandler);
            } else {
                // Graph data exists, just use it
                buildGraph(sessionStorage.rukmal_data_graph, graphHandler);
            }
        }
    } else {
        downloadGraphData(buildGraph, graphHandler);
    }
}

/**
 * Function to download graph data (from my Resume GitHub Page;
 * [http://github.com/rukmal/Resume]). Passes resulting data to a callback.
 *
 * @param {Callback} dataHandler Function to handle response.
 */
function downloadGraphData(dataHandler, dataHandlerArg) {
    $.get(rukmal_data_graph_url, function (data, status) {
        if (status === 'success') {
            // Check if session storage is supported; store if it is; update flags
            if (typeof(Storage) !== 'undefined') {
                sessionStorage.rukmal_data_graph = data;
                sessionStorage.rukmal_data_graph_last_updated = new Date().getTime();
            }

            // Handle data, pass thru data handler args too
            dataHandler(data, dataHandlerArg);
        }
    });
}

/**
 * Function to build a store and load graph data.
 *
 * @param {String} data String of graph data (RDF/XML).
 * @param {Callback} graphHandler Callback function to handle graph data.
 */
function buildGraph(data, graphHandler) {
    // Creating new graph
    // TODO: (this is PRIME candidate for improvement in the future;
    // this is done on each reload, on every page. There must be a better way.)
    const store = $rdf.graph();
    // Parsing data into store
    $rdf.parse(data, store, store_base_uri, store_mimeType);

    // Calling graph handler function with the graph data
    graphHandler(store);
}

/**
 * Function to get all nodes of a given type from the Precis knowledge graph.
 *
 * @param {Object} store RDFLib.js Store.
 * @param {String} type_name Target type to get.
 * @param {Callback} resCallback Callback function called with each result.
 * @param {Callback} doneCallback Callback function called when query is complete.
 */
function getOfType(store, type_name, resCallback, doneCallback) {
    // SPARQL query (plain text)
    var sparql_query = `
    PREFIX precis: <http://precis.rukmal.me/ontology#>
    PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
    SELECT ?name ?date
    WHERE {
        ?name rdf:type precis:${type_name} .
        ?name precis:hasDate ?date .
    }
    `;

    // Converting SPARQL query to rdflibjs query object
    // Note: RDFLib.js docs are absolute fucking garbage, so look at the source code:
    // https://linkeddata.github.io/rdflib.js/doc/sparql-to-query.js.html
    var rdflib_query = $rdf.SPARQLToQuery(sparql_query, false, store);

    // Running query
    // `resCallback` is called with each result
    // Third `false` argument is for stupid ass rdflib.js reasons
    // `doneCallback` is called when the query is complete
    // See: https://linkeddata.github.io/rdflib.js/doc/query.js.html
    store.query(rdflib_query, resCallback, false, doneCallback);
}

/**
 * Function to get all properties of a given node. Also get names of the nested
 * property (if it is also an object), and the externalResource of the nested node.
 *
 * @param {Object} store RDFLib.js store.
 * @param {String} individual_iri Target IRI.
 * @param {Callback} resCallback Callback function called with each result.
 * @param {Callback} doneCallback Callback function called when query is complete.
 */
function getAllProperties(store, individual_iri, resCallback, doneCallback) {
    var sparql_query = `
    PREFIX precis: <http://precis.rukmal.me/ontology#>
    PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
    SELECT ?predicate ?obj ?obj_name ?obj_ext_res ?obj_website
    WHERE {
        <${individual_iri}> ?predicate ?obj .
        OPTIONAL { ?obj precis:hasName ?obj_name . }
        OPTIONAL { ?obj precis:externalResource ?obj_ext_res . }
        OPTIONAL { ?obj precis:hasWebsite ?obj_website . }
    }
    `;

    // Converting to rdflibjs query object
    var rdflib_query = $rdf.SPARQLToQuery(sparql_query, false, store);

    // Running query
    store.query(rdflib_query, resCallback, false, doneCallback);
}
<commit_msg>Added query to extract WorkExperience information<commit_after>// Graph Constants
const max_graph_age = 60 * 60 * 24 * 1000; // Seconds (1 day; can probably be longer)
const rukmal_data_graph_url = 'https://raw.githubusercontent.com/rukmal/Resume/gh-pages/personal_data.rdf';

// Store-specific things
const store_mimeType = 'application/rdf+xml';
const store_base_uri = 'http://precis.rukmal.me/'

/**
 * Function to load graph data.
 *
 * This data is loaded either from the existing browser storage, or from GitHub. This
 * decision is made dynamically, first checking if browser storage exists, then if the
 * graph data exists, before finally checking if the graph data is not too old (as set by)
 * `max_graph_age`. If any of these conditions are not satisfied, `downloadGraphData` is
 * called, and the data is downloaded asynchronously from GitHub.
 *
 * Note that the "asynchronous" feature here does little to nothing, as all content
 * is coming from the graph; this is effectively a blocking call, hence the callback to
 * populate page data.
 *
 * @param {Callback} graphHandler Callback function to handle the build graph store.
 */
function loadGraphData(graphHandler) {
    // This loop misses the case of web storage NOT existing, and the variable not existing
    if(typeof(Storage) !== 'undefined') {
        // HTML5 Web storage exists, check for store
        if (typeof(sessionStorage.rukmal_data_graph) === 'undefined') {
            // Browser storage exists, but graph data does not
            // Download graph data
            downloadGraphData(buildGraph, graphHandler)
        } else {
            if ((Number(sessionStorage.rukmal_data_graph_last_updated) -
                new Date().getTime()) > max_graph_age) {
                // Graph data exists, but it is too old
                downloadGraphData(buildGraph, graphHandler);
            } else {
                // Graph data exists, just use it
                buildGraph(sessionStorage.rukmal_data_graph, graphHandler);
            }
        }
    } else {
        downloadGraphData(buildGraph, graphHandler);
    }
}

/**
 * Function to download graph data (from my Resume GitHub Page;
 * [http://github.com/rukmal/Resume]). Passes resulting data to a callback.
 *
 * @param {Callback} dataHandler Function to handle response.
 */
function downloadGraphData(dataHandler, dataHandlerArg) {
    $.get(rukmal_data_graph_url, function (data, status) {
        if (status === 'success') {
            // Check if session storage is supported; store if it is; update flags
            if (typeof(Storage) !== 'undefined') {
                sessionStorage.rukmal_data_graph = data;
                sessionStorage.rukmal_data_graph_last_updated = new Date().getTime();
            }

            // Handle data, pass thru data handler args too
            dataHandler(data, dataHandlerArg);
        }
    });
}

/**
 * Function to build a store and load graph data.
 *
 * @param {String} data String of graph data (RDF/XML).
 * @param {Callback} graphHandler Callback function to handle graph data.
 */
function buildGraph(data, graphHandler) {
    // Creating new graph
    // TODO: (this is PRIME candidate for improvement in the future;
    // this is done on each reload, on every page. There must be a better way.)
    const store = $rdf.graph();
    // Parsing data into store
    $rdf.parse(data, store, store_base_uri, store_mimeType);

    // Calling graph handler function with the graph data
    graphHandler(store);
}

/**
 * Function to get all nodes of a given type from the Precis knowledge graph.
 *
 * @param {Object} store RDFLib.js Store.
 * @param {String} type_name Target type to get.
 * @param {Callback} resCallback Callback function called with each result.
 * @param {Callback} doneCallback Callback function called when query is complete.
 */
function getOfType(store, type_name, resCallback, doneCallback) {
    // SPARQL query (plain text)
    var sparql_query = `
    PREFIX precis: <http://precis.rukmal.me/ontology#>
    PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
    SELECT ?name ?date
    WHERE {
        ?name rdf:type precis:${type_name} .
        ?name precis:hasDate ?date .
    }
    `;

    // Converting SPARQL query to rdflibjs query object
    // Note: RDFLib.js docs are absolute fucking garbage, so look at the source code:
    // https://linkeddata.github.io/rdflib.js/doc/sparql-to-query.js.html
    var rdflib_query = $rdf.SPARQLToQuery(sparql_query, false, store);

    // Running query
    // `resCallback` is called with each result
    // Third `false` argument is for stupid ass rdflib.js reasons
    // `doneCallback` is called when the query is complete
    // See: https://linkeddata.github.io/rdflib.js/doc/query.js.html
    store.query(rdflib_query, resCallback, false, doneCallback);
}

/**
 * Function to get all properties of a given node. Also get names of the nested
 * property (if it is also an object), and the externalResource of the nested node.
 *
 * @param {Object} store RDFLib.js store.
 * @param {String} individual_iri Target IRI.
 * @param {Callback} resCallback Callback function called with each result.
 * @param {Callback} doneCallback Callback function called when query is complete.
 */
function getAllProperties(store, individual_iri, resCallback, doneCallback) {
    var sparql_query = `
    PREFIX precis: <http://precis.rukmal.me/ontology#>
    PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
    SELECT ?predicate ?obj ?obj_name ?obj_ext_res ?obj_website
    WHERE {
        <${individual_iri}> ?predicate ?obj .
        OPTIONAL { ?obj precis:hasName ?obj_name . }
        OPTIONAL { ?obj precis:externalResource ?obj_ext_res . }
        OPTIONAL { ?obj precis:hasWebsite ?obj_website . }
    }
    `;

    // Converting to rdflibjs query object
    var rdflib_query = $rdf.SPARQLToQuery(sparql_query, false, store);

    // Running query
    store.query(rdflib_query, resCallback, false, doneCallback);
}

/**
 * Function to get all properties of a given WorkExperience. Also resolves secondary
 * and tertiary "parentOrganization"s for the "employedAt" organization.
 *
 * Note that the reason for the absolutely ridiculous amount of "OPTIONAL" statements
 * is because rdflib.js is awful and doesn't allow for nested if statements.
 *
 * @param {Object} store RDFLib.js store.
 * @param {String} individual_iri Target IRI.
 * @param {Callback} resCallback Callback function called with each result.
 * @param {Callback} doneCallback Callback function called when query is complete.
 */
function getAllWorkExperience(store, individual_iri, resCallback, doneCallback) {
    var sparql_query = `
    PREFIX precis: <http://precis.rukmal.me/ontology#>
    PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
    SELECT DISTINCT ?description_text ?priority ?desc ?predicate ?obj ?obj_website ?obj_name ?parentOrgName ?parentOrgResource ?parentOrgWebsite ?superParentOrgName ?superParentOrgWebsite ?superParentOrgResource
    WHERE {
        <${individual_iri}> ?predicate ?obj .
        OPTIONAL { ?obj precis:hasName ?obj_name . }
        OPTIONAL { ?obj precis:hasWebsite ?obj_website . }
        OPTIONAL {
            <${individual_iri}> precis:hasDescription ?desc .
            ?desc precis:hasPriority ?priority .
            ?desc precis:hasText ?description_text .
        }
        OPTIONAL {
            <${individual_iri}> precis:employedAt ?org .
            ?org precis:hasParentOrganization ?parentOrg .
            ?parentOrg precis:hasName ?parentOrgName .
            ?parentOrg precis:hasWebsite ?parentOrgWebsite .
        }
        OPTIONAL {
            <${individual_iri}> precis:employedAt ?org .
            ?org precis:hasParentOrganization ?parentOrg .
            ?parentOrg precis:hasName ?parentOrgName .
            ?parentOrg precis:hasWebsite ?parentOrgWebsite .
            ?parentOrg precis:externalResource ?parentOrgResource .
        }
        OPTIONAL {
            <${individual_iri}> precis:employedAt ?org .
            ?org precis:hasParentOrganization ?parentOrg .
            ?parentOrg precis:hasParentOrganization ?superParentOrg .
            ?superParentOrg precis:hasName ?superParentOrgName .
            ?superParentOrg precis:hasWebsite ?superParentOrgWebsite .
            ?superParentOrg precis:externalResource ?superParentOrgResource .
        }
        OPTIONAL {
            <${individual_iri}> precis:employedAt ?org .
            ?org precis:hasParentOrganization ?parentOrg .
            ?parentOrg precis:hasParentOrganization ?superParentOrg .
            ?superParentOrg precis:hasName ?superParentOrgName .
            ?superParentOrg precis:hasWebsite ?superParentOrgWebsite .
        }
    }
    `;

    // Converting to rdflibjs query object
    var rdflib_query = $rdf.SPARQLToQuery(sparql_query, false, store);

    // Running query
    store.query(rdflib_query, resCallback, false, doneCallback);
}
<|endoftext|>


<commit_before>/* global $this: true */
/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "animationsSlider" }] */

if ($.cookie('themeCSSpath')) {
  $('link#theme-stylesheet').attr('href', $.cookie('themeCSSpath'))
}
if ($.cookie('themeLayout')) {
  $('body').addClass($.cookie('themeLayout'))
}

$(function () {
  sliderHomepage()
  sliders()
  fullScreenContainer()
  productDetailGallery(4000)
  menuSliding()
  productDetailSizes()
  utils()
  animations()
  counters()
  demo()
  contactForm()
})

// Ajax contact
function contactForm () {
  var form = $('.contact-form')
  form.submit(function () {
    $this = $(this)
    $.post($(this).attr('action'),
      $this.serialize(),
      function () {
        $this[0].reset() // clear form

        $('#contact-message')
        .html('
<button type="button" class="close" data-dismiss="alert">×Close</button>Thank you for getting in touch. We will get back to you soon!
')
        .fadeIn()
      }
      , 'json')
    return false
  })
}

/* for demo purpose only - can be deleted */
function demo () {
  if ($.cookie('themeCSSpath')) {
    $('link#theme-stylesheet').attr('href', $.cookie('themeCSSpath'))
  }

  $('#colour').change(function () {
    if ($(this).val() !== '') {
      var themeCSSpath = 'css/style.' + $(this).val() + '.css'

      $('link#theme-stylesheet').attr('href', themeCSSpath)

      $.cookie('themeCSSpath', themeCSSpath, {expires: 365, path: '/'})
    }

    return false
  })

  $('#layout').change(function () {
    if ($(this).val() !== '') {
      var themeLayout = $(this).val()

      $('body').removeClass('wide')
      $('body').removeClass('boxed')

      $('body').addClass(themeLayout)

      $.cookie('themeLayout', themeLayout, {expires: 365, path: '/'})
    }

    return false
  })
}

/* slider homepage */
function sliderHomepage () {
  if ($('#slider').length) {
    // var owl = $('#slider')

    $('#slider').owlCarousel({
      autoPlay: 3000,
      items: 4,
      itemsDesktopSmall: [900, 3],
      itemsTablet: [600, 3],
      itemsMobile: [500, 2]
    })
  }
}

/* sliders */
function sliders () {
  if ($('.owl-carousel').length) {
    $('.customers').owlCarousel({
      items: 6,
      itemsDesktopSmall: [990, 4],
      itemsTablet: [768, 2],
      itemsMobile: [480, 1]
    })

    $('.testimonials').owlCarousel({
      items: 4,
      itemsDesktopSmall: [990, 3],
      itemsTablet: [768, 2],
      itemsMobile: [480, 1]
    })

    $('.project').owlCarousel({
      navigation: true, // Show next and prev buttons
      navigationText: ['', ''],
      slideSpeed: 300,
      paginationSpeed: 400,
      autoPlay: true,
      stopOnHover: true,
      singleItem: true,
      afterInit: '',
      lazyLoad: true
    })

    $('.homepage').owlCarousel({
      navigation: false, // Show next and prev buttons
      navigationText: ['', ''],
      slideSpeed: 2000,
      paginationSpeed: 1000,
      autoPlay: true,
      stopOnHover: true,
      singleItem: true,
      lazyLoad: false,
      addClassActive: true,
      afterInit: function () {
        // animationsSlider()
      },
      afterMove: function () {
        // animationsSlider()
      }
    })
  }
}

/* menu sliding */
function menuSliding () {
  $('.dropdown').on('show.bs.dropdown', function () {
    if ($(window).width() > 750) {
      $(this).find('.dropdown-menu').first().stop(true, true).slideDown()
    } else {
      $(this).find('.dropdown-menu').first().stop(true, true).show()
    }
  })

  $('.dropdown').on('hide.bs.dropdown', function () {
    if ($(window).width() > 750) {
      $(this).find('.dropdown-menu').first().stop(true, true).slideUp()
    } else {
      $(this).find('.dropdown-menu').first().stop(true, true).hide()
    }
  })
}

/* animations */
function animations () {
  var delayTime = 0
  $('[data-animate]').css({opacity: '0'})
  $('[data-animate]').waypoint(function () {
    delayTime += 150
    $(this).delay(delayTime).queue(function (next) {
      $(this).toggleClass('animated')
      $(this).toggleClass($(this).data('animate'))
      delayTime = 0
      next()
      // $(this).removeClass('animated')
      // $(this).toggleClass($(this).data('animate'))
    })
  }, {
    offset: '90%',
    triggerOnce: true
  })

  $('[data-animate-hover]').hover(function () {
    $(this).css({opacity: 1})
    $(this).addClass('animated')
    $(this).removeClass($(this).data('animate'))
    $(this).addClass($(this).data('animate-hover'))
  }, function () {
    $(this).removeClass('animated')
    $(this).removeClass($(this).data('animate-hover'))
  })
}

function animationsSlider () {
  var delayTimeSlider = 400

  $('.owl-item:not(.active) [data-animate-always]').each(function () {
    $(this).removeClass('animated')
    $(this).removeClass($(this).data('animate-always'))
    $(this).stop(true, true, true).css({opacity: 0})
  })

  $('.owl-item.active [data-animate-always]').each(function () {
    delayTimeSlider += 500

    $(this).delay(delayTimeSlider).queue(function () {
      $(this).addClass('animated')
      $(this).addClass($(this).data('animate-always'))

      console.log($(this).data('animate-always'))
    })
  })
}

/* counters */
function counters () {
  $('.counter').counterUp({
    delay: 10,
    time: 1000
  })
}

/* picture zoom */
function pictureZoom () {
  $('.product .image, .post .image, .photostream div').each(function () {
    var imgHeight = $(this).find('img').height()
    $(this).height(imgHeight)
  })
}

/* full screen intro */
function fullScreenContainer () {
  var screenWidth = $(window).width() + 'px'
  var screenHeight = '500px'

  if ($(window).height() > 500) {
    screenHeight = $(window).height() + 'px'
  }

  $('#intro, #intro .item').css({
    width: screenWidth,
    height: screenHeight
  })
}

function utils () {
  /* tooltips */
  $('[data-toggle="tooltip"]').tooltip()

  /* click on the box activates the radio */
  $('#checkout').on('click', '.box.shipping-method, .box.payment-method', function () {
    var radio = $(this).find(':radio')
    radio.prop('checked', true)
  })

  /* click on the box activates the link in it */
  $('.box.clickable').on('click', function () {
    window.location = $(this).find('a').attr('href')
  })

  /* external links in new window */
  $('.external').on('click', function (e) {
    e.preventDefault()
    window.open($(this).attr('href'))
  })

  /* animated scrolling */
  $('.scroll-to, .scroll-to-top').click(function (event) {
    var fullUrl = this.href
    var parts = fullUrl.split('#')

    if (parts.length > 1) {
      scrollTo(fullUrl)
      event.preventDefault()
    }
  })

  function scrollTo (fullUrl) {
    var parts = fullUrl.split('#')
    var trgt = parts[1]
    var targetOffset = $('#' + trgt).offset()
    var targetTop = targetOffset.top - 100

    if (targetTop < 0) {
      targetTop = 0
    }

    $('html, body').animate({
      scrollTop: targetTop
    }, 1000)
  }
}

/* product detail gallery */
function productDetailGallery (confDetailSwitch) {
  $('.thumb:first').addClass('active')
  var timer = setInterval(autoSwitch, confDetailSwitch)

  $('.thumb').click(function (e) {
    switchImage($(this))
    clearInterval(timer)
    timer = setInterval(autoSwitch, confDetailSwitch)
    e.preventDefault()
  })

  $('#mainImage').hover(function () {
    clearInterval(timer)
  }, function () {
    timer = setInterval(autoSwitch, confDetailSwitch)
  })

  function autoSwitch () {
    var nextThumb = $('.thumb.active').closest('div').next('div').find('.thumb')
    if (nextThumb.length === 0) {
      nextThumb = $('.thumb:first')
    }
    switchImage(nextThumb)
  }

  function switchImage (thumb) {
    $('.thumb').removeClass('active')
    var bigUrl = thumb.attr('href')
    thumb.addClass('active')
    $('#mainImage img').attr('src', bigUrl)
  }
}

/* product detail sizes */
function productDetailSizes () {
  $('.sizes a').click(function (e) {
    e.preventDefault()
    $('.sizes a').removeClass('active')
    $('.size-input').prop('checked', false)
    $(this).addClass('active')
    $(this).next('input').prop('checked', true)
  })
}

$.fn.alignElementsSameHeight = function () {
  $('.same-height-row').each(function () {
    var maxHeight = 0
    var children = $(this).find('.same-height')
    children.height('auto')

    if ($(window).width() > 768) {
      children.each(function () {
        if ($(this).innerHeight() > maxHeight) {
          maxHeight = $(this).innerHeight()
        }
      })
      children.innerHeight(maxHeight)
    }

    maxHeight = 0
    children = $(this).find('.same-height-always')
    children.height('auto')
    children.each(function () {
      if ($(this).height() > maxHeight) {
        maxHeight = $(this).innerHeight()
      }
    })
    children.innerHeight(maxHeight)
  })
}

var windowWidth
$(function () {
  windowWidth = $(window).width()

  $(this).alignElementsSameHeight()
  pictureZoom()
})

$(window).resize(function () {
  var newWindowWidth = $(window).width()

  if (windowWidth !== newWindowWidth) {
    setTimeout(function () {
      $(this).alignElementsSameHeight()
      fullScreenContainer()
      pictureZoom()
    }, 205)
    windowWidth = newWindowWidth
  }
})
<commit_msg>Fixed travis<commit_after>/* global $this: true */
/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "animationsSlider" }] */

if ($.cookie('themeCSSpath')) {
  $('link#theme-stylesheet').attr('href', $.cookie('themeCSSpath'))
}
if ($.cookie('themeLayout')) {
  $('body').addClass($.cookie('themeLayout'))
}

$(function () {
  sliderHomepage()
  sliders()
  fullScreenContainer()
  productDetailGallery(4000)
  menuSliding()
  productDetailSizes()
  utils()
  animations()
  counters()
  demo()
  contactForm()
})

// Ajax contact
function contactForm () {
  var form = $('.contact-form')
  form.submit(function () {
    $this = $(this)
    $.post($(this).attr('action'),
      $this.serialize(),
      function () {
        $this[0].reset() // clear form

        $('#contact-message')
          .html('
<button type="button" class="close" data-dismiss="alert">×Close</button>Thank you for getting in touch. We will get back to you soon!
')
          .fadeIn()
      }
      , 'json')
    return false
  })
}

/* for demo purpose only - can be deleted */
function demo () {
  if ($.cookie('themeCSSpath')) {
    $('link#theme-stylesheet').attr('href', $.cookie('themeCSSpath'))
  }

  $('#colour').change(function () {
    if ($(this).val() !== '') {
      var themeCSSpath = 'css/style.' + $(this).val() + '.css'

      $('link#theme-stylesheet').attr('href', themeCSSpath)

      $.cookie('themeCSSpath', themeCSSpath, {expires: 365, path: '/'})
    }

    return false
  })

  $('#layout').change(function () {
    if ($(this).val() !== '') {
      var themeLayout = $(this).val()

      $('body').removeClass('wide')
      $('body').removeClass('boxed')

      $('body').addClass(themeLayout)

      $.cookie('themeLayout', themeLayout, {expires: 365, path: '/'})
    }

    return false
  })
}

/* slider homepage */
function sliderHomepage () {
  if ($('#slider').length) {
    // var owl = $('#slider')

    $('#slider').owlCarousel({
      autoPlay: 3000,
      items: 4,
      itemsDesktopSmall: [900, 3],
      itemsTablet: [600, 3],
      itemsMobile: [500, 2]
    })
  }
}

/* sliders */
function sliders () {
  if ($('.owl-carousel').length) {
    $('.customers').owlCarousel({
      items: 6,
      itemsDesktopSmall: [990, 4],
      itemsTablet: [768, 2],
      itemsMobile: [480, 1]
    })

    $('.testimonials').owlCarousel({
      items: 4,
      itemsDesktopSmall: [990, 3],
      itemsTablet: [768, 2],
      itemsMobile: [480, 1]
    })

    $('.project').owlCarousel({
      navigation: true, // Show next and prev buttons
      navigationText: ['', ''],
      slideSpeed: 300,
      paginationSpeed: 400,
      autoPlay: true,
      stopOnHover: true,
      singleItem: true,
      afterInit: '',
      lazyLoad: true
    })

    $('.homepage').owlCarousel({
      navigation: false, // Show next and prev buttons
      navigationText: ['', ''],
      slideSpeed: 2000,
      paginationSpeed: 1000,
      autoPlay: true,
      stopOnHover: true,
      singleItem: true,
      lazyLoad: false,
      addClassActive: true,
      afterInit: function () {
        // animationsSlider()
      },
      afterMove: function () {
        // animationsSlider()
      }
    })
  }
}

/* menu sliding */
function menuSliding () {
  $('.dropdown').on('show.bs.dropdown', function () {
    if ($(window).width() > 750) {
      $(this).find('.dropdown-menu').first().stop(true, true).slideDown()
    } else {
      $(this).find('.dropdown-menu').first().stop(true, true).show()
    }
  })

  $('.dropdown').on('hide.bs.dropdown', function () {
    if ($(window).width() > 750) {
      $(this).find('.dropdown-menu').first().stop(true, true).slideUp()
    } else {
      $(this).find('.dropdown-menu').first().stop(true, true).hide()
    }
  })
}

/* animations */
function animations () {
  var delayTime = 0
  $('[data-animate]').css({opacity: '0'})
  $('[data-animate]').waypoint(function () {
    delayTime += 150
    $(this).delay(delayTime).queue(function (next) {
      $(this).toggleClass('animated')
      $(this).toggleClass($(this).data('animate'))
      delayTime = 0
      next()
      // $(this).removeClass('animated')
      // $(this).toggleClass($(this).data('animate'))
    })
  }, {
    offset: '90%',
    triggerOnce: true
  })

  $('[data-animate-hover]').hover(function () {
    $(this).css({opacity: 1})
    $(this).addClass('animated')
    $(this).removeClass($(this).data('animate'))
    $(this).addClass($(this).data('animate-hover'))
  }, function () {
    $(this).removeClass('animated')
    $(this).removeClass($(this).data('animate-hover'))
  })
}

function animationsSlider () {
  var delayTimeSlider = 400

  $('.owl-item:not(.active) [data-animate-always]').each(function () {
    $(this).removeClass('animated')
    $(this).removeClass($(this).data('animate-always'))
    $(this).stop(true, true, true).css({opacity: 0})
  })

  $('.owl-item.active [data-animate-always]').each(function () {
    delayTimeSlider += 500

    $(this).delay(delayTimeSlider).queue(function () {
      $(this).addClass('animated')
      $(this).addClass($(this).data('animate-always'))

      console.log($(this).data('animate-always'))
    })
  })
}

/* counters */
function counters () {
  $('.counter').counterUp({
    delay: 10,
    time: 1000
  })
}

/* picture zoom */
function pictureZoom () {
  $('.product .image, .post .image, .photostream div').each(function () {
    var imgHeight = $(this).find('img').height()
    $(this).height(imgHeight)
  })
}

/* full screen intro */
function fullScreenContainer () {
  var screenWidth = $(window).width() + 'px'
  var screenHeight = '500px'

  if ($(window).height() > 500) {
    screenHeight = $(window).height() + 'px'
  }

  $('#intro, #intro .item').css({
    width: screenWidth,
    height: screenHeight
  })
}

function utils () {
  /* tooltips */
  $('[data-toggle="tooltip"]').tooltip()

  /* click on the box activates the radio */
  $('#checkout').on('click', '.box.shipping-method, .box.payment-method', function () {
    var radio = $(this).find(':radio')
    radio.prop('checked', true)
  })

  /* click on the box activates the link in it */
  $('.box.clickable').on('click', function () {
    window.location = $(this).find('a').attr('href')
  })

  /* external links in new window */
  $('.external').on('click', function (e) {
    e.preventDefault()
    window.open($(this).attr('href'))
  })

  /* animated scrolling */
  $('.scroll-to, .scroll-to-top').click(function (event) {
    var fullUrl = this.href
    var parts = fullUrl.split('#')

    if (parts.length > 1) {
      scrollTo(fullUrl)
      event.preventDefault()
    }
  })

  function scrollTo (fullUrl) {
    var parts = fullUrl.split('#')
    var trgt = parts[1]
    var targetOffset = $('#' + trgt).offset()
    var targetTop = targetOffset.top - 100

    if (targetTop < 0) {
      targetTop = 0
    }

    $('html, body').animate({
      scrollTop: targetTop
    }, 1000)
  }
}

/* product detail gallery */
function productDetailGallery (confDetailSwitch) {
  $('.thumb:first').addClass('active')
  var timer = setInterval(autoSwitch, confDetailSwitch)

  $('.thumb').click(function (e) {
    switchImage($(this))
    clearInterval(timer)
    timer = setInterval(autoSwitch, confDetailSwitch)
    e.preventDefault()
  })

  $('#mainImage').hover(function () {
    clearInterval(timer)
  }, function () {
    timer = setInterval(autoSwitch, confDetailSwitch)
  })

  function autoSwitch () {
    var nextThumb = $('.thumb.active').closest('div').next('div').find('.thumb')
    if (nextThumb.length === 0) {
      nextThumb = $('.thumb:first')
    }
    switchImage(nextThumb)
  }

  function switchImage (thumb) {
    $('.thumb').removeClass('active')
    var bigUrl = thumb.attr('href')
    thumb.addClass('active')
    $('#mainImage img').attr('src', bigUrl)
  }
}

/* product detail sizes */
function productDetailSizes () {
  $('.sizes a').click(function (e) {
    e.preventDefault()
    $('.sizes a').removeClass('active')
    $('.size-input').prop('checked', false)
    $(this).addClass('active')
    $(this).next('input').prop('checked', true)
  })
}

$.fn.alignElementsSameHeight = function () {
  $('.same-height-row').each(function () {
    var maxHeight = 0
    var children = $(this).find('.same-height')
    children.height('auto')

    if ($(window).width() > 768) {
      children.each(function () {
        if ($(this).innerHeight() > maxHeight) {
          maxHeight = $(this).innerHeight()
        }
      })
      children.innerHeight(maxHeight)
    }

    maxHeight = 0
    children = $(this).find('.same-height-always')
    children.height('auto')
    children.each(function () {
      if ($(this).height() > maxHeight) {
        maxHeight = $(this).innerHeight()
      }
    })
    children.innerHeight(maxHeight)
  })
}

var windowWidth
$(function () {
  windowWidth = $(window).width()

  $(this).alignElementsSameHeight()
  pictureZoom()
})

$(window).resize(function () {
  var newWindowWidth = $(window).width()

  if (windowWidth !== newWindowWidth) {
    setTimeout(function () {
      $(this).alignElementsSameHeight()
      fullScreenContainer()
      pictureZoom()
    }, 205)
    windowWidth = newWindowWidth
  }
})
<|endoftext|>


<commit_before>// specify env file path
require('dotenv').config({path: 'keys.env'});
var fetch = require('isomorphic-fetch');
var express = require('express');
var app = express();

var weatherURL = `http://api.openweathermap.org/data/2.5/weather?units=imperial&APPID=${process.env.WEATHER_KEY}`

// express callback function for JSON fetch
app.get('/weather/:lat/:lon', function (req, res) {
    fetch(`${weatherURL}&lat=${req.params.lat}&lon=${req.params.lon}`)
        .then(response => response.json())
        .then(weather => res.send(weather))
        .catch(error => res.send(error))
})

// verify server is running
app.listen(3030, function () {
    console.log("I'm alive!");
})
<commit_msg>port update<commit_after>// specify env file path
require('dotenv').config({path: 'keys.env'});
var fetch = require('isomorphic-fetch');
var express = require('express');
var app = express();

var weatherURL = `http://api.openweathermap.org/data/2.5/weather?units=imperial&APPID=${process.env.WEATHER_KEY}`

// express callback function for JSON fetch
app.get('/weather/:lat/:lon', function (req, res) {
    fetch(`${weatherURL}&lat=${req.params.lat}&lon=${req.params.lon}`)
        .then(response => response.json())
        .then(weather => res.send(weather))
        .catch(error => res.send(error))
})

// verify server is running
app.listen(11646, function () {
    console.log("I'm alive!");
})
<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Hula' });
});

module.exports = router;
<commit_msg>redirect in index<commit_after>var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
    res.redirect('/service');
});

module.exports = router;
<|endoftext|>


<commit_before>/**
 * handles editability case for lists, because it depends on form and forms already depends on lists it had to be split out
 * @namespace
 */
openerp.web.list_editable = function (openerp) {
    var KEY_RETURN = 13,
        KEY_ESCAPE = 27;
    var QWeb = openerp.web.qweb;

    // editability status of list rows
    openerp.web.ListView.prototype.defaults.editable = null;

    // TODO: not sure second @lends on existing item is correct, to check
    openerp.web.ListView.include(/** @lends openerp.web.ListView# */{
        init: function () {
            var self = this;
            this._super.apply(this, arguments);
            $(this.groups).bind({
                'edit': function (e, id, dataset) {
                    self.do_edit(dataset.index, id, dataset);
                },
                'saved': function () {
                    if (self.groups.get_selection().length) {
                        return;
                    }
                    self.configure_pager(self.dataset);
                    self.compute_aggregates();
                }
            })
        },
        /**
         * Handles the activation of a record in editable mode (making a record
         * editable), called *after* the record has become editable.
         *
         * The default behavior is to setup the listview's dataset to match
         * whatever dataset was provided by the editing List
         *
         * @param {Number} index index of the record in the dataset
         * @param {Object} id identifier of the record being edited
         * @param {openerp.web.DataSet} dataset dataset in which the record is available
         */
        do_edit: function (index, id, dataset) {
            _.extend(this.dataset, dataset);
        },
        /**
         * Sets editability status for the list, based on defaults, view
         * architecture and the provided flag, if any.
         *
         * @param {Boolean} [force] forces the list to editability. Sets new row edition status to "bottom".
         */
        set_editable: function (force) {
            // If ``force``, set editability to bottom
            // otherwise rely on view default
            // view' @editable is handled separately as we have not yet
            // fetched and processed the view at this point.
            this.options.editable = (
                    (force && "bottom")
                    || this.defaults.editable);
        },
        /**
         * Replace do_search to handle editability process
         */
        do_search: function(domain, context, group_by) {
            this.set_editable(context['set_editable']);
            this._super.apply(this, arguments);
        },
        /**
         * Replace do_add_record to handle editability (and adding new record
         * as an editable row at the top or bottom of the list)
         */
        do_add_record: function () {
            if (this.options.editable) {
                this.groups.new_record();
            } else {
                this._super();
            }
        },
        on_loaded: function (data, grouped) {
            // tree/@editable takes priority on everything else if present.
            this.options.editable = data.arch.attrs.editable || this.options.editable;
            return this._super(data, grouped);
        },
        /**
         * Ensures the editable list is saved (saves any pending edition if
         * needed, or tries to)
         *
         * Returns a deferred to the end of the saving.
         *
         * @returns {$.Deferred}
         */
        ensure_saved: function () {
            return this.groups.ensure_saved();
        }
    });

    openerp.web.ListView.Groups.include(/** @lends openerp.web.ListView.Groups# */{
        passtrough_events: openerp.web.ListView.Groups.prototype.passtrough_events + " edit saved",
        new_record: function () {
            // TODO: handle multiple children
            this.children[null].new_record();
        },
        /**
         * Ensures descendant editable List instances are all saved if they have
         * pending editions.
         *
         * @returns {$.Deferred}
         */
        ensure_saved: function () {
            return $.when.apply(null,
                _.invoke(
                    _.values(this.children),
                    'ensure_saved'));
        }
    });

    openerp.web.ListView.List.include(/** @lends openerp.web.ListView.List# */{
        row_clicked: function (event) {
            if (!this.options.editable) {
                return this._super.apply(this, arguments);
            }
            this.edit_record($(event.currentTarget).data('id'));
        },
        /**
         * Checks if a record is being edited, and if so cancels it
         */
        cancel_pending_edition: function () {
            var self = this, cancelled = $.Deferred();
            if (!this.edition) {
                cancelled.resolve();
                return cancelled.promise();
            }

            if (this.edition_id != null) {
                this.reload_record(self.records.get(this.edition_id)).then(function () {
                    cancelled.resolve();
                });
            } else {
                cancelled.resolve();
            }
            cancelled.then(function () {
                self.view.unpad_columns();
                self.edition_form.stop();
                self.edition_form.$element.remove();
                delete self.edition_form;
                delete self.edition_id;
                delete self.edition;
            });
            this.pad_table_to(5);
            return cancelled.promise();
        },
        /**
         * Adapts this list's view description to be suitable to the inner form
         * view of a row being edited.
         *
         * @returns {Object} fields_view_get's view section suitable for putting into form view of editable rows.
         */
        get_form_fields_view: function () {
            // deep copy of view
            var view = $.extend(true, {}, this.group.view.fields_view);
            _(view.arch.children).each(function (widget) {
                widget.attrs.nolabel = true;
                if (widget.tag === 'button') {
                    delete widget.attrs.string;
                }
            });
            view.arch.attrs.col = 2 * view.arch.children.length;
            return view;
        },
        on_row_keyup: function (e) {
            var self = this;
            switch (e.which) {
            case KEY_RETURN:
                this.save_row().then(function (result) {
                    if (result.created) {
                        self.new_record();
                        return;
                    }

                    var next_record_id,
                        next_record = self.records.at(
                                self.records.indexOf(result.edited_record) + 1);
                    if (next_record) {
                        next_record_id = next_record.get('id');
                        self.dataset.index = _(self.dataset.ids)
                                .indexOf(next_record_id);
                    } else {
                        self.dataset.index = 0;
                        next_record_id = self.records.at(0).get('id');
                    }
                    self.edit_record(next_record_id);
                });
                break;
            case KEY_ESCAPE:
                this.cancel_edition();
                break;
            }
        },
        render_row_as_form: function (row) {
            var self = this;
            this.cancel_pending_edition().then(function () {
                var record_id = $(row).data('id');
                var $new_row = $('<tr>', {
                        id: _.uniqueId('oe-editable-row-'),
                        'data-id': record_id,
                        'class': row ? $(row).attr('class') : '' + ' oe_forms',
                        click: function (e) {e.stopPropagation();}
                    })
                    .delegate('button.oe-edit-row-save', 'click', function () {
                        self.save_row();
                    })
                    .delegate('button', 'keyup', function (e) {
                        e.stopImmediatePropagation();
                    })
                    .keyup(function () {
                        return self.on_row_keyup.apply(self, arguments); })
                    .keydown(function (e) { e.stopPropagation(); });
                if (row) {
                    $new_row.replaceAll(row);
                } else if (self.options.editable) {
                    var $last_child = self.$current.children('tr:last');
                    if (self.records.length) {
                        if (self.options.editable === 'top') {
                            $new_row.insertBefore(
                                self.$current.children('[data-id]:first'));
                        } else {
                            $new_row.insertAfter(
                                self.$current.children('[data-id]:last'));
                        }
                    } else {
                        $new_row.prependTo(self.$current);
                    }
                    if ($last_child.is(':not([data-id])')) {
                        $last_child.remove();
                    }
                }
                self.edition = true;
                self.edition_id = record_id;
                self.edition_form = _.extend(new openerp.web.ListEditableFormView(self.view, self.dataset, false), {
                    form_template: 'ListView.row.form',
                    registry: openerp.web.list.form.widgets,
                    $element: $new_row
                });
                // HA HA
                self.edition_form.appendTo();
                $.when(self.edition_form.on_loaded(self.get_form_fields_view())).then(function () {
                    // put in $.when just in case  FormView.on_loaded becomes asynchronous
                    $new_row.find('> td')
                          .addClass('oe-field-cell')
                          .removeAttr('width')
                      .end()
                      .find('td:last').removeClass('oe-field-cell').end();
                    if (self.options.selectable) {
                        $new_row.prepend('<th>');
                    }
                    if (self.options.isClarkGable) {
                        $new_row.prepend('<th>');
                    }
                    // pad in case of groupby
                    _(self.columns).each(function (column) {
                        if (column.meta) {
                            $new_row.prepend('<td>');
                        }
                    });
                    // Add column for the save, if
                    // there is none in the list
                    if (!self.options.deletable) {
                        self.view.pad_columns(
                            1, {except: $new_row});
                    }

                    self.edition_form.do_show();
                });
            });
        },
        handle_onwrite: function (source_record_id) {
            var self = this;
            var on_write_callback = self.view.fields_view.arch.attrs.on_write;
            if (!on_write_callback) { return; }
            this.dataset.call(on_write_callback, [source_record_id], function (ids) {
                _(ids).each(function (id) {
                    var record = self.records.get(id);
                    if (!record) {
                        // insert after the source record
                        var index = self.records.indexOf(
                            self.records.get(source_record_id)) + 1;
                        record = new openerp.web.list.Record({id: id});
                        self.records.add(record, {at: index});
                        self.dataset.ids.splice(index, 0, id);
                    }
                    self.reload_record(record);
                });
            });
        },
        /**
         * Saves the current row, and returns a Deferred resolving to an object
         * with the following properties:
         *
         * ``created``
         *   Boolean flag indicating whether the record saved was being created
         *   (``true`` or edited (``false``)
         * ``edited_record``
         *   The result of saving the record (either the newly created record,
         *   or the post-edition record), after insertion in the Collection if
         *   needs be.
         *
         * @returns {$.Deferred<{created: Boolean, edited_record: Record}>}
         */
        save_row: function () {
            //noinspection JSPotentiallyInvalidConstructorUsage
            var self = this, done = $.Deferred();
            return this.edition_form
                .do_save(null, this.options.editable === 'top')
                .pipe(function (result) {
                    if (result.created && !self.edition_id) {
                        self.records.add({id: result.result},
                            {at: self.options.editable === 'top' ? 0 : null});
                        self.edition_id = result.result;
                    }
                    var edited_record = self.records.get(self.edition_id);

                    return $.when(
                        self.handle_onwrite(self.edition_id),
                        self.cancel_pending_edition().then(function () {
                            $(self).trigger('saved', [self.dataset]);
                        })).pipe(function () {
                            return {
                                created: result.created || false,
                                edited_record: edited_record
                            };
                        }, null);
                }, null);
        },
        /**
         * If the current list is being edited, ensures it's saved
         */
        ensure_saved: function () {
            if (this.edition) {
                return this.save_row();
            }
            //noinspection JSPotentiallyInvalidConstructorUsage
            return $.Deferred().resolve().promise();
        },
        /**
         * Cancels the edition of the row for the current dataset index
         */
        cancel_edition: function () {
            this.cancel_pending_edition();
        },
        /**
         * Edits record currently selected via dataset
         */
        edit_record: function (record_id) {
            this.render_row_as_form(
                this.$current.find('[data-id=' + record_id + ']'));
            $(this).trigger(
                'edit',
                [record_id, this.dataset]);
        },
        new_record: function () {
            this.dataset.index = null;
            this.render_row_as_form();
        },
        render_record: function (record) {
            var index = this.records.indexOf(record),
                 self = this;
            // FIXME: context dict should probably be extracted cleanly
            return QWeb.render('ListView.row', {
                columns: this.columns,
                options: this.options,
                record: record,
                row_parity: (index % 2 === 0) ? 'even' : 'odd',
                view: this.view,
                render_cell: function () {
                    return self.render_cell.apply(self, arguments); },
                edited: !!this.edition_form
            });
        }
    });
    if (!openerp.web.list) {
        openerp.web.list = {};
    }
    if (!openerp.web.list.form) {
        openerp.web.list.form = {};
    }
    openerp.web.list.form.WidgetFrame = openerp.web.form.WidgetFrame.extend({
        template: 'ListView.row.frame'
    });
    var form_widgets = openerp.web.form.widgets;
    openerp.web.list.form.widgets = form_widgets.extend({
        'frame': 'openerp.web.list.form.WidgetFrame'
    });
    // All form widgets inherit a problematic behavior from
    // openerp.web.form.WidgetFrame: the cell itself is removed when invisible
    // whether it's @invisible or @attrs[invisible]. In list view, only the
    // former should completely remove the cell. We need to override update_dom
    // on all widgets since we can't just hit on widget itself (I think)
    var list_form_widgets = openerp.web.list.form.widgets;
    _(form_widgets.map).each(function (widget_path, key) {
        if (key === 'frame') { return; }
        var new_path = 'openerp.web.list.form.' + key;

        openerp.web.list.form[key] = (form_widgets.get_object(key)).extend({
            update_dom: function () {
                this.$element.children().css('visibility', '');
                if (this.modifiers.tree_invisible) {
                    var old_invisible = this.invisible;
                    this.invisible = true;
                    this._super();
                    this.invisible = old_invisible;
                } else if (this.invisible) {
                    this.$element.children().css('visibility', 'hidden');
                } else {
                    this._super();
                }
            }
        });
        list_form_widgets.add(key, new_path);
    });

    openerp.web.ListEditableFormView = openerp.web.FormView.extend({
        init_view: function() {},
        _render_and_insert: function () {
            return this.start();
        }
    });
};
<commit_msg>[MERGE] fix to classes set on editable list rows: oe_forms was not set in case of row being replaced<commit_after>/**
 * handles editability case for lists, because it depends on form and forms already depends on lists it had to be split out
 * @namespace
 */
openerp.web.list_editable = function (openerp) {
    var KEY_RETURN = 13,
        KEY_ESCAPE = 27;
    var QWeb = openerp.web.qweb;

    // editability status of list rows
    openerp.web.ListView.prototype.defaults.editable = null;

    // TODO: not sure second @lends on existing item is correct, to check
    openerp.web.ListView.include(/** @lends openerp.web.ListView# */{
        init: function () {
            var self = this;
            this._super.apply(this, arguments);
            $(this.groups).bind({
                'edit': function (e, id, dataset) {
                    self.do_edit(dataset.index, id, dataset);
                },
                'saved': function () {
                    if (self.groups.get_selection().length) {
                        return;
                    }
                    self.configure_pager(self.dataset);
                    self.compute_aggregates();
                }
            })
        },
        /**
         * Handles the activation of a record in editable mode (making a record
         * editable), called *after* the record has become editable.
         *
         * The default behavior is to setup the listview's dataset to match
         * whatever dataset was provided by the editing List
         *
         * @param {Number} index index of the record in the dataset
         * @param {Object} id identifier of the record being edited
         * @param {openerp.web.DataSet} dataset dataset in which the record is available
         */
        do_edit: function (index, id, dataset) {
            _.extend(this.dataset, dataset);
        },
        /**
         * Sets editability status for the list, based on defaults, view
         * architecture and the provided flag, if any.
         *
         * @param {Boolean} [force] forces the list to editability. Sets new row edition status to "bottom".
         */
        set_editable: function (force) {
            // If ``force``, set editability to bottom
            // otherwise rely on view default
            // view' @editable is handled separately as we have not yet
            // fetched and processed the view at this point.
            this.options.editable = (
                    (force && "bottom")
                    || this.defaults.editable);
        },
        /**
         * Replace do_search to handle editability process
         */
        do_search: function(domain, context, group_by) {
            this.set_editable(context['set_editable']);
            this._super.apply(this, arguments);
        },
        /**
         * Replace do_add_record to handle editability (and adding new record
         * as an editable row at the top or bottom of the list)
         */
        do_add_record: function () {
            if (this.options.editable) {
                this.groups.new_record();
            } else {
                this._super();
            }
        },
        on_loaded: function (data, grouped) {
            // tree/@editable takes priority on everything else if present.
            this.options.editable = data.arch.attrs.editable || this.options.editable;
            return this._super(data, grouped);
        },
        /**
         * Ensures the editable list is saved (saves any pending edition if
         * needed, or tries to)
         *
         * Returns a deferred to the end of the saving.
         *
         * @returns {$.Deferred}
         */
        ensure_saved: function () {
            return this.groups.ensure_saved();
        }
    });

    openerp.web.ListView.Groups.include(/** @lends openerp.web.ListView.Groups# */{
        passtrough_events: openerp.web.ListView.Groups.prototype.passtrough_events + " edit saved",
        new_record: function () {
            // TODO: handle multiple children
            this.children[null].new_record();
        },
        /**
         * Ensures descendant editable List instances are all saved if they have
         * pending editions.
         *
         * @returns {$.Deferred}
         */
        ensure_saved: function () {
            return $.when.apply(null,
                _.invoke(
                    _.values(this.children),
                    'ensure_saved'));
        }
    });

    openerp.web.ListView.List.include(/** @lends openerp.web.ListView.List# */{
        row_clicked: function (event) {
            if (!this.options.editable) {
                return this._super.apply(this, arguments);
            }
            this.edit_record($(event.currentTarget).data('id'));
        },
        /**
         * Checks if a record is being edited, and if so cancels it
         */
        cancel_pending_edition: function () {
            var self = this, cancelled = $.Deferred();
            if (!this.edition) {
                cancelled.resolve();
                return cancelled.promise();
            }

            if (this.edition_id != null) {
                this.reload_record(self.records.get(this.edition_id)).then(function () {
                    cancelled.resolve();
                });
            } else {
                cancelled.resolve();
            }
            cancelled.then(function () {
                self.view.unpad_columns();
                self.edition_form.stop();
                self.edition_form.$element.remove();
                delete self.edition_form;
                delete self.edition_id;
                delete self.edition;
            });
            this.pad_table_to(5);
            return cancelled.promise();
        },
        /**
         * Adapts this list's view description to be suitable to the inner form
         * view of a row being edited.
         *
         * @returns {Object} fields_view_get's view section suitable for putting into form view of editable rows.
         */
        get_form_fields_view: function () {
            // deep copy of view
            var view = $.extend(true, {}, this.group.view.fields_view);
            _(view.arch.children).each(function (widget) {
                widget.attrs.nolabel = true;
                if (widget.tag === 'button') {
                    delete widget.attrs.string;
                }
            });
            view.arch.attrs.col = 2 * view.arch.children.length;
            return view;
        },
        on_row_keyup: function (e) {
            var self = this;
            switch (e.which) {
            case KEY_RETURN:
                this.save_row().then(function (result) {
                    if (result.created) {
                        self.new_record();
                        return;
                    }

                    var next_record_id,
                        next_record = self.records.at(
                                self.records.indexOf(result.edited_record) + 1);
                    if (next_record) {
                        next_record_id = next_record.get('id');
                        self.dataset.index = _(self.dataset.ids)
                                .indexOf(next_record_id);
                    } else {
                        self.dataset.index = 0;
                        next_record_id = self.records.at(0).get('id');
                    }
                    self.edit_record(next_record_id);
                });
                break;
            case KEY_ESCAPE:
                this.cancel_edition();
                break;
            }
        },
        render_row_as_form: function (row) {
            var self = this;
            this.cancel_pending_edition().then(function () {
                var record_id = $(row).data('id');
                var $new_row = $('<tr>', {
                        id: _.uniqueId('oe-editable-row-'),
                        'data-id': record_id,
                        'class': (row ? $(row).attr('class') : '') + ' oe_forms',
                        click: function (e) {e.stopPropagation();}
                    })
                    .delegate('button.oe-edit-row-save', 'click', function () {
                        self.save_row();
                    })
                    .delegate('button', 'keyup', function (e) {
                        e.stopImmediatePropagation();
                    })
                    .keyup(function () {
                        return self.on_row_keyup.apply(self, arguments); })
                    .keydown(function (e) { e.stopPropagation(); });
                if (row) {
                    $new_row.replaceAll(row);
                } else if (self.options.editable) {
                    var $last_child = self.$current.children('tr:last');
                    if (self.records.length) {
                        if (self.options.editable === 'top') {
                            $new_row.insertBefore(
                                self.$current.children('[data-id]:first'));
                        } else {
                            $new_row.insertAfter(
                                self.$current.children('[data-id]:last'));
                        }
                    } else {
                        $new_row.prependTo(self.$current);
                    }
                    if ($last_child.is(':not([data-id])')) {
                        $last_child.remove();
                    }
                }
                self.edition = true;
                self.edition_id = record_id;
                self.edition_form = _.extend(new openerp.web.ListEditableFormView(self.view, self.dataset, false), {
                    form_template: 'ListView.row.form',
                    registry: openerp.web.list.form.widgets,
                    $element: $new_row
                });
                // HA HA
                self.edition_form.appendTo();
                $.when(self.edition_form.on_loaded(self.get_form_fields_view())).then(function () {
                    // put in $.when just in case  FormView.on_loaded becomes asynchronous
                    $new_row.find('> td')
                          .addClass('oe-field-cell')
                          .removeAttr('width')
                      .end()
                      .find('td:last').removeClass('oe-field-cell').end();
                    if (self.options.selectable) {
                        $new_row.prepend('<th>');
                    }
                    if (self.options.isClarkGable) {
                        $new_row.prepend('<th>');
                    }
                    // pad in case of groupby
                    _(self.columns).each(function (column) {
                        if (column.meta) {
                            $new_row.prepend('<td>');
                        }
                    });
                    // Add column for the save, if
                    // there is none in the list
                    if (!self.options.deletable) {
                        self.view.pad_columns(
                            1, {except: $new_row});
                    }

                    self.edition_form.do_show();
                });
            });
        },
        handle_onwrite: function (source_record_id) {
            var self = this;
            var on_write_callback = self.view.fields_view.arch.attrs.on_write;
            if (!on_write_callback) { return; }
            this.dataset.call(on_write_callback, [source_record_id], function (ids) {
                _(ids).each(function (id) {
                    var record = self.records.get(id);
                    if (!record) {
                        // insert after the source record
                        var index = self.records.indexOf(
                            self.records.get(source_record_id)) + 1;
                        record = new openerp.web.list.Record({id: id});
                        self.records.add(record, {at: index});
                        self.dataset.ids.splice(index, 0, id);
                    }
                    self.reload_record(record);
                });
            });
        },
        /**
         * Saves the current row, and returns a Deferred resolving to an object
         * with the following properties:
         *
         * ``created``
         *   Boolean flag indicating whether the record saved was being created
         *   (``true`` or edited (``false``)
         * ``edited_record``
         *   The result of saving the record (either the newly created record,
         *   or the post-edition record), after insertion in the Collection if
         *   needs be.
         *
         * @returns {$.Deferred<{created: Boolean, edited_record: Record}>}
         */
        save_row: function () {
            //noinspection JSPotentiallyInvalidConstructorUsage
            var self = this, done = $.Deferred();
            return this.edition_form
                .do_save(null, this.options.editable === 'top')
                .pipe(function (result) {
                    if (result.created && !self.edition_id) {
                        self.records.add({id: result.result},
                            {at: self.options.editable === 'top' ? 0 : null});
                        self.edition_id = result.result;
                    }
                    var edited_record = self.records.get(self.edition_id);

                    return $.when(
                        self.handle_onwrite(self.edition_id),
                        self.cancel_pending_edition().then(function () {
                            $(self).trigger('saved', [self.dataset]);
                        })).pipe(function () {
                            return {
                                created: result.created || false,
                                edited_record: edited_record
                            };
                        }, null);
                }, null);
        },
        /**
         * If the current list is being edited, ensures it's saved
         */
        ensure_saved: function () {
            if (this.edition) {
                return this.save_row();
            }
            //noinspection JSPotentiallyInvalidConstructorUsage
            return $.Deferred().resolve().promise();
        },
        /**
         * Cancels the edition of the row for the current dataset index
         */
        cancel_edition: function () {
            this.cancel_pending_edition();
        },
        /**
         * Edits record currently selected via dataset
         */
        edit_record: function (record_id) {
            this.render_row_as_form(
                this.$current.find('[data-id=' + record_id + ']'));
            $(this).trigger(
                'edit',
                [record_id, this.dataset]);
        },
        new_record: function () {
            this.dataset.index = null;
            this.render_row_as_form();
        },
        render_record: function (record) {
            var index = this.records.indexOf(record),
                 self = this;
            // FIXME: context dict should probably be extracted cleanly
            return QWeb.render('ListView.row', {
                columns: this.columns,
                options: this.options,
                record: record,
                row_parity: (index % 2 === 0) ? 'even' : 'odd',
                view: this.view,
                render_cell: function () {
                    return self.render_cell.apply(self, arguments); },
                edited: !!this.edition_form
            });
        }
    });
    if (!openerp.web.list) {
        openerp.web.list = {};
    }
    if (!openerp.web.list.form) {
        openerp.web.list.form = {};
    }
    openerp.web.list.form.WidgetFrame = openerp.web.form.WidgetFrame.extend({
        template: 'ListView.row.frame'
    });
    var form_widgets = openerp.web.form.widgets;
    openerp.web.list.form.widgets = form_widgets.extend({
        'frame': 'openerp.web.list.form.WidgetFrame'
    });
    // All form widgets inherit a problematic behavior from
    // openerp.web.form.WidgetFrame: the cell itself is removed when invisible
    // whether it's @invisible or @attrs[invisible]. In list view, only the
    // former should completely remove the cell. We need to override update_dom
    // on all widgets since we can't just hit on widget itself (I think)
    var list_form_widgets = openerp.web.list.form.widgets;
    _(form_widgets.map).each(function (widget_path, key) {
        if (key === 'frame') { return; }
        var new_path = 'openerp.web.list.form.' + key;

        openerp.web.list.form[key] = (form_widgets.get_object(key)).extend({
            update_dom: function () {
                this.$element.children().css('visibility', '');
                if (this.modifiers.tree_invisible) {
                    var old_invisible = this.invisible;
                    this.invisible = true;
                    this._super();
                    this.invisible = old_invisible;
                } else if (this.invisible) {
                    this.$element.children().css('visibility', 'hidden');
                } else {
                    this._super();
                }
            }
        });
        list_form_widgets.add(key, new_path);
    });

    openerp.web.ListEditableFormView = openerp.web.FormView.extend({
        init_view: function() {},
        _render_and_insert: function () {
            return this.start();
        }
    });
};
<|endoftext|>