var PHPCI = { intervals: {}, init: function () { // Setup the date locale moment.locale(PHPCI_LANGUAGE); $(document).ready(function () { // Format datetimes $('time[datetime]').each(function() { var thisDate = $(this).attr('datetime'); var formattedDate = moment(thisDate).format($(this).data('format') || 'lll'); $(this).text(formattedDate); }); // Update latest builds every 5 seconds: PHPCI.getBuilds(); PHPCI.intervals.getBuilds = setInterval(PHPCI.getBuilds, 5000); // Update latest project builds every 10 seconds: if (typeof PHPCI_PROJECT_ID != 'undefined') { PHPCI.intervals.getProjectBuilds = setInterval(PHPCI.getProjectBuilds, 10000); } PHPCI.uiUpdated(); // Set menu is active $('a[href="'+document.URL+'"]').parent().addClass('active'); $('a[href="'+document.URL+'"]').parent().parent().addClass('menu-open'); $('a[href="'+document.URL+'"]').parent().parent().parent().addClass('active'); }); $(window).on('builds-updated', function (e, data) { PHPCI.updateHeaderBuilds(data); }); }, getBuilds: function () { $.ajax({ url: PHPCI_URL + 'build/latest', success: function (data) { $(window).trigger('builds-updated', [data]); }, error: PHPCI.handleFailedAjax }); }, getProjectBuilds: function () { $.ajax({ url: PHPCI_URL + 'project/builds/' + PHPCI_PROJECT_ID + '?branch=' + PHPCI_PROJECT_BRANCH, success: function (data) { $('#latest-builds').html(data); }, error: PHPCI.handleFailedAjax }); }, updateHeaderBuilds: function (data) { $('.phpci-pending-list').empty(); $('.phpci-running-list').empty(); if (!data.pending.count) { $('.phpci-pending').hide(); } else { $('.phpci-pending').show(); $('.phpci-pending .header').text(Lang.get('n_builds_pending', data.pending.count)); $.each(data.pending.items, function (idx, build) { $('.phpci-pending-list').append(build.header_row); }); } if (!data.running.count) { $('.phpci-running').hide(); } else { $('.phpci-running').show(); $('.phpci-running .header').text(Lang.get('n_builds_running', data.running.count)); $.each(data.running.items, function (idx, build) { $('.phpci-running-list').append(build.header_row); }); } }, get: function (uri, success) { $.ajax({ url: window.PHPCI_URL + uri, success: function (data) { success(); PHPCI.uiUpdated(); }, error: PHPCI.handleFailedAjax }); }, handleFailedAjax: function (xhr) { if (xhr.status == 401) { window.location.href = window.PHPCI_URL + 'session/login'; } }, uiUpdated: function () { $('.duration').each(function () { var seconds = $(this).data('duration'); if (seconds == 0) { return; } $(this).text(moment.duration(seconds, 'seconds').humanize()); }); $('.datetime').each(function () { var dateString = $(this).data('date'); if (!dateString) { return; } $(this).text(moment(dateString).format('lll')); }); } }; PHPCI.init(); function handleFailedAjax(xhr) { PHPCI.handleFailedAjax(xhr); } /** * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind * for the details of code below */ if (!Function.prototype.bind) { Function.prototype.bind = function (oThis) { if (typeof this !== "function") { // closest thing possible to the ECMAScript 5 internal IsCallable function throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); } var aArgs = Array.prototype.slice.call(arguments, 1), fToBind = this, fNOP = function () { }, fBound = function () { return fToBind.apply(this instanceof fNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments))); }; fNOP.prototype = this.prototype; fBound.prototype = new fNOP(); return fBound; }; } /** * Used for delete buttons in the system, just to prevent accidental clicks. */ function confirmDelete(url, subject, reloadAfter) { var dialog = new PHPCIConfirmDialog({ message: subject + ' will be permanently deleted. Are you sure?', confirmBtnCaption: 'Delete', /* confirm-btn click handler */ confirmed: function (e) { var dialog = this; e.preventDefault(); /* Call delete URL */ $.ajax({ url: url, success: function (data) { if (reloadAfter) { dialog.onClose = function () { window.location.reload(); }; } dialog.showStatusMessage('Successfully deleted!', 1000); }, error: function (data) { dialog.showStatusMessage('Deletion failed! Server says "' + data.statusText + '"'); if (data.status == 401) { handleFailedAjax(data); } } }); } }); dialog.show(); return dialog; } /** * PHPCIConfirmDialog constructor options object * @type {{message: string, title: string, confirmBtnCaption: string, cancelBtnCaption: string, confirmed: Function}} */ var PHPCIConfirmDialogOptions = { message: 'The action will be performed and cannot be undone. Are you sure?', title: 'Confirmation Dialog', confirmBtnCaption: 'Ok', cancelBtnCaption: 'Cancel', confirmed: function (e) { this.close(); } }; var PHPCIConfirmDialog = Class.extend({ /** * @private * @var {bool} Determines whether the dialog has been confirmed */ confirmed: false, /** * @param {PHPCIConfirmDialogOptions} options */ init: function (options) { options = options ? $.extend(PHPCIConfirmDialogOptions, options) : PHPCIConfirmDialogOptions; if (!$('#confirm-dialog').length) { /* Add the dialog html to a page on first use. No need to have it there before first use. */ $('body').append( '' ); } /* Define dialog controls */ this.$dialog = $('#confirm-dialog'); this.$cancelBtn = this.$dialog.find('div.modal-footer button.btn-default'); this.$confirmBtn = this.$dialog.find('div.modal-footer button.btn-primary'); this.$title = this.$dialog.find('h4.modal-title'); this.$body = this.$dialog.find('div.modal-body'); /* Initialize its values */ this.$title.html(options.title ? options.title : PHPCIConfirmDialogOptions.title); this.$body.html(options.message ? options.message : PHPCIConfirmDialogOptions.message); this.$confirmBtn.html( options.confirmBtnCaption ? options.confirmBtnCaption : PHPCIConfirmDialogOptions.confirmBtnCaption ); this.$cancelBtn.html( options.cancelBtnCaption ? options.cancelBtnCaption : PHPCIConfirmDialogOptions.cancelBtnCaption ); /* Events */ this.confirmBtnClick = options.confirmed; /* Re-bind handlers */ this.$confirmBtn.unbind('click'); this.$confirmBtn.click(this.onConfirm.bind(this)); this.$confirmBtn.unbind('hidden.bs.modal'); /* Bind the close event of the dialog to the set of onClose* methods */ this.$dialog.on('hidden.bs.modal', function () {this.onClose()}.bind(this)); this.$dialog.on('hidden.bs.modal', function () { if (this.confirmed) { this.onCloseConfirmed(); } else { this.onCloseCanceled(); } }.bind(this)); /* Restore state if was changed previously */ this.$cancelBtn.show(); this.$confirmBtn.show(); this.confirmed = false; }, /** * Show dialog */ show: function () { this.$dialog.modal('show'); }, /** * Hide dialog */ close: function () { this.$dialog.modal('hide'); }, onConfirm: function (e) { this.confirmed = true; $(this).attr('disabled', 'disabled'); this.confirmBtnClick(e); }, /** * Called only when confirmed dialog was closed */ onCloseConfirmed: function () {}, /** * Called only when canceled dialog was closed */ onCloseCanceled: function () {}, /** * Called always when the dialog was closed */ onClose: function () {}, showStatusMessage: function (message, closeTimeout) { this.$confirmBtn.hide(); this.$cancelBtn.html('Close'); /* Status message */ this.$body.html(message); if (closeTimeout) { window.setTimeout(function () { /* Hide the dialog */ this.close(); }.bind(this), closeTimeout); } } }); /** * Used to initialise the project form: */ function setupProjectForm() { $('.github-container').hide(); $('#element-reference').change(function() { var el = $(this); var val = el.val(); var type = $('#element-type').val(); var acceptable = { 'github': { 'ssh': /git\@github\.com\:([a-zA-Z0-9_\-]+\/[a-zA-Z0-9_\-]+)\.git/, 'git': /git\:\/\/github.com\/([a-zA-Z0-9_\-]+\/[a-zA-Z0-9_\-]+)\.git/, 'http': /https\:\/\/github\.com\/([a-zA-Z0-9_\-]+\/[a-zA-Z0-9_\-]+)(\.git)?/ }, 'bitbucket': { 'ssh': /git\@bitbucket\.org\:([a-zA-Z0-9_\-]+\/[a-zA-Z0-9_\-]+)\.git/, 'http': /https\:\/\/[a-zA-Z0-9_\-]+\@bitbucket.org\/([a-zA-Z0-9_\-]+\/[a-zA-Z0-9_\-]+)\.git/, 'anon': /https\:\/\/bitbucket.org\/([a-zA-Z0-9_\-]+\/[a-zA-Z0-9_\-]+)(\.git)?/ } }; if( acceptable[type] !== undefined ) { for(var i in acceptable[type]) { if(val.match(acceptable[type][i])) { el.val(val.replace(acceptable[type][i], '$1')); } } } }); $('#element-type').change(function() { if ($(this).val() == 'github') { $('#loading').show(); $.ajax({ dataType: "json", url: window.PHPCI_URL + 'project/github-repositories', success: function (data) { $('#loading').hide(); if (data && data.repos) { $('#element-github').empty(); for (var i in data.repos) { var name = data.repos[i]; $('#element-github').append($('').text(name).val(name)); } $('.github-container').slideDown(); } }, error: handleFailedAjax }); } else { $('.github-container').slideUp(); } $('#element-reference').trigger('change'); }); $('#element-github').change(function() { var val = $('#element-github').val(); if(val != 'choose') { $('#element-type').val('github'); $('#element-reference').val(val); $('label[for=element-reference]').hide(); $('label[for=element-type]').hide(); $('#element-reference').hide(); $('#element-type').hide(); $('#element-token').val(window.github_token); $('#element-title').val(val); } else { $('label[for=element-reference]').show(); $('label[for=element-type]').show(); $('#element-reference').show(); $('#element-type').show(); $('#element-reference').val(''); $('#element-token').val(''); } }); } var Lang = { get: function () { var args = Array.prototype.slice.call(arguments);; var string = args.shift(); if (PHPCI_STRINGS[string]) { args.unshift(PHPCI_STRINGS[string]); return sprintf.apply(sprintf[0], args); } return 'MISSING: ' + string; } }; moment.locale(PHPCI_LANGUAGE);