<commit_before>(function ($) {
    $("#menu-toggle").click(function(e) {
        e.preventDefault();
        $("#wrapper").toggleClass("toggled");
    });
})(jQuery);

// Always send CSRF token with ajax requests.
(function ($) {
    $.ajaxSetup({
        headers: {
            'X-CSRF-Token': $('meta[name="_token"]').attr('content')
        }
    });
})(jQuery);

// Slide up alert messages after 4 seconds.
(function ($) {
    $(document).ready(function () {
        var firstAlert = $('.container:first > .alert');
        if (firstAlert.length !== 1) {
            return;
        }

        setTimeout(function () {
            firstAlert.slideUp(500);
        }, 4000);
    });
})(jQuery);

// Add the tab selector to the URL as a fragment when the tab changes and if a tab selector fragment is found in the URL
// when the page is loaded, select the relevant tab.
(function ($) {
    // Add the tab selector to the URL as a fragment when the tab changes
    $('input[name="tabs"][type="radio"]').on('change', function () {
        var newLocation = window.location.toString().replace(/#tab[0-9]+/g, '');
        window.location = newLocation + '#' + $(this).prop('id');
    });

    // If a tab selector fragment is found in the URL when the page is loaded, select the relevant tab.
    $(document).ready(function () {
        var matches = window.location.toString().match(/#tab[0-9]+/g);
        // Check the URL for tab selectors
        if (!matches || matches.length < 1) {
            return;
        }

        // Make sure the tab related to the selected exists in the DOM
        if ($(matches[0]).length < 1) {
            return;
        }

        // Select the relevant tab
        $(matches[0]).prop('checked', 'checked');
    });
})(jQuery);

// Click handler for the "Post" button to create new comments
(function ($) {
    $('#btn-chat').on('click', function (e) {
        e.preventDefault();
        $('#comment-form').submit();
    });
})(jQuery);

// Check for new comments every 20 seconds
(function ($) {
    if ($('.chat-card').length < 1 || window.location.toString().match(/comments_page=[2-9]([0-9]+)*/) !== null) {
        return;
    }

    var chatInterval = setInterval(function () {
        // Get the time since the newest chat and the vulnerability ID
        var timeSince       = $('ul.chat > li:first-child').find('.time-since'),
            newerThan       = timeSince.data('created-at') || '0000-00-00 00:00:00',
            vulnerabilityId = timeSince.data('vulnerability-id');

        // Make sure we have both otherwise we can't send the request
        if (!newerThan || !vulnerabilityId) {
            return;
        }

        $.ajax({
            url:      '/comments/updated/' + vulnerabilityId,
            type:     'POST',
            dataType: 'HTML',
            data:     'newer-than=' + newerThan
        }).done(function (comments) {
            // If there are no new comments, there's nothing to do
            if (!comments || comments.length < 1) {
                return;
            }

            // Prepend the new comments to the comments list and reset the comment count value
            $(comments).prependTo('ul.chat');
            $('#comment-count').text($('ul.chat > li').length);
            // TODO: update all the dates of all the other comments
        }).fail(function (jqXHR) {
            // Check which error was encountered
            var firstDigit = (''+jqXHR.status)[0],
                body       = $('body');

            if ((firstDigit != '5' && firstDigit != '4') || !body.data('comment-poll')) {
                return;
            }

            // If a 5xx for 4xx error was encountered then stop polling for comments until the next page request
            clearInterval(body.data('comment-poll'));
        });
    }, 20000);

    // Store the chat interval in a data attribute on the body element
    $('body').data('comment-poll', chatInterval);
})(jQuery);

// Enable existing comment editing
(function ($) {
    // Make sure the there are comment edit buttons on the page
    var commentEditBtns = $('.edit-comment');
    if (commentEditBtns.length < 1) {
        return;
    }

    // Bind a click handler to the edit comment button
    commentEditBtns.on('click', function (e) {
        // Make sure we can get the comment ID
        var commentId = $(this).data('comment-id');
        if (!commentId) {
            return;
        }

        // Prevent the default action and fade out the comment text and fade in the comment editor
        e.preventDefault();
        $('#comment-text-' + commentId).fadeOut(300, function() {
            $('#comment-editor-' + commentId).fadeIn(300);
        });
    });

    // Cancel comment editing button
    $('.edit-comment-cancel').on('click', function(e) {
        // Make sure we can get the comment ID
        var commentId = $(this).data('comment-id');
        if (!commentId) {
            return;
        }

        // Prevent the default action and fade out the comment editor fade in the comment text
        e.preventDefault();
        $('#comment-editor-' + commentId).fadeOut(300, function() {
            $('#comment-text-' + commentId).fadeIn(300);
        });
    });
})(jQuery);

// Enable existing comment edit form post when the "Save Changes" button is clicked
(function ($) {
    // Make sure we have a save comment button
    var saveCommentBtn = $('.btn-edit-chat');
    if (saveCommentBtn.length < 1) {
        return;
    }

    // When the save comment button is clicked, submit the form to save the comment
    saveCommentBtn.on('click', function (e) {
        var commentId = $(this).data('comment-id');
        if (!commentId) {
            return;
        }

        e.preventDefault();
        $('#comment-editor-' + commentId).find('form').submit();
    });
})(jQuery);

// Send to Jira with Ajax
(function ($) {
    var submitAjaxForm = function (formContainer, defaultErrorTxt, done, hideAfterSuccess) {
        if (formContainer.length < 1) {
            return;
        }

        defaultErrorTxt = defaultErrorTxt
            || "Something went wrong and we could not complete your request this time. Please try again.";

        var form = formContainer.find('form'),
            modalContent = formContainer.find('.modal-content'),
            overlayAndIcon = formContainer.find('.waiting-icon-container, .waiting-overlay'),
            defaultError = '
'
                + '<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>'
                + defaultErrorTxt + '
',
            // Hide the error alert after 5 seconds
            hideAlert = function (form) {
                var alert = modalContent.find('.alert');
                if (alert.length < 1 || !form) {
                    return;
                }

                form.find('input:visible, select:visible').on('focusin', function () {
                    alert.slideUp(500, function () {
                        $(this).remove();
                    });
                });
            };

        // When the Send to Jira form is submitted
        form.on('submit', function (e) {
            // Prevent the default form submission
            e.preventDefault();
            var form = $(this);

            // Send an ajax request with all the form data
            $.ajax({
                url: $(this).prop('action'),
                type: $(this).prop('method'),
                dataType: 'JSON',
                data: $(this).serialize(),
                // Overlay the form with a light-grey overlay and a loading icon
                beforeSend: function () {
                    overlayAndIcon.fadeIn(300);
                }
            }).done(function (data) {
                // Successful ajax request, but invalid response. Show the default error.
                if (!data.message) {
                    modalContent.prepend(defaultError);
                    return;
                }

                // Prepend the form with the response message
                modalContent.prepend(data.message);
                if (data.isError) {
                    // If the response is an error, hide it after 5s.
                    hideAlert(form);
                    return;
                }

                // Success: hide the success message only if the hideAfterSuccess parameter is set
                if (hideAfterSuccess) {
                    hideAlert(form);
                }

                // Reset the form inputs
                form.find('input:not([type="submit"]):not([name="_token"]), select').val("");

                // Execute custom done functionality
                if (done) {
                    done(data);
                }
            }).fail(function () {
                // Show the default error.
                modalContent.prepend(defaultError);
                hideAlert(form);
            }).always(function () {
                // Always hide the overlay and loading icon when the ajax request is done
                overlayAndIcon.fadeOut(300);
            });
        });
    };

    // Jira issue creation
    submitAjaxForm(
        $('#jira'),
        'Something went wrong and we were not able to create an new Jira Issue for you, but please try again.'
    );

    // New asset creation for the Ruggedy app
    submitAjaxForm(
        $('#add-asset-form'),
        "Something went wrong and we were not able to create a new Asset for you, but please try again.",
        function(data) {
            var assetContainer = $('#related-assets'),
                assetsSelect = $('#assets-select');
            // Make sure everything we need exists in the DOM
            if (assetContainer.length < 1 || assetsSelect.length < 1 || !data.html) {
                return;
            }

            // Append the Asset HTML to right-hand side of the form as a visual aid
            $(data.html).appendTo(assetContainer);

            // Clear the select options and reset them to all the added Assets as selected options of the multiple
            // select that will be sent when the Vulnerability record is sent
            assetsSelect.html("");
            assetContainer.find('.asset').each(function () {
                $('<option />').val($(this)
                    .data('asset-id'))
                    .prop('selected', 'selected')
                    .appendTo(assetsSelect);
            });
        },
        true
    );

})(jQuery);

// Enable removal of thumbnail images from custom Vulnerabilities
(function ($) {
    $('.remove-poc').on('click', function (e) {
        e.preventDefault();
        var mustDelete = confirm("Are you sure you want to remove this image? This cannot be undone.");
        if (!mustDelete) {
            return;
        }

        var thumbnailBlock = $(this).parent('.remove-block').parent();
        thumbnailBlock.siblings('label, input').removeClass('hidden');
        thumbnailBlock.remove();
    });
})(jQuery);

// Enable removing of Assets in the Ruggedy App Vulnerability view
(function ($) {
    $('.remove-asset').on('click', function (e) {
        e.preventDefault();

        // Confirm the action to delete an Asset
        var mustDelete = confirm("Are you sure you want to remove this Asset? This cannot be undone.");
        if (!mustDelete) {
            return;
        }

        // Initialise variables
        var defaultError   = '
'
            + '<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>'
            + 'The Asset could not be deleted. Please try again.
',
            relatedAssets  = $('#related-assets'),
            iconAndOverlay = $('.container:first').children('.waiting-icon-container, .waiting-overlay'),
            assetBlock     = $(this).parent('div').parent('div').parent('a').parent('.asset'),
            assetId        = $(this).data('asset-id'),
            assetsSelect   = $('#assets-select');

        // Prepare and send the ajax request
        $.ajax({
            url: '/asset/delete/' + assetId,
            type: 'GET',
            dataType: 'JSON',
            // Overlay the window with a light-grey overlay and a loading icon
            beforeSend: function () {
                iconAndOverlay.fadeIn(300)
            }
        }).fail(function () {
            // Show a standard error message
            relatedAssets.prepend(defaultError);
        }).done(function (data) {
            // Successful ajax request, but invalid response. Show the default error.
            if (!data.message) {
                relatedAssets.prepend(defaultError);
                return;
            }

            // Prepend the assets div with the response message
            relatedAssets.prepend(data.message);
            if (data.isError) {
                return;
            }

            // After 3.5 seconds slide up and remove the related Asset block and
            // remove the asset ID from the assets[] multiple select input
            setTimeout(function () {
                assetBlock.slideUp(500, function () {
                    $(this).remove();
                    // Clear the select options and reset them to all the added Assets as selected options of the
                    // multiple select that will be sent when the Vulnerability record is sent
                    assetsSelect.html("");
                    relatedAssets.find('.asset').each(function () {
                        $('<option />').val($(this)
                            .data('asset-id'))
                            .prop('selected', 'selected')
                            .appendTo(assetsSelect);
                    });
                });
            }, 3500);
        }).always(function () {
            // Clear the loading overlay and icon
            iconAndOverlay.fadeOut(300);

            // Find the alert message
            var alert = relatedAssets.find('.alert:first');
            if (alert.length !== 1) {
                return;
            }

            // Slide the alert message up after 3 seconds
            setTimeout(function () {
                alert.slideUp(500, function () {
                    $(this).remove();
                });
            }, 3000);
        });
    });
})(jQuery);

// Prompt before deleting
(function ($) {
    $(document).ready(function () {
        $('.delete-link').on('click', function (e) {
            e.preventDefault();
            var confirmUrl = $(this).prop('href');

            swal({
                title: "Are you sure?",
                text: "This cannot be undone.",
                type: "warning",
                showCancelButton: true,
                confirmButtonColor: "#DD6B55",
                confirmButtonText: "Yes, delete it!"
            }).then(function () {
                console.log(confirmUrl);
                window.location.href = confirmUrl;
            }).catch(swal.noop);
        });
    });
})(jQuery);<commit_msg>Make the delete sweetalert customisable per link using data attributes and fallback default values.<commit_after>(function ($) {
    $("#menu-toggle").click(function(e) {
        e.preventDefault();
        $("#wrapper").toggleClass("toggled");
    });
})(jQuery);

// Always send CSRF token with ajax requests.
(function ($) {
    $.ajaxSetup({
        headers: {
            'X-CSRF-Token': $('meta[name="_token"]').attr('content')
        }
    });
})(jQuery);

// Slide up alert messages after 4 seconds.
(function ($) {
    $(document).ready(function () {
        var firstAlert = $('.container:first > .alert');
        if (firstAlert.length !== 1) {
            return;
        }

        setTimeout(function () {
            firstAlert.slideUp(500);
        }, 4000);
    });
})(jQuery);

// Add the tab selector to the URL as a fragment when the tab changes and if a tab selector fragment is found in the URL
// when the page is loaded, select the relevant tab.
(function ($) {
    // Add the tab selector to the URL as a fragment when the tab changes
    $('input[name="tabs"][type="radio"]').on('change', function () {
        var newLocation = window.location.toString().replace(/#tab[0-9]+/g, '');
        window.location = newLocation + '#' + $(this).prop('id');
    });

    // If a tab selector fragment is found in the URL when the page is loaded, select the relevant tab.
    $(document).ready(function () {
        var matches = window.location.toString().match(/#tab[0-9]+/g);
        // Check the URL for tab selectors
        if (!matches || matches.length < 1) {
            return;
        }

        // Make sure the tab related to the selected exists in the DOM
        if ($(matches[0]).length < 1) {
            return;
        }

        // Select the relevant tab
        $(matches[0]).prop('checked', 'checked');
    });
})(jQuery);

// Click handler for the "Post" button to create new comments
(function ($) {
    $('#btn-chat').on('click', function (e) {
        e.preventDefault();
        $('#comment-form').submit();
    });
})(jQuery);

// Check for new comments every 20 seconds
(function ($) {
    if ($('.chat-card').length < 1 || window.location.toString().match(/comments_page=[2-9]([0-9]+)*/) !== null) {
        return;
    }

    var chatInterval = setInterval(function () {
        // Get the time since the newest chat and the vulnerability ID
        var timeSince       = $('ul.chat > li:first-child').find('.time-since'),
            newerThan       = timeSince.data('created-at') || '0000-00-00 00:00:00',
            vulnerabilityId = timeSince.data('vulnerability-id');

        // Make sure we have both otherwise we can't send the request
        if (!newerThan || !vulnerabilityId) {
            return;
        }

        $.ajax({
            url:      '/comments/updated/' + vulnerabilityId,
            type:     'POST',
            dataType: 'HTML',
            data:     'newer-than=' + newerThan
        }).done(function (comments) {
            // If there are no new comments, there's nothing to do
            if (!comments || comments.length < 1) {
                return;
            }

            // Prepend the new comments to the comments list and reset the comment count value
            $(comments).prependTo('ul.chat');
            $('#comment-count').text($('ul.chat > li').length);
            // TODO: update all the dates of all the other comments
        }).fail(function (jqXHR) {
            // Check which error was encountered
            var firstDigit = (''+jqXHR.status)[0],
                body       = $('body');

            if ((firstDigit != '5' && firstDigit != '4') || !body.data('comment-poll')) {
                return;
            }

            // If a 5xx for 4xx error was encountered then stop polling for comments until the next page request
            clearInterval(body.data('comment-poll'));
        });
    }, 20000);

    // Store the chat interval in a data attribute on the body element
    $('body').data('comment-poll', chatInterval);
})(jQuery);

// Enable existing comment editing
(function ($) {
    // Make sure the there are comment edit buttons on the page
    var commentEditBtns = $('.edit-comment');
    if (commentEditBtns.length < 1) {
        return;
    }

    // Bind a click handler to the edit comment button
    commentEditBtns.on('click', function (e) {
        // Make sure we can get the comment ID
        var commentId = $(this).data('comment-id');
        if (!commentId) {
            return;
        }

        // Prevent the default action and fade out the comment text and fade in the comment editor
        e.preventDefault();
        $('#comment-text-' + commentId).fadeOut(300, function() {
            $('#comment-editor-' + commentId).fadeIn(300);
        });
    });

    // Cancel comment editing button
    $('.edit-comment-cancel').on('click', function(e) {
        // Make sure we can get the comment ID
        var commentId = $(this).data('comment-id');
        if (!commentId) {
            return;
        }

        // Prevent the default action and fade out the comment editor fade in the comment text
        e.preventDefault();
        $('#comment-editor-' + commentId).fadeOut(300, function() {
            $('#comment-text-' + commentId).fadeIn(300);
        });
    });
})(jQuery);

// Enable existing comment edit form post when the "Save Changes" button is clicked
(function ($) {
    // Make sure we have a save comment button
    var saveCommentBtn = $('.btn-edit-chat');
    if (saveCommentBtn.length < 1) {
        return;
    }

    // When the save comment button is clicked, submit the form to save the comment
    saveCommentBtn.on('click', function (e) {
        var commentId = $(this).data('comment-id');
        if (!commentId) {
            return;
        }

        e.preventDefault();
        $('#comment-editor-' + commentId).find('form').submit();
    });
})(jQuery);

// Send to Jira with Ajax
(function ($) {
    var submitAjaxForm = function (formContainer, defaultErrorTxt, done, hideAfterSuccess) {
        if (formContainer.length < 1) {
            return;
        }

        defaultErrorTxt = defaultErrorTxt
            || "Something went wrong and we could not complete your request this time. Please try again.";

        var form = formContainer.find('form'),
            modalContent = formContainer.find('.modal-content'),
            overlayAndIcon = formContainer.find('.waiting-icon-container, .waiting-overlay'),
            defaultError = '
'
                + '<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>'
                + defaultErrorTxt + '
',
            // Hide the error alert after 5 seconds
            hideAlert = function (form) {
                var alert = modalContent.find('.alert');
                if (alert.length < 1 || !form) {
                    return;
                }

                form.find('input:visible, select:visible').on('focusin', function () {
                    alert.slideUp(500, function () {
                        $(this).remove();
                    });
                });
            };

        // When the Send to Jira form is submitted
        form.on('submit', function (e) {
            // Prevent the default form submission
            e.preventDefault();
            var form = $(this);

            // Send an ajax request with all the form data
            $.ajax({
                url: $(this).prop('action'),
                type: $(this).prop('method'),
                dataType: 'JSON',
                data: $(this).serialize(),
                // Overlay the form with a light-grey overlay and a loading icon
                beforeSend: function () {
                    overlayAndIcon.fadeIn(300);
                }
            }).done(function (data) {
                // Successful ajax request, but invalid response. Show the default error.
                if (!data.message) {
                    modalContent.prepend(defaultError);
                    return;
                }

                // Prepend the form with the response message
                modalContent.prepend(data.message);
                if (data.isError) {
                    // If the response is an error, hide it after 5s.
                    hideAlert(form);
                    return;
                }

                // Success: hide the success message only if the hideAfterSuccess parameter is set
                if (hideAfterSuccess) {
                    hideAlert(form);
                }

                // Reset the form inputs
                form.find('input:not([type="submit"]):not([name="_token"]), select').val("");

                // Execute custom done functionality
                if (done) {
                    done(data);
                }
            }).fail(function () {
                // Show the default error.
                modalContent.prepend(defaultError);
                hideAlert(form);
            }).always(function () {
                // Always hide the overlay and loading icon when the ajax request is done
                overlayAndIcon.fadeOut(300);
            });
        });
    };

    // Jira issue creation
    submitAjaxForm(
        $('#jira'),
        'Something went wrong and we were not able to create an new Jira Issue for you, but please try again.'
    );

    // New asset creation for the Ruggedy app
    submitAjaxForm(
        $('#add-asset-form'),
        "Something went wrong and we were not able to create a new Asset for you, but please try again.",
        function(data) {
            var assetContainer = $('#related-assets'),
                assetsSelect = $('#assets-select');
            // Make sure everything we need exists in the DOM
            if (assetContainer.length < 1 || assetsSelect.length < 1 || !data.html) {
                return;
            }

            // Append the Asset HTML to right-hand side of the form as a visual aid
            $(data.html).appendTo(assetContainer);

            // Clear the select options and reset them to all the added Assets as selected options of the multiple
            // select that will be sent when the Vulnerability record is sent
            assetsSelect.html("");
            assetContainer.find('.asset').each(function () {
                $('<option />').val($(this)
                    .data('asset-id'))
                    .prop('selected', 'selected')
                    .appendTo(assetsSelect);
            });
        },
        true
    );

})(jQuery);

// Enable removal of thumbnail images from custom Vulnerabilities
(function ($) {
    $('.remove-poc').on('click', function (e) {
        e.preventDefault();
        var mustDelete = confirm("Are you sure you want to remove this image? This cannot be undone.");
        if (!mustDelete) {
            return;
        }

        var thumbnailBlock = $(this).parent('.remove-block').parent();
        thumbnailBlock.siblings('label, input').removeClass('hidden');
        thumbnailBlock.remove();
    });
})(jQuery);

// Enable removing of Assets in the Ruggedy App Vulnerability view
(function ($) {
    $('.remove-asset').on('click', function (e) {
        e.preventDefault();

        // Confirm the action to delete an Asset
        var mustDelete = confirm("Are you sure you want to remove this Asset? This cannot be undone.");
        if (!mustDelete) {
            return;
        }

        // Initialise variables
        var defaultError   = '
'
            + '<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>'
            + 'The Asset could not be deleted. Please try again.
',
            relatedAssets  = $('#related-assets'),
            iconAndOverlay = $('.container:first').children('.waiting-icon-container, .waiting-overlay'),
            assetBlock     = $(this).parent('div').parent('div').parent('a').parent('.asset'),
            assetId        = $(this).data('asset-id'),
            assetsSelect   = $('#assets-select');

        // Prepare and send the ajax request
        $.ajax({
            url: '/asset/delete/' + assetId,
            type: 'GET',
            dataType: 'JSON',
            // Overlay the window with a light-grey overlay and a loading icon
            beforeSend: function () {
                iconAndOverlay.fadeIn(300)
            }
        }).fail(function () {
            // Show a standard error message
            relatedAssets.prepend(defaultError);
        }).done(function (data) {
            // Successful ajax request, but invalid response. Show the default error.
            if (!data.message) {
                relatedAssets.prepend(defaultError);
                return;
            }

            // Prepend the assets div with the response message
            relatedAssets.prepend(data.message);
            if (data.isError) {
                return;
            }

            // After 3.5 seconds slide up and remove the related Asset block and
            // remove the asset ID from the assets[] multiple select input
            setTimeout(function () {
                assetBlock.slideUp(500, function () {
                    $(this).remove();
                    // Clear the select options and reset them to all the added Assets as selected options of the
                    // multiple select that will be sent when the Vulnerability record is sent
                    assetsSelect.html("");
                    relatedAssets.find('.asset').each(function () {
                        $('<option />').val($(this)
                            .data('asset-id'))
                            .prop('selected', 'selected')
                            .appendTo(assetsSelect);
                    });
                });
            }, 3500);
        }).always(function () {
            // Clear the loading overlay and icon
            iconAndOverlay.fadeOut(300);

            // Find the alert message
            var alert = relatedAssets.find('.alert:first');
            if (alert.length !== 1) {
                return;
            }

            // Slide the alert message up after 3 seconds
            setTimeout(function () {
                alert.slideUp(500, function () {
                    $(this).remove();
                });
            }, 3000);
        });
    });
})(jQuery);

// Prompt before deleting
(function ($) {
    $(document).ready(function () {
        $('.delete-link').on('click', function (e) {
            e.preventDefault();
            var confirmUrl = $(this).prop('href'),
                title      = $(this).data('confirm-title') || "Are you sure?",
                text       = $(this).data('confirm-text') || "This cannot be undone.",
                btnText    = $(this).data('confirm-btn-text') || "Yes, delete it!";

            swal({
                title: title,
                text: text,
                type: "warning",
                showCancelButton: true,
                confirmButtonColor: "#DD6B55",
                confirmButtonText: btnText
            }).then(function () {
                console.log(confirmUrl);
                window.location.href = confirmUrl;
            }).catch(swal.noop);
        });
    });
})(jQuery);<|endoftext|>


<commit_before>'use strict';

var chalk   = require('chalk');
var latest  = require('github-latest');
var path    = require('path');
var request = require('request');
var util    = require('util');
var yeoman  = require('yeoman-generator');
var yosay   = require('yosay');

var EvolveGenerator = yeoman.generators.Base.extend({
  init: function() {
    if (this.args.length) {
      this.framework = this.args[0];
    }

    if (this.framework && this.framework.indexOf('#') > 0) {
      this.branch = this.framework.split('#').pop() || 'master';
    }
  },

  listFrameworks: function() {
    if (this.framework) {
      return false;
    }

    var done    = this.async();
    var options = {
      url: 'https://api.github.com/orgs/evolution/repos',
      headers: {
        'User-Agent': 'evolution/generator-evolve',
      },
    };

    request(options, function(err, response, body) {
      if (err || response.statusCode !== 200) {
        console.error(response);

        throw err;
      }

      this.frameworks = JSON.parse(body).map(function(repo) {
        return repo.name;
      }).filter(function(name) {
        return name.match(/^\w+$/);
      });

      done();
    }.bind(this));
  },

  promptForFramework: function() {
    if (this.framework || !this.frameworks) {
      return false;
    }

    if (this.frameworks.length === 1) {
      this.framework = this.frameworks[0];

      return true;
    }

    var done = this.async();

    this.prompt([
      {
        type: 'list',
        name: 'framework',
        message: 'Select a framework',
        choices: this.frameworks,
      }
    ], function(answers) {
      this.framework = answers.framework;
      done();
    }.bind(this));
  },

  validateFramework: function() {
    if (!this.framework) {
      throw new Error('No Evolution framework selected');
    }
  },

  setProjectName: function() {
    this.projectName = [
      chalk.dim('['),
      chalk.yellow('Evolution'),
      chalk.white(this.framework[0].toUpperCase() + this.framework.slice(1)),
      chalk.dim('Generator'),
      chalk.dim(']'),
    ].join(' ');

    this.log.info(this.projectName);
  },

  getBranch: function() {
    if (this.branch) {
      return false;
    }

    var done = this.async();

    latest('evolution', this.framework, function(err, tag) {
      this.branch = tag || 'master'
      done();
    }.bind(this));
  },

  downloadBranch: function() {
    if (this.options['framework-path']) {
      this.options['dev'] = true;

      return false;
    }

    var done = this.async();

    this.log.info('Downloading', chalk.yellow('evolution') + '/' + chalk.white(this.framework) + '@' + chalk.dim(this.branch));

    this.remote('evolution', this.framework, this.branch, function(err, remote) {
      if (err) {
        throw err;
      }

      this.options['framework-path'] = remote.cachePath;
      done();
    }.bind(this));
  },

  resolveFrameworkPath: function() {
    this.options['framework-path'] = path.resolve(this.options['framework-path'].split('~').join(process.env.HOME));

    this.log.info('Generator path:', chalk.white(this.options['framework-path']));
  },

  installDependencies: function() {
    var done  = this.async();
    var cwd   = process.cwd();

    process.chdir(this.options['framework-path']);

    this.log.info('Installing NPM dependencies...');

    this.npmInstall(this.options['framework-path'], { 'quiet': false }, function() {
      process.chdir(cwd);
      done();
    });
  },

  runFrameworkGenerator: function() {
    var Generator = require(this.options['framework-path']);
    var generator = new Generator(this.args, this.options);

    generator.run();
  },
});

module.exports = EvolveGenerator;
<commit_msg>Quiet npm install<commit_after>'use strict';

var chalk   = require('chalk');
var latest  = require('github-latest');
var path    = require('path');
var request = require('request');
var util    = require('util');
var yeoman  = require('yeoman-generator');
var yosay   = require('yosay');

var EvolveGenerator = yeoman.generators.Base.extend({
  init: function() {
    if (this.args.length) {
      this.framework = this.args[0];
    }

    if (this.framework && this.framework.indexOf('#') > 0) {
      this.branch = this.framework.split('#').pop() || 'master';
    }
  },

  listFrameworks: function() {
    if (this.framework) {
      return false;
    }

    var done    = this.async();
    var options = {
      url: 'https://api.github.com/orgs/evolution/repos',
      headers: {
        'User-Agent': 'evolution/generator-evolve',
      },
    };

    request(options, function(err, response, body) {
      if (err || response.statusCode !== 200) {
        console.error(response);

        throw err;
      }

      this.frameworks = JSON.parse(body).map(function(repo) {
        return repo.name;
      }).filter(function(name) {
        return name.match(/^\w+$/);
      });

      done();
    }.bind(this));
  },

  promptForFramework: function() {
    if (this.framework || !this.frameworks) {
      return false;
    }

    if (this.frameworks.length === 1) {
      this.framework = this.frameworks[0];

      return true;
    }

    var done = this.async();

    this.prompt([
      {
        type: 'list',
        name: 'framework',
        message: 'Select a framework',
        choices: this.frameworks,
      }
    ], function(answers) {
      this.framework = answers.framework;
      done();
    }.bind(this));
  },

  validateFramework: function() {
    if (!this.framework) {
      throw new Error('No Evolution framework selected');
    }
  },

  setProjectName: function() {
    this.projectName = [
      chalk.dim('['),
      chalk.yellow('Evolution'),
      chalk.white(this.framework[0].toUpperCase() + this.framework.slice(1)),
      chalk.dim('Generator'),
      chalk.dim(']'),
    ].join(' ');

    this.log.info(this.projectName);
  },

  getBranch: function() {
    if (this.branch) {
      return false;
    }

    var done = this.async();

    latest('evolution', this.framework, function(err, tag) {
      this.branch = tag || 'master'
      done();
    }.bind(this));
  },

  downloadBranch: function() {
    if (this.options['framework-path']) {
      this.options['dev'] = true;

      return false;
    }

    var done = this.async();

    this.log.info('Downloading', chalk.yellow('evolution') + '/' + chalk.white(this.framework) + '@' + chalk.dim(this.branch));

    this.remote('evolution', this.framework, this.branch, function(err, remote) {
      if (err) {
        throw err;
      }

      this.options['framework-path'] = remote.cachePath;
      done();
    }.bind(this));
  },

  resolveFrameworkPath: function() {
    this.options['framework-path'] = path.resolve(this.options['framework-path'].split('~').join(process.env.HOME));

    this.log.info('Generator path:', chalk.white(this.options['framework-path']));
  },

  installDependencies: function() {
    var done  = this.async();
    var cwd   = process.cwd();

    process.chdir(this.options['framework-path']);

    this.log.info('Installing NPM dependencies...');

    this.npmInstall(this.options['framework-path'], { 'quiet': true }, function() {
      process.chdir(cwd);
      done();
    });
  },

  runFrameworkGenerator: function() {
    var Generator = require(this.options['framework-path']);
    var generator = new Generator(this.args, this.options);

    generator.run();
  },
});

module.exports = EvolveGenerator;
<|endoftext|>


<commit_before>'use strict';

var fs = require('fs');
var path = require('path');
var generators = require('yeoman-generator');
var _ = require('lodash');
var mout = require('mout');
var cordova = require('cordova');
var chalk = require('chalk');
var ionicUtils = require('../utils');

var appPath = path.join(process.cwd(), 'app');

module.exports = generators.Base.extend({
  constructor: function () {
    generators.Base.apply(this, arguments);
    console.log(ionicUtils.greeting);

    this.argument('appName', { type: String, required: false });
    this.option('appName', { type: String, required: false });
    this.option('appId', { type: String, required: false });
    this.option('compass', { type: Boolean, required: false });
    this.option('starter', { type: String, required: false });
    this.option('templates', { type: Array, required: false });
    this.option('plugins', { type: Object, required: false });
    this.options.selected = {};
  },

  prompting: {
    askForCompass: function askForCompass() {
      var done = this.async();

      this.prompt([{
        type: 'confirm',
        name: 'compass',
        message: 'Would you like to use Sass with Compass (requires Ruby)?',
        default: (typeof(this.options.compass) !== 'undefined') ? this.options.compass : false
      }], function (props) {
        this.compass = this.options.selected.compass = props.compass;

        done();
      }.bind(this));
    },

    askForPlugins: function askForPlugins() {
      var done = this.async();

      if (this.options.plugins) {
        ionicUtils.mergePlugins(this.options.plugins);
      }

      this.prompt(ionicUtils.plugins.prompts, function (props) {
        this.plugins = this.options.selected.plugins = props.plugins;

        done();
      }.bind(this));
    },

    askForStarter: function askForStarter() {
      var done = this.async();

      if (this.options.templates) {
        ionicUtils.mergeStarterTemplates(this.options.templates);
      }

      var defaultIndex = 0;
      if (this.options.starter) {
        defaultIndex = _.findIndex(ionicUtils.starters.templates, { name: this.options.starter });

        if (defaultIndex === -1) {
          defaultIndex = 0;
          this.log(chalk.bgYellow(chalk.black('WARN')) +
            chalk.magenta(' Unable to locate the requested default template: ') +
            this.options.starter);
        }
      }

      this.prompt([{
        type: 'list',
        name: 'starter',
        message: 'Which starter template would you like to use?',
        choices: _.pluck(ionicUtils.starters.templates, 'name'),
        default: defaultIndex
      }], function (props) {
        this.starter = this.options.selected.starter = _.find(ionicUtils.starters.templates, { name: props.starter });
        done();
      }.bind(this));
    }

  },

  configuring: {
    commonVariables: function() {
      this.appName = this.appName || this.options.appName || path.basename(process.cwd());
      this.appName = mout.string.pascalCase(this.appName);
      this.appId = this.options.appId || 'com.example.' + this.appName;
      this.appPath = 'app';
      this.root = process.cwd();

      this.pkg = JSON.parse(this.readFileAsString(path.join(__dirname, '../package.json')));
    },

    setupEnv: function setupEnv() {
        // Removes thumbnail cache files
        var invisibleFiles = ['Thumbs.db', '.DS_Store'];
        invisibleFiles.forEach(function(filename) {
            var file = path.join(process.cwd(), filename)
            if(fs.existsSync(file) ) {
                fs.unlinkSync(file);
            }
       });
      // Copies the contents of the generator example app
      // directory into your users new application path
      this.sourceRoot(path.join(__dirname, '../templates/'));
      this.directory('common/root', '.', true);
    },

    packageFiles: function packageFiles() {
      this.template('common/_bower.json', 'bower.json');
      this.template('common/_bowerrc', '.bowerrc');
      this.template('common/_package.json', 'package.json');
      this.template('common/Gruntfile.js', 'Gruntfile.js');
      this.template('common/_gitignore', '.gitignore');
    }
  },

  writing: {
    cordovaInit: function cordovaInit() {
      var done = this.async();
      cordova.create('.', this.appId, this.appName, function (error) {
        if (error) {
          console.log(chalk.yellow(error.message + ': Skipping `cordova create`'));
        } else {
          console.log(chalk.yellow('Created a new Cordova project with name "' + this.appName + '" and id "' + this.appId + '"'));
        }
        done();
      }.bind(this));
    },

    installPlugins: function installPlugins() {
      console.log(chalk.yellow('\nInstall plugins registered at plugins.cordova.io: ') + chalk.green('grunt plugin:add:org.apache.cordova.globalization'));
      console.log(chalk.yellow('Or install plugins direct from source: ') + chalk.green('grunt plugin:add:https://github.com/apache/cordova-plugin-console.git\n'));
      if (this.plugins.length > 0) {
        console.log(chalk.yellow('Installing selected Cordova plugins, please wait.'));

        // Turns out plugin() doesn't accept a callback so we try/catch instead
        try {
          cordova.plugin('add', this.plugins);
        } catch (e) {
          console.log(e);
          this.log.error(chalk.red('Please run `yo ionic` in an empty directory, or in that of an already existing cordova project.'));
          process.exit(1);
        }
      }
    },

    installStarter: function installStarter() {
      console.log(chalk.yellow('Installing starter template. Please wait'));
      var done = this.async();

      var callback = function (error, remote) {
        if (error) {
          done(error);
        }

        // Template remote initialization: Copy from remote root folder (.) to working directory (/app)
        remote.directory('.', 'app');

        this.starterCache = remote.cachePath;
        done();
      }.bind(this);

      if (this.starter && this.starter.path) {
        this.log(chalk.bgYellow(chalk.black('WARN')) +
          chalk.magenta(' Getting the template from a local path.  This should only be used for developing new templates.'));
        this.remoteDir(this.starter.path, callback);
      } else if (this.starter.url) {
        this.remote(this.starter.url, callback, true);
      } else {
        this.remote(this.starter.user, this.starter.repo, 'master', callback, true);
      }
    },

    readIndex: function readIndex() {
      this.indexFile = this.engine(this.read(path.join(this.starterCache, 'index.html')), this);
    },

    appJs: function appJs() {

      var scriptPrefix = 'js' + path.sep;

      var scripts = [scriptPrefix + 'configuration.js'];

      this.fs.store.each(function (file, index) {
        if (file.path.indexOf('.js') !== -1)
        {
          var relPath = path.relative(appPath, file.path);
          if (relPath.indexOf(scriptPrefix) === 0) {
            scripts.push(relPath);
          }
        }
      });

      //this.indexFile = this.appendScripts(this.indexFile, 'scripts/scripts.js', scripts);
    },

    createIndexHtml: function createIndexHtml() {

        // Regex: Vendor CSS
        this.indexFile = this.indexFile.replace(/<link href="lib\/ionic\/css\/ionic.css" rel="stylesheet">/g, "<!-- build:css styles\/vendor.css -->\n    <!-- bower:css -->\n    <!-- endbower -->\n    <!-- endbuild -->");

        // Regex: User CSS
        //this.indexFile = this.indexFile.replace(/<link href="css\/style.css" rel="stylesheet">/g, "<!-- build:css styles\/vendor.css -->\n    <!-- bower:css -->\n    <!-- endbower -->\n    <!-- endbuild -->");

        // Regex: Vendor scripts (vendor.js)
        this.indexFile = this.indexFile.replace(/<script src="lib\/ionic\/js\/ionic.bundle.js"><\/script>/g, "<!-- build:js scripts\/vendor.js -->\n    <!-- bower:js -->\n    <!-- endbower -->\n    <!-- endbuild -->");

       // Regex: User scripts (scripts.js)
       this.indexFile = this.indexFile.replace(/<!-- your app's js -->/g,"<!-- your app's js -->\n    <!-- build:js scripts\/scripts.js -->");
       this.indexFile = this.indexFile.replace(/<\/head>/g,"  <script src=\"scripts\/configuration.js\"><\/script>\n  <!-- endbuild -->\n  <\/head>");

       // Regex/Rename: Scripts path (Ionics 'js' to 'scripts')
       this.indexFile = this.indexFile.replace(/href="css/g,"href=\"styles");

       // Regex/Rename: CSS path (Ionics 'css' to 'styles')
       this.indexFile = this.indexFile.replace(/src="js/g,"src=\"scripts");

       // Write index.html
       this.indexFile = this.indexFile.replace(/'/g, "'");
       this.write(path.join(this.appPath, 'index.html'), this.indexFile);
    },

    ensureStyles: function ensureStyles() {
      // Only create a main style file if the starter template didn't
      // have any styles. In the case it does, the starter should
      // supply both main.css and main.scss files, one of which
      // will be deleted

      var cssFile = 'main.' + (this.compass ? 'scss' : 'css');
      var unusedFile = 'main.' + (this.compass ? 'css' : 'scss');
      var stylePath = path.join(process.cwd(), 'app', 'css');
      var found = false;

      this.fs.store.each(function (file, index) {
        if (path.dirname(file.path) === stylePath) {
          var name = path.basename(file.path);

          if (name === cssFile) {
            found = true;
          } else if (name === unusedFile) {
            // BUG: The log will still report the the file was created
            this.fs.delete(file.path);
          }
        }
      }.bind(this));

      if (!found) {
        this.copy('styles/' + cssFile, 'app/css/' + cssFile);
      }

    },

    cordovaHooks: function cordovaHooks() {
      this.directory('hooks', 'hooks', true);
    },

    testFiles: function testFiles() {
      this.template('spec/controllers.js', 'test/spec/controllers.js');
    },

    packages: function () {
      this.installDependencies({ skipInstall: this.options['skip-install'] });
    }
  },

  end: {
    hookPerms: function hookPerms() {
      var iconsAndSplash = 'hooks/after_prepare/icons_and_splashscreens.js';
      fs.chmodSync(iconsAndSplash, '755');
    },
    folderNames: function folderNames() {
      // Rename: Scripts path (Ionics 'js' to 'scripts')
      fs.rename(path.join(appPath, 'css'), path.join(appPath, 'styles'), function(err) {
          if ( err ) console.log('ERROR: ' + err);
      });

      // Rename: CSS path (Ionics 'css' to 'styles')
      fs.rename(path.join(appPath, 'js'), path.join(appPath, 'scripts'), function(err) {
          if ( err ) console.log('ERROR: ' + err);
      });

      // Rename: Images path (Ionics 'img' to 'images')
      fs.rename(path.join(appPath, 'img'), path.join(appPath, 'images'), function(err) {
          if ( err ) console.log('ERROR: ' + err);
      });
    },

  }
});

<commit_msg>Small fix for index.html indentation.<commit_after>'use strict';

var fs = require('fs');
var path = require('path');
var generators = require('yeoman-generator');
var _ = require('lodash');
var mout = require('mout');
var cordova = require('cordova');
var chalk = require('chalk');
var ionicUtils = require('../utils');

var appPath = path.join(process.cwd(), 'app');

module.exports = generators.Base.extend({
  constructor: function () {
    generators.Base.apply(this, arguments);
    console.log(ionicUtils.greeting);

    this.argument('appName', { type: String, required: false });
    this.option('appName', { type: String, required: false });
    this.option('appId', { type: String, required: false });
    this.option('compass', { type: Boolean, required: false });
    this.option('starter', { type: String, required: false });
    this.option('templates', { type: Array, required: false });
    this.option('plugins', { type: Object, required: false });
    this.options.selected = {};
  },

  prompting: {
    askForCompass: function askForCompass() {
      var done = this.async();

      this.prompt([{
        type: 'confirm',
        name: 'compass',
        message: 'Would you like to use Sass with Compass (requires Ruby)?',
        default: (typeof(this.options.compass) !== 'undefined') ? this.options.compass : false
      }], function (props) {
        this.compass = this.options.selected.compass = props.compass;

        done();
      }.bind(this));
    },

    askForPlugins: function askForPlugins() {
      var done = this.async();

      if (this.options.plugins) {
        ionicUtils.mergePlugins(this.options.plugins);
      }

      this.prompt(ionicUtils.plugins.prompts, function (props) {
        this.plugins = this.options.selected.plugins = props.plugins;

        done();
      }.bind(this));
    },

    askForStarter: function askForStarter() {
      var done = this.async();

      if (this.options.templates) {
        ionicUtils.mergeStarterTemplates(this.options.templates);
      }

      var defaultIndex = 0;
      if (this.options.starter) {
        defaultIndex = _.findIndex(ionicUtils.starters.templates, { name: this.options.starter });

        if (defaultIndex === -1) {
          defaultIndex = 0;
          this.log(chalk.bgYellow(chalk.black('WARN')) +
            chalk.magenta(' Unable to locate the requested default template: ') +
            this.options.starter);
        }
      }

      this.prompt([{
        type: 'list',
        name: 'starter',
        message: 'Which starter template would you like to use?',
        choices: _.pluck(ionicUtils.starters.templates, 'name'),
        default: defaultIndex
      }], function (props) {
        this.starter = this.options.selected.starter = _.find(ionicUtils.starters.templates, { name: props.starter });
        done();
      }.bind(this));
    }

  },

  configuring: {
    commonVariables: function() {
      this.appName = this.appName || this.options.appName || path.basename(process.cwd());
      this.appName = mout.string.pascalCase(this.appName);
      this.appId = this.options.appId || 'com.example.' + this.appName;
      this.appPath = 'app';
      this.root = process.cwd();

      this.pkg = JSON.parse(this.readFileAsString(path.join(__dirname, '../package.json')));
    },

    setupEnv: function setupEnv() {
        // Removes thumbnail cache files
        var invisibleFiles = ['Thumbs.db', '.DS_Store'];
        invisibleFiles.forEach(function(filename) {
            var file = path.join(process.cwd(), filename)
            if(fs.existsSync(file) ) {
                fs.unlinkSync(file);
            }
       });
      // Copies the contents of the generator example app
      // directory into your users new application path
      this.sourceRoot(path.join(__dirname, '../templates/'));
      this.directory('common/root', '.', true);
    },

    packageFiles: function packageFiles() {
      this.template('common/_bower.json', 'bower.json');
      this.template('common/_bowerrc', '.bowerrc');
      this.template('common/_package.json', 'package.json');
      this.template('common/Gruntfile.js', 'Gruntfile.js');
      this.template('common/_gitignore', '.gitignore');
    }
  },

  writing: {
    cordovaInit: function cordovaInit() {
      var done = this.async();
      cordova.create('.', this.appId, this.appName, function (error) {
        if (error) {
          console.log(chalk.yellow(error.message + ': Skipping `cordova create`'));
        } else {
          console.log(chalk.yellow('Created a new Cordova project with name "' + this.appName + '" and id "' + this.appId + '"'));
        }
        done();
      }.bind(this));
    },

    installPlugins: function installPlugins() {
      console.log(chalk.yellow('\nInstall plugins registered at plugins.cordova.io: ') + chalk.green('grunt plugin:add:org.apache.cordova.globalization'));
      console.log(chalk.yellow('Or install plugins direct from source: ') + chalk.green('grunt plugin:add:https://github.com/apache/cordova-plugin-console.git\n'));
      if (this.plugins.length > 0) {
        console.log(chalk.yellow('Installing selected Cordova plugins, please wait.'));

        // Turns out plugin() doesn't accept a callback so we try/catch instead
        try {
          cordova.plugin('add', this.plugins);
        } catch (e) {
          console.log(e);
          this.log.error(chalk.red('Please run `yo ionic` in an empty directory, or in that of an already existing cordova project.'));
          process.exit(1);
        }
      }
    },

    installStarter: function installStarter() {
      console.log(chalk.yellow('Installing starter template. Please wait'));
      var done = this.async();

      var callback = function (error, remote) {
        if (error) {
          done(error);
        }

        // Template remote initialization: Copy from remote root folder (.) to working directory (/app)
        remote.directory('.', 'app');

        this.starterCache = remote.cachePath;
        done();
      }.bind(this);

      if (this.starter && this.starter.path) {
        this.log(chalk.bgYellow(chalk.black('WARN')) +
          chalk.magenta(' Getting the template from a local path.  This should only be used for developing new templates.'));
        this.remoteDir(this.starter.path, callback);
      } else if (this.starter.url) {
        this.remote(this.starter.url, callback, true);
      } else {
        this.remote(this.starter.user, this.starter.repo, 'master', callback, true);
      }
    },

    readIndex: function readIndex() {
      this.indexFile = this.engine(this.read(path.join(this.starterCache, 'index.html')), this);
    },

    appJs: function appJs() {

      var scriptPrefix = 'js' + path.sep;

      var scripts = [scriptPrefix + 'configuration.js'];

      this.fs.store.each(function (file, index) {
        if (file.path.indexOf('.js') !== -1)
        {
          var relPath = path.relative(appPath, file.path);
          if (relPath.indexOf(scriptPrefix) === 0) {
            scripts.push(relPath);
          }
        }
      });

      //this.indexFile = this.appendScripts(this.indexFile, 'scripts/scripts.js', scripts);
    },

    createIndexHtml: function createIndexHtml() {

        // Regex: Vendor CSS
        this.indexFile = this.indexFile.replace(/<link href="lib\/ionic\/css\/ionic.css" rel="stylesheet">/g, "<!-- build:css styles\/vendor.css -->\n    <!-- bower:css -->\n    <!-- endbower -->\n    <!-- endbuild -->");

        // Regex: User CSS
        //this.indexFile = this.indexFile.replace(/<link href="css\/style.css" rel="stylesheet">/g, "<!-- build:css styles\/vendor.css -->\n    <!-- bower:css -->\n    <!-- endbower -->\n    <!-- endbuild -->");

        // Regex: Vendor scripts (vendor.js)
        this.indexFile = this.indexFile.replace(/<script src="lib\/ionic\/js\/ionic.bundle.js"><\/script>/g, "<!-- build:js scripts\/vendor.js -->\n    <!-- bower:js -->\n    <!-- endbower -->\n    <!-- endbuild -->");

       // Regex: User scripts (scripts.js)
       this.indexFile = this.indexFile.replace(/<!-- your app's js -->/g,"<!-- your app's js -->\n    <!-- build:js scripts\/scripts.js -->");
       this.indexFile = this.indexFile.replace(/<\/head>/g,"  <script src=\"scripts\/configuration.js\"><\/script>\n    <!-- endbuild -->\n  <\/head>");

       // Regex/Rename: Scripts path (Ionics 'js' to 'scripts')
       this.indexFile = this.indexFile.replace(/href="css/g,"href=\"styles");

       // Regex/Rename: CSS path (Ionics 'css' to 'styles')
       this.indexFile = this.indexFile.replace(/src="js/g,"src=\"scripts");

       // Write index.html
       this.indexFile = this.indexFile.replace(/'/g, "'");
       this.write(path.join(this.appPath, 'index.html'), this.indexFile);
    },

    ensureStyles: function ensureStyles() {
      // Only create a main style file if the starter template didn't
      // have any styles. In the case it does, the starter should
      // supply both main.css and main.scss files, one of which
      // will be deleted

      var cssFile = 'main.' + (this.compass ? 'scss' : 'css');
      var unusedFile = 'main.' + (this.compass ? 'css' : 'scss');
      var stylePath = path.join(process.cwd(), 'app', 'css');
      var found = false;

      this.fs.store.each(function (file, index) {
        if (path.dirname(file.path) === stylePath) {
          var name = path.basename(file.path);

          if (name === cssFile) {
            found = true;
          } else if (name === unusedFile) {
            // BUG: The log will still report the the file was created
            this.fs.delete(file.path);
          }
        }
      }.bind(this));

      if (!found) {
        this.copy('styles/' + cssFile, 'app/css/' + cssFile);
      }

    },

    cordovaHooks: function cordovaHooks() {
      this.directory('hooks', 'hooks', true);
    },

    testFiles: function testFiles() {
      this.template('spec/controllers.js', 'test/spec/controllers.js');
    },

    packages: function () {
      this.installDependencies({ skipInstall: this.options['skip-install'] });
    }
  },

  end: {
    hookPerms: function hookPerms() {
      var iconsAndSplash = 'hooks/after_prepare/icons_and_splashscreens.js';
      fs.chmodSync(iconsAndSplash, '755');
    },
    folderNames: function folderNames() {
      // Rename: Scripts path (Ionics 'js' to 'scripts')
      fs.rename(path.join(appPath, 'css'), path.join(appPath, 'styles'), function(err) {
          if ( err ) console.log('ERROR: ' + err);
      });

      // Rename: CSS path (Ionics 'css' to 'styles')
      fs.rename(path.join(appPath, 'js'), path.join(appPath, 'scripts'), function(err) {
          if ( err ) console.log('ERROR: ' + err);
      });

      // Rename: Images path (Ionics 'img' to 'images')
      fs.rename(path.join(appPath, 'img'), path.join(appPath, 'images'), function(err) {
          if ( err ) console.log('ERROR: ' + err);
      });
    },

  }
});

<|endoftext|>


<commit_before>/*!
 * Copyright (C) 2017 Glayzzle (BSD3 License)
 * @authors https://github.com/glayzzle/php-parser/graphs/contributors
 * @url http://glayzzle.com
 */

/**
 * @private check if argument is a number
 */
function isNumber(n) {
  return n != "." && n != "," && !isNaN(parseFloat(n)) && isFinite(n);
}

/**
 * The PHP Parser class that build the AST tree from the lexer
 * @constructor {Parser}
 * @property {Lexer} lexer - current lexer instance
 * @property {AST} ast - the AST factory instance
 * @property {Integer|String} token - current token
 * @property {Boolean} extractDoc - should extract documentation as AST node
 * @property {Boolean} suppressErrors - should ignore parsing errors and continue
 * @property {Boolean} debug - should output debug informations
 */
const parser = function(lexer, ast) {
  this.lexer = lexer;
  this.ast = ast;
  this.tok = lexer.tok;
  this.EOF = lexer.EOF;
  this.token = null;
  this.prev = null;
  this.debug = false;
  this.php7 = true;
  this.extractDoc = false;
  this.suppressErrors = false;
  const mapIt = function(item) {
    return [item, null];
  };
  this.entries = {
    IDENTIFIER: new Map(
      [
        this.tok.T_ABSTRACT,
        this.tok.T_ARRAY,
        this.tok.T_AS,
        this.tok.T_BREAK,
        this.tok.T_CALLABLE,
        this.tok.T_CASE,
        this.tok.T_CATCH,
        this.tok.T_CLASS,
        this.tok.T_CLASS_C,
        this.tok.T_CLONE,
        this.tok.T_CONST,
        this.tok.T_CONTINUE,
        this.tok.T_DECLARE,
        this.tok.T_DEFAULT,
        this.tok.T_DIR,
        this.tok.T_DO,
        this.tok.T_ECHO,
        this.tok.T_ELSE,
        this.tok.T_ELSEIF,
        this.tok.T_EMPTY,
        this.tok.T_ENDDECLARE,
        this.tok.T_ENDFOR,
        this.tok.T_ENDFOREACH,
        this.tok.T_ENDIF,
        this.tok.T_ENDSWITCH,
        this.tok.T_ENDWHILE,
        this.tok.T_EVAL,
        this.tok.T_EXIT,
        this.tok.T_EXTENDS,
        this.tok.T_FILE,
        this.tok.T_FINAL,
        this.tok.T_FINALLY,
        this.tok.T_FUNC_C,
        this.tok.T_FOR,
        this.tok.T_FOREACH,
        this.tok.T_FUNCTION,
        this.tok.T_GLOBAL,
        this.tok.T_GOTO,
        this.tok.T_IF,
        this.tok.T_IMPLEMENTS,
        this.tok.T_INCLUDE,
        this.tok.T_INCLUDE_ONCE,
        this.tok.T_INSTANCEOF,
        this.tok.T_INSTEADOF,
        this.tok.T_INTERFACE,
        this.tok.T_ISSET,
        this.tok.T_LINE,
        this.tok.T_LIST,
        this.tok.T_LOGICAL_AND,
        this.tok.T_LOGICAL_OR,
        this.tok.T_LOGICAL_XOR,
        this.tok.T_METHOD_C,
        this.tok.T_NAMESPACE,
        this.tok.T_NEW,
        this.tok.T_NS_C,
        this.tok.T_PRINT,
        this.tok.T_PRIVATE,
        this.tok.T_PROTECTED,
        this.tok.T_PUBLIC,
        this.tok.T_REQUIRE,
        this.tok.T_REQUIRE_ONCE,
        this.tok.T_RETURN,
        this.tok.T_STATIC,
        this.tok.T_SWITCH,
        this.tok.T_THROW,
        this.tok.T_TRAIT,
        this.tok.T_TRY,
        this.tok.T_UNSET,
        this.tok.T_USE,
        this.tok.T_VAR,
        this.tok.T_WHILE,
        this.tok.T_YIELD
      ].map(mapIt)
    ),
    VARIABLE: new Map(
      [
        this.tok.T_VARIABLE,
        "$",
        "&",
        this.tok.T_NS_SEPARATOR,
        this.tok.T_STRING,
        this.tok.T_NAMESPACE,
        this.tok.T_STATIC
      ].map(mapIt)
    ),
    SCALAR: new Map(
      [
        this.tok.T_CONSTANT_ENCAPSED_STRING,
        this.tok.T_START_HEREDOC,
        this.tok.T_LNUMBER,
        this.tok.T_DNUMBER,
        this.tok.T_ARRAY,
        "[",
        this.tok.T_CLASS_C,
        this.tok.T_TRAIT_C,
        this.tok.T_FUNC_C,
        this.tok.T_METHOD_C,
        this.tok.T_LINE,
        this.tok.T_FILE,
        this.tok.T_DIR,
        this.tok.T_NS_C,
        '"',
        'b"',
        'B"',
        "-",
        this.tok.T_NS_SEPARATOR
      ].map(mapIt)
    ),
    T_MAGIC_CONST: new Map(
      [
        this.tok.T_CLASS_C,
        this.tok.T_TRAIT_C,
        this.tok.T_FUNC_C,
        this.tok.T_METHOD_C,
        this.tok.T_LINE,
        this.tok.T_FILE,
        this.tok.T_DIR,
        this.tok.T_NS_C
      ].map(mapIt)
    ),
    T_MEMBER_FLAGS: new Map(
      [
        this.tok.T_PUBLIC,
        this.tok.T_PRIVATE,
        this.tok.T_PROTECTED,
        this.tok.T_STATIC,
        this.tok.T_ABSTRACT,
        this.tok.T_FINAL
      ].map(mapIt)
    ),
    EOS: new Map(
      [";", this.tok.T_CLOSE_TAG, this.EOF, this.tok.T_INLINE_HTML].map(mapIt)
    ),
    EXPR: new Map(
      [
        "@",
        "-",
        "+",
        "!",
        "~",
        "(",
        "`",
        this.tok.T_LIST,
        this.tok.T_CLONE,
        this.tok.T_INC,
        this.tok.T_DEC,
        this.tok.T_NEW,
        this.tok.T_ISSET,
        this.tok.T_EMPTY,
        this.tok.T_INCLUDE,
        this.tok.T_INCLUDE_ONCE,
        this.tok.T_REQUIRE,
        this.tok.T_REQUIRE_ONCE,
        this.tok.T_EVAL,
        this.tok.T_INT_CAST,
        this.tok.T_DOUBLE_CAST,
        this.tok.T_STRING_CAST,
        this.tok.T_ARRAY_CAST,
        this.tok.T_OBJECT_CAST,
        this.tok.T_BOOL_CAST,
        this.tok.T_UNSET_CAST,
        this.tok.T_EXIT,
        this.tok.T_PRINT,
        this.tok.T_YIELD,
        this.tok.T_STATIC,
        this.tok.T_FUNCTION,
        // using VARIABLES :
        this.tok.T_VARIABLE,
        "$",
        this.tok.T_NS_SEPARATOR,
        this.tok.T_STRING,
        // using SCALAR :
        this.tok.T_STRING, // @see variable.js line 45 > conflict with variable = shift/reduce :)
        this.tok.T_CONSTANT_ENCAPSED_STRING,
        this.tok.T_START_HEREDOC,
        this.tok.T_LNUMBER,
        this.tok.T_DNUMBER,
        this.tok.T_ARRAY,
        "[",
        this.tok.T_CLASS_C,
        this.tok.T_TRAIT_C,
        this.tok.T_FUNC_C,
        this.tok.T_METHOD_C,
        this.tok.T_LINE,
        this.tok.T_FILE,
        this.tok.T_DIR,
        this.tok.T_NS_C
      ].map(mapIt)
    )
  };
};

/**
 * helper : gets a token name
 */
parser.prototype.getTokenName = function(token) {
  if (!isNumber(token)) {
    return "'" + token + "'";
  } else {
    if (token == this.EOF) return "the end of file (EOF)";
    return this.lexer.engine.tokens.values[token];
  }
};

/**
 * main entry point : converts a source code to AST
 */
parser.prototype.parse = function(code, filename) {
  this._errors = [];
  this.filename = filename || "eval";
  this.currentNamespace = [""];
  if (this.extractDoc) {
    this._docs = [];
  } else {
    this._docs = null;
  }
  this.lexer.setInput(code);
  this.lexer.comment_tokens = this.extractDoc;
  this.length = this.lexer._input.length;
  this.innerList = false;
  const program = this.ast.prepare("program", this);
  let childs = [];
  this.next();
  while (this.token != this.EOF) {
    const node = this.read_start();
    if (node !== null && node !== undefined) {
      if (Array.isArray(node)) {
        childs = childs.concat(node);
      } else {
        childs.push(node);
      }
    }
  }
  return program(childs, this._errors, this._docs);
};

/**
 * Raise an error
 */
parser.prototype.raiseError = function(message, msgExpect, expect, token) {
  message += " on line " + this.lexer.yylloc.first_line;
  if (!this.suppressErrors) {
    const err = new SyntaxError(
      message,
      this.filename,
      this.lexer.yylloc.first_line
    );
    err.lineNumber = this.lexer.yylloc.first_line;
    err.fileName = this.filename;
    err.columnNumber = this.lexer.yylloc.first_column;
    throw err;
  }
  // Error node :
  const node = this.ast.prepare("error", this)(
    message,
    token,
    this.lexer.yylloc.first_line,
    expect
  );
  this._errors.push(node);
  return node;
};

/**
 * handling errors
 */
parser.prototype.error = function(expect) {
  let msg = "Parse Error : syntax error";
  let token = this.getTokenName(this.token);
  let msgExpect = "";

  if (this.token !== this.EOF) {
    if (isNumber(this.token)) {
      let symbol = this.text();
      if (symbol.length > 10) {
        symbol = symbol.substring(0, 7) + "...";
      }
      token = "'" + symbol + "' (" + token + ")";
    }
    msg += ", unexpected " + token;
  }
  if (expect && !Array.isArray(expect)) {
    if (isNumber(expect) || expect.length === 1) {
      msgExpect = ", expecting " + this.getTokenName(expect);
    }
    msg += msgExpect;
  }
  return this.raiseError(msg, msgExpect, expect, token);
};

/**
 * Creates a new AST node
 */
parser.prototype.node = function(name) {
  return this.ast.prepare(name, this);
};

/**
 * expects an end of statement or end of file
 * @return {boolean}
 */
parser.prototype.expectEndOfStatement = function() {
  if (this.token === ";") {
    this.next();
    if (this.token === this.tok.T_CLOSE_TAG) {
      // strip close tag (statement already closed with ';')
      this.next();
    }
  } else if (this.token === this.tok.T_CLOSE_TAG) {
    this.next();
  } else if (this.token !== this.tok.T_INLINE_HTML && this.token !== this.EOF) {
    this.error(";");
    return false;
  }
  return true;
};

/** outputs some debug information on current token **/
const ignoreStack = [
  "parser.next"
];
parser.prototype.showlog = function() {
  const stack = new Error().stack.split("\n");
  let line;
  for (let offset = 2; offset < stack.length; offset++) {
    line = stack[offset].trim();
    let found = false;
    for (let i = 0; i < ignoreStack.length; i++) {
      if (line.substring(3, 3 + ignoreStack[i].length) === ignoreStack[i]) {
        found = true;
        break;
      }
    }
    if (!found) {
      break;
    }
  }
  // eslint-disable-next-line no-console
  console.log(
    "Line " +
      this.lexer.yylloc.first_line +
      " : " +
      this.getTokenName(this.token) +
      ">" +
      this.lexer.yytext +
      "<" +
      " @-->" +
      line
  );
  return this;
};

/**
 * Force the parser to check the current token.
 *
 * If the current token does not match to expected token,
 * the an error will be raised.
 *
 * If the suppressError mode is activated, then the error will
 * be added to the program error stack and this function will return `false`.
 *
 * @param {String|Number} token
 * @return {boolean}
 * @throws Error
 */
parser.prototype.expect = function(token) {
  if (Array.isArray(token)) {
    if (token.indexOf(this.token) === -1) {
      this.error(token);
      return false;
    }
  } else if (this.token != token) {
    this.error(token);
    return false;
  }
  return true;
};

/**
 * Returns the current token contents
 * @return {String}
 */
parser.prototype.text = function() {
  return this.lexer.yytext;
};

/** consume the next token **/
parser.prototype.next = function() {

  // prepare the back command
  this.prev = [
    this.lexer.yylloc.first_line,
    this.lexer.yylloc.first_column,
    this.lexer.offset
  ];

  // eating the token
  this.token = this.lexer.lex() || this.EOF;

  // showing the debug
  if (this.debug) {
    this.showlog();
  }

  // handling comments
  if (this.extractDoc) {
    while (
      this.token === this.tok.T_COMMENT ||
      this.token === this.tok.T_DOC_COMMENT
    ) {
      // APPEND COMMENTS
      if (this.token === this.tok.T_COMMENT) {
        this._docs.push(this.read_comment());
      } else {
        this._docs.push(this.read_doc_comment());
      }
      // eating the token
      this.token = this.lexer.lex() || this.EOF;
    }
  }

  return this;
};

/**
 * Check if token is of specified type
 */
parser.prototype.is = function(type) {
  if (Array.isArray(type)) {
    return type.indexOf(this.token) !== -1;
  }
  return this.entries[type].has(this.token);
};

// extends the parser with syntax files
[
  require("./parser/array.js"),
  require("./parser/class.js"),
  require("./parser/comment.js"),
  require("./parser/expr.js"),
  require("./parser/function.js"),
  require("./parser/if.js"),
  require("./parser/loops.js"),
  require("./parser/main.js"),
  require("./parser/namespace.js"),
  require("./parser/scalar.js"),
  require("./parser/statement.js"),
  require("./parser/switch.js"),
  require("./parser/try.js"),
  require("./parser/utils.js"),
  require("./parser/variable.js")
].forEach(function(ext) {
  for (const k in ext) {
    parser.prototype[k] = ext[k];
  }
});

module.exports = parser;
<commit_msg>fix location on comment nodes<commit_after>/*!
 * Copyright (C) 2017 Glayzzle (BSD3 License)
 * @authors https://github.com/glayzzle/php-parser/graphs/contributors
 * @url http://glayzzle.com
 */

/**
 * @private check if argument is a number
 */
function isNumber(n) {
  return n != "." && n != "," && !isNaN(parseFloat(n)) && isFinite(n);
}

/**
 * The PHP Parser class that build the AST tree from the lexer
 * @constructor {Parser}
 * @property {Lexer} lexer - current lexer instance
 * @property {AST} ast - the AST factory instance
 * @property {Integer|String} token - current token
 * @property {Boolean} extractDoc - should extract documentation as AST node
 * @property {Boolean} suppressErrors - should ignore parsing errors and continue
 * @property {Boolean} debug - should output debug informations
 */
const parser = function(lexer, ast) {
  this.lexer = lexer;
  this.ast = ast;
  this.tok = lexer.tok;
  this.EOF = lexer.EOF;
  this.token = null;
  this.prev = null;
  this.debug = false;
  this.php7 = true;
  this.extractDoc = false;
  this.suppressErrors = false;
  const mapIt = function(item) {
    return [item, null];
  };
  this.entries = {
    IDENTIFIER: new Map(
      [
        this.tok.T_ABSTRACT,
        this.tok.T_ARRAY,
        this.tok.T_AS,
        this.tok.T_BREAK,
        this.tok.T_CALLABLE,
        this.tok.T_CASE,
        this.tok.T_CATCH,
        this.tok.T_CLASS,
        this.tok.T_CLASS_C,
        this.tok.T_CLONE,
        this.tok.T_CONST,
        this.tok.T_CONTINUE,
        this.tok.T_DECLARE,
        this.tok.T_DEFAULT,
        this.tok.T_DIR,
        this.tok.T_DO,
        this.tok.T_ECHO,
        this.tok.T_ELSE,
        this.tok.T_ELSEIF,
        this.tok.T_EMPTY,
        this.tok.T_ENDDECLARE,
        this.tok.T_ENDFOR,
        this.tok.T_ENDFOREACH,
        this.tok.T_ENDIF,
        this.tok.T_ENDSWITCH,
        this.tok.T_ENDWHILE,
        this.tok.T_EVAL,
        this.tok.T_EXIT,
        this.tok.T_EXTENDS,
        this.tok.T_FILE,
        this.tok.T_FINAL,
        this.tok.T_FINALLY,
        this.tok.T_FUNC_C,
        this.tok.T_FOR,
        this.tok.T_FOREACH,
        this.tok.T_FUNCTION,
        this.tok.T_GLOBAL,
        this.tok.T_GOTO,
        this.tok.T_IF,
        this.tok.T_IMPLEMENTS,
        this.tok.T_INCLUDE,
        this.tok.T_INCLUDE_ONCE,
        this.tok.T_INSTANCEOF,
        this.tok.T_INSTEADOF,
        this.tok.T_INTERFACE,
        this.tok.T_ISSET,
        this.tok.T_LINE,
        this.tok.T_LIST,
        this.tok.T_LOGICAL_AND,
        this.tok.T_LOGICAL_OR,
        this.tok.T_LOGICAL_XOR,
        this.tok.T_METHOD_C,
        this.tok.T_NAMESPACE,
        this.tok.T_NEW,
        this.tok.T_NS_C,
        this.tok.T_PRINT,
        this.tok.T_PRIVATE,
        this.tok.T_PROTECTED,
        this.tok.T_PUBLIC,
        this.tok.T_REQUIRE,
        this.tok.T_REQUIRE_ONCE,
        this.tok.T_RETURN,
        this.tok.T_STATIC,
        this.tok.T_SWITCH,
        this.tok.T_THROW,
        this.tok.T_TRAIT,
        this.tok.T_TRY,
        this.tok.T_UNSET,
        this.tok.T_USE,
        this.tok.T_VAR,
        this.tok.T_WHILE,
        this.tok.T_YIELD
      ].map(mapIt)
    ),
    VARIABLE: new Map(
      [
        this.tok.T_VARIABLE,
        "$",
        "&",
        this.tok.T_NS_SEPARATOR,
        this.tok.T_STRING,
        this.tok.T_NAMESPACE,
        this.tok.T_STATIC
      ].map(mapIt)
    ),
    SCALAR: new Map(
      [
        this.tok.T_CONSTANT_ENCAPSED_STRING,
        this.tok.T_START_HEREDOC,
        this.tok.T_LNUMBER,
        this.tok.T_DNUMBER,
        this.tok.T_ARRAY,
        "[",
        this.tok.T_CLASS_C,
        this.tok.T_TRAIT_C,
        this.tok.T_FUNC_C,
        this.tok.T_METHOD_C,
        this.tok.T_LINE,
        this.tok.T_FILE,
        this.tok.T_DIR,
        this.tok.T_NS_C,
        '"',
        'b"',
        'B"',
        "-",
        this.tok.T_NS_SEPARATOR
      ].map(mapIt)
    ),
    T_MAGIC_CONST: new Map(
      [
        this.tok.T_CLASS_C,
        this.tok.T_TRAIT_C,
        this.tok.T_FUNC_C,
        this.tok.T_METHOD_C,
        this.tok.T_LINE,
        this.tok.T_FILE,
        this.tok.T_DIR,
        this.tok.T_NS_C
      ].map(mapIt)
    ),
    T_MEMBER_FLAGS: new Map(
      [
        this.tok.T_PUBLIC,
        this.tok.T_PRIVATE,
        this.tok.T_PROTECTED,
        this.tok.T_STATIC,
        this.tok.T_ABSTRACT,
        this.tok.T_FINAL
      ].map(mapIt)
    ),
    EOS: new Map(
      [";", this.tok.T_CLOSE_TAG, this.EOF, this.tok.T_INLINE_HTML].map(mapIt)
    ),
    EXPR: new Map(
      [
        "@",
        "-",
        "+",
        "!",
        "~",
        "(",
        "`",
        this.tok.T_LIST,
        this.tok.T_CLONE,
        this.tok.T_INC,
        this.tok.T_DEC,
        this.tok.T_NEW,
        this.tok.T_ISSET,
        this.tok.T_EMPTY,
        this.tok.T_INCLUDE,
        this.tok.T_INCLUDE_ONCE,
        this.tok.T_REQUIRE,
        this.tok.T_REQUIRE_ONCE,
        this.tok.T_EVAL,
        this.tok.T_INT_CAST,
        this.tok.T_DOUBLE_CAST,
        this.tok.T_STRING_CAST,
        this.tok.T_ARRAY_CAST,
        this.tok.T_OBJECT_CAST,
        this.tok.T_BOOL_CAST,
        this.tok.T_UNSET_CAST,
        this.tok.T_EXIT,
        this.tok.T_PRINT,
        this.tok.T_YIELD,
        this.tok.T_STATIC,
        this.tok.T_FUNCTION,
        // using VARIABLES :
        this.tok.T_VARIABLE,
        "$",
        this.tok.T_NS_SEPARATOR,
        this.tok.T_STRING,
        // using SCALAR :
        this.tok.T_STRING, // @see variable.js line 45 > conflict with variable = shift/reduce :)
        this.tok.T_CONSTANT_ENCAPSED_STRING,
        this.tok.T_START_HEREDOC,
        this.tok.T_LNUMBER,
        this.tok.T_DNUMBER,
        this.tok.T_ARRAY,
        "[",
        this.tok.T_CLASS_C,
        this.tok.T_TRAIT_C,
        this.tok.T_FUNC_C,
        this.tok.T_METHOD_C,
        this.tok.T_LINE,
        this.tok.T_FILE,
        this.tok.T_DIR,
        this.tok.T_NS_C
      ].map(mapIt)
    )
  };
};

/**
 * helper : gets a token name
 */
parser.prototype.getTokenName = function(token) {
  if (!isNumber(token)) {
    return "'" + token + "'";
  } else {
    if (token == this.EOF) return "the end of file (EOF)";
    return this.lexer.engine.tokens.values[token];
  }
};

/**
 * main entry point : converts a source code to AST
 */
parser.prototype.parse = function(code, filename) {
  this._errors = [];
  this.filename = filename || "eval";
  this.currentNamespace = [""];
  if (this.extractDoc) {
    this._docs = [];
  } else {
    this._docs = null;
  }
  this.lexer.setInput(code);
  this.lexer.comment_tokens = this.extractDoc;
  this.length = this.lexer._input.length;
  this.innerList = false;
  const program = this.ast.prepare("program", this);
  let childs = [];
  this.next();
  while (this.token != this.EOF) {
    const node = this.read_start();
    if (node !== null && node !== undefined) {
      if (Array.isArray(node)) {
        childs = childs.concat(node);
      } else {
        childs.push(node);
      }
    }
  }
  return program(childs, this._errors, this._docs);
};

/**
 * Raise an error
 */
parser.prototype.raiseError = function(message, msgExpect, expect, token) {
  message += " on line " + this.lexer.yylloc.first_line;
  if (!this.suppressErrors) {
    const err = new SyntaxError(
      message,
      this.filename,
      this.lexer.yylloc.first_line
    );
    err.lineNumber = this.lexer.yylloc.first_line;
    err.fileName = this.filename;
    err.columnNumber = this.lexer.yylloc.first_column;
    throw err;
  }
  // Error node :
  const node = this.ast.prepare("error", this)(
    message,
    token,
    this.lexer.yylloc.first_line,
    expect
  );
  this._errors.push(node);
  return node;
};

/**
 * handling errors
 */
parser.prototype.error = function(expect) {
  let msg = "Parse Error : syntax error";
  let token = this.getTokenName(this.token);
  let msgExpect = "";

  if (this.token !== this.EOF) {
    if (isNumber(this.token)) {
      let symbol = this.text();
      if (symbol.length > 10) {
        symbol = symbol.substring(0, 7) + "...";
      }
      token = "'" + symbol + "' (" + token + ")";
    }
    msg += ", unexpected " + token;
  }
  if (expect && !Array.isArray(expect)) {
    if (isNumber(expect) || expect.length === 1) {
      msgExpect = ", expecting " + this.getTokenName(expect);
    }
    msg += msgExpect;
  }
  return this.raiseError(msg, msgExpect, expect, token);
};

/**
 * Creates a new AST node
 */
parser.prototype.node = function(name) {
  return this.ast.prepare(name, this);
};

/**
 * expects an end of statement or end of file
 * @return {boolean}
 */
parser.prototype.expectEndOfStatement = function() {
  if (this.token === ";") {
    this.next();
    if (this.token === this.tok.T_CLOSE_TAG) {
      // strip close tag (statement already closed with ';')
      this.next();
    }
  } else if (this.token === this.tok.T_CLOSE_TAG) {
    this.next();
  } else if (this.token !== this.tok.T_INLINE_HTML && this.token !== this.EOF) {
    this.error(";");
    return false;
  }
  return true;
};

/** outputs some debug information on current token **/
const ignoreStack = [
  "parser.next"
];
parser.prototype.showlog = function() {
  const stack = new Error().stack.split("\n");
  let line;
  for (let offset = 2; offset < stack.length; offset++) {
    line = stack[offset].trim();
    let found = false;
    for (let i = 0; i < ignoreStack.length; i++) {
      if (line.substring(3, 3 + ignoreStack[i].length) === ignoreStack[i]) {
        found = true;
        break;
      }
    }
    if (!found) {
      break;
    }
  }
  // eslint-disable-next-line no-console
  console.log(
    "Line " +
      this.lexer.yylloc.first_line +
      " : " +
      this.getTokenName(this.token) +
      ">" +
      this.lexer.yytext +
      "<" +
      " @-->" +
      line
  );
  return this;
};

/**
 * Force the parser to check the current token.
 *
 * If the current token does not match to expected token,
 * the an error will be raised.
 *
 * If the suppressError mode is activated, then the error will
 * be added to the program error stack and this function will return `false`.
 *
 * @param {String|Number} token
 * @return {boolean}
 * @throws Error
 */
parser.prototype.expect = function(token) {
  if (Array.isArray(token)) {
    if (token.indexOf(this.token) === -1) {
      this.error(token);
      return false;
    }
  } else if (this.token != token) {
    this.error(token);
    return false;
  }
  return true;
};

/**
 * Returns the current token contents
 * @return {String}
 */
parser.prototype.text = function() {
  return this.lexer.yytext;
};

/** consume the next token **/
parser.prototype.next = function() {

  // prepare the back command
  this.prev = [
    this.lexer.yylloc.first_line,
    this.lexer.yylloc.first_column,
    this.lexer.offset
  ];

  // eating the token
  this.token = this.lexer.lex() || this.EOF;

  // showing the debug
  if (this.debug) {
    this.showlog();
  }

  // handling comments
  if (this.extractDoc) {
    while (
      this.token === this.tok.T_COMMENT ||
      this.token === this.tok.T_DOC_COMMENT
    ) {
      // APPEND COMMENTS
      if (this.token === this.tok.T_COMMENT) {
        this._docs.push(this.read_comment());
      } else {
        this._docs.push(this.read_doc_comment());
      }
    }
  }

  return this;
};

/**
 * Check if token is of specified type
 */
parser.prototype.is = function(type) {
  if (Array.isArray(type)) {
    return type.indexOf(this.token) !== -1;
  }
  return this.entries[type].has(this.token);
};

// extends the parser with syntax files
[
  require("./parser/array.js"),
  require("./parser/class.js"),
  require("./parser/comment.js"),
  require("./parser/expr.js"),
  require("./parser/function.js"),
  require("./parser/if.js"),
  require("./parser/loops.js"),
  require("./parser/main.js"),
  require("./parser/namespace.js"),
  require("./parser/scalar.js"),
  require("./parser/statement.js"),
  require("./parser/switch.js"),
  require("./parser/try.js"),
  require("./parser/utils.js"),
  require("./parser/variable.js")
].forEach(function(ext) {
  for (const k in ext) {
    parser.prototype[k] = ext[k];
  }
});

module.exports = parser;
<|endoftext|>