2013-12-10 06:38:05 +00:00
var TEAM _PATTERN = '^[a-zA-Z][a-zA-Z0-9]+$' ;
var ROBOT _PATTERN = '^[a-zA-Z][a-zA-Z0-9]+$' ;
2013-12-26 22:45:16 +00:00
function getRestUrl ( args ) {
var url = '' ;
for ( var i = 0 ; i < arguments . length ; ++ i ) {
if ( i > 0 ) {
url += '/' ;
}
url += encodeURI ( arguments [ i ] )
}
return url ;
}
2014-01-10 01:17:28 +00:00
function clickElement ( el ) {
// From: http://stackoverflow.com/questions/16802795/click-not-working-in-mocha-phantomjs-on-certain-elements
var ev = document . createEvent ( "MouseEvent" ) ;
ev . initMouseEvent (
"click" ,
true /* bubble */ , true /* cancelable */ ,
window , null ,
0 , 0 , 0 , 0 , /* coordinates */
false , false , false , false , /* modifier keys */
0 /*left*/ , null ) ;
el . dispatchEvent ( ev ) ;
}
2013-12-26 22:45:16 +00:00
2013-11-05 00:36:56 +00:00
function getFirstTextLine ( commentString ) {
if ( ! commentString ) { return '' ; }
var lines = commentString . split ( '\n' ) ;
var MARKDOWN _CHARS = {
'#' : true ,
'-' : true ,
'>' : true ,
'`' : true
} ;
for ( var i = 0 ; i < lines . length ; ++ i ) {
// Skip code lines.
if ( lines [ i ] . indexOf ( ' ' ) == 0 ) {
continue ;
}
// Skip empty lines.
if ( $ . trim ( lines [ i ] ) . length == 0 ) {
continue ;
}
// Skip control lines.
if ( MARKDOWN _CHARS [ $ . trim ( lines [ i ] ) [ 0 ] ] ) {
continue ;
}
return getMarkedDown ( lines [ i ] ) ;
}
return '' ;
}
2013-12-26 22:45:16 +00:00
function createRobotAccount ( ApiService , is _org , orgname , name , callback ) {
ApiService . createRobot ( is _org ? orgname : null , null , { 'robot_shortname' : name } ) . then ( callback , function ( resp ) {
2013-12-10 06:38:05 +00:00
bootbox . dialog ( {
"message" : resp . data ? resp . data : 'The robot account could not be created' ,
"title" : "Cannot create robot account" ,
"buttons" : {
"close" : {
"label" : "Close" ,
"className" : "btn-primary"
}
}
} ) ;
} ) ;
}
2013-12-26 22:45:16 +00:00
function createOrganizationTeam ( ApiService , orgname , teamname , callback ) {
2013-12-10 06:38:05 +00:00
var data = {
'name' : teamname ,
'role' : 'member'
} ;
2013-12-26 22:45:16 +00:00
var params = {
'orgname' : orgname ,
'teamname' : teamname
} ;
2013-12-10 06:38:05 +00:00
2013-12-26 22:45:16 +00:00
ApiService . updateOrganizationTeam ( data , params ) . then ( callback , function ( ) {
2013-12-10 06:38:05 +00:00
bootbox . dialog ( {
"message" : resp . data ? resp . data : 'The team could not be created' ,
"title" : "Cannot create team" ,
"buttons" : {
"close" : {
"label" : "Close" ,
"className" : "btn-primary"
}
}
} ) ;
} ) ;
}
2013-11-05 00:36:56 +00:00
function getMarkedDown ( string ) {
return Markdown . getSanitizingConverter ( ) . makeHtml ( string || '' ) ;
}
2014-03-12 04:49:46 +00:00
quayApp = angular . module ( 'quay' , [ 'ngRoute' , 'chieffancypants.loadingBar' , 'angular-tour' , 'restangular' , 'angularMoment' , 'angulartics' , /*'angulartics.google.analytics',*/ 'angulartics.mixpanel' , 'mgcrea.ngStrap' , 'ngCookies' , 'ngSanitize' , 'angular-md5' , 'pasvaz.bindonce' , 'ansiToHtml' , 'ngAnimate' ] , function ( $provide , cfpLoadingBarProvider ) {
2013-12-17 18:19:59 +00:00
cfpLoadingBarProvider . includeSpinner = false ;
2013-12-26 22:45:16 +00:00
2014-03-18 19:08:46 +00:00
/ * *
* Specialized wrapper around array which provides a toggle ( ) method for viewing the contents of the
* array in a manner that is asynchronously filled in over a short time period . This prevents long
* pauses in the UI for ngRepeat ' s when the array is significant in size .
* /
$provide . factory ( 'AngularViewArray' , [ '$interval' , function ( $interval ) {
var ADDTIONAL _COUNT = 50 ;
function _ViewArray ( ) {
this . isVisible = false ;
this . visibleEntries = null ;
this . hasEntries = false ;
this . entries = [ ] ;
this . timerRef _ = null ;
this . currentIndex _ = 0 ;
}
_ViewArray . prototype . push = function ( elem ) {
this . entries . push ( elem ) ;
this . hasEntries = true ;
if ( this . isVisible ) {
this . setVisible ( true ) ;
}
} ;
_ViewArray . prototype . toggle = function ( ) {
this . setVisible ( ! this . isVisible ) ;
} ;
_ViewArray . prototype . setVisible = function ( newState ) {
this . isVisible = newState ;
this . visibleEntries = [ ] ;
this . currentIndex _ = 0 ;
if ( newState ) {
this . showAdditionalEntries _ ( ) ;
this . startTimer _ ( ) ;
} else {
this . stopTimer _ ( ) ;
}
} ;
_ViewArray . prototype . showAdditionalEntries _ = function ( ) {
var i = 0 ;
for ( i = this . currentIndex _ ; i < ( this . currentIndex _ + ADDTIONAL _COUNT ) && i < this . entries . length ; ++ i ) {
this . visibleEntries . push ( this . entries [ i ] ) ;
}
this . currentIndex _ = i ;
if ( this . currentIndex _ >= this . entries . length ) {
this . stopTimer _ ( ) ;
}
} ;
_ViewArray . prototype . startTimer _ = function ( ) {
var that = this ;
this . timerRef _ = $interval ( function ( ) {
that . showAdditionalEntries _ ( ) ;
} , 10 ) ;
} ;
_ViewArray . prototype . stopTimer _ = function ( ) {
if ( this . timerRef _ ) {
$interval . cancel ( this . timerRef _ ) ;
this . timerRef _ = null ;
}
} ;
var service = {
'create' : function ( ) {
return new _ViewArray ( ) ;
}
} ;
return service ;
} ] ) ;
2014-02-25 23:22:55 +00:00
2014-04-01 04:23:53 +00:00
$provide . factory ( 'DataFileService' , [ function ( ) {
var dataFileService = { } ;
dataFileService . getName _ = function ( filePath ) {
var parts = filePath . split ( '/' ) ;
return parts [ parts . length - 1 ] ;
} ;
dataFileService . tryAsZip _ = function ( buf , success , failure ) {
var zip = null ;
var zipFiles = null ;
try {
var zip = new JSZip ( buf ) ;
zipFiles = zip . files ;
} catch ( e ) {
failure ( ) ;
return ;
}
var files = [ ] ;
for ( var filePath in zipFiles ) {
if ( zipFiles . hasOwnProperty ( filePath ) ) {
files . push ( {
'name' : dataFileService . getName _ ( filePath ) ,
'path' : filePath ,
'canRead' : true ,
'toBlob' : ( function ( fp ) {
return function ( ) {
return new Blob ( [ zip . file ( fp ) . asArrayBuffer ( ) ] ) ;
} ;
} ( filePath ) )
} ) ;
}
}
success ( files ) ;
} ;
dataFileService . tryAsTarGz _ = function ( buf , success , failure ) {
2014-04-01 17:00:26 +00:00
var collapsePath = function ( originalPath ) {
// Tar files can contain entries of the form './', so we need to collapse
// those paths down.
var parts = originalPath . split ( '/' ) ;
for ( var i = parts . length - 1 ; i >= 0 ; i -- ) {
var part = parts [ i ] ;
if ( part == '.' ) {
parts . splice ( i , 1 ) ;
}
}
return parts . join ( '/' ) ;
} ;
2014-04-01 04:23:53 +00:00
var gunzip = new Zlib . Gunzip ( buf ) ;
var plain = gunzip . decompress ( ) ;
var handler = new MultiFile ( ) ;
handler . files = [ ] ;
handler . processTarChunks ( dataFileService . arrayToString ( plain ) , 0 ) ;
if ( ! handler . files . length ) {
failure ( ) ;
return ;
}
var files = [ ] ;
for ( var i = 0 ; i < handler . files . length ; ++ i ) {
var currentFile = handler . files [ i ] ;
2014-04-01 17:00:26 +00:00
var path = collapsePath ( currentFile . filename ) ;
if ( path == '' ) { continue ; }
2014-04-01 04:23:53 +00:00
files . push ( {
2014-04-01 17:00:26 +00:00
'name' : dataFileService . getName _ ( path ) ,
'path' : path ,
2014-04-01 04:23:53 +00:00
'canRead' : true ,
'toBlob' : ( function ( currentFile ) {
return function ( ) {
return new Blob ( [ currentFile . data ] , { type : 'application/octet-binary' } ) ;
} ;
} ( currentFile ) )
} ) ;
}
success ( files ) ;
} ;
dataFileService . blobToString = function ( blob , callback ) {
var reader = new FileReader ( ) ;
reader . onload = function ( event ) {
callback ( reader . result ) ;
} ;
reader . readAsText ( blob ) ;
} ;
dataFileService . arrayToString = function ( buf ) {
return String . fromCharCode . apply ( null , new Uint16Array ( buf ) ) ;
} ;
dataFileService . readDataArrayAsPossibleArchive = function ( buf , success , failure ) {
dataFileService . tryAsZip _ ( buf , success , function ( ) {
dataFileService . tryAsTarGz _ ( buf , success , failure ) ;
} ) ;
} ;
dataFileService . downloadDataFileAsArrayBuffer = function ( $scope , url , progress , error , loaded ) {
var request = new XMLHttpRequest ( ) ;
request . open ( 'GET' , url , true ) ;
request . responseType = 'arraybuffer' ;
request . onprogress = function ( e ) {
$scope . $apply ( function ( ) {
var percentLoaded ;
if ( e . lengthComputable ) {
progress ( e . loaded / e . total ) ;
}
} ) ;
} ;
request . onerror = function ( ) {
$scope . $apply ( function ( ) {
error ( ) ;
} ) ;
} ;
request . onload = function ( ) {
if ( this . status == 200 ) {
$scope . $apply ( function ( ) {
var uint8array = new Uint8Array ( request . response ) ;
loaded ( uint8array ) ;
} ) ;
return ;
}
} ;
request . send ( ) ;
} ;
return dataFileService ;
} ] ) ;
2014-01-14 21:01:37 +00:00
$provide . factory ( 'UtilService' , [ '$sanitize' , function ( $sanitize ) {
var utilService = { } ;
2014-04-01 04:23:53 +00:00
2014-01-14 21:01:37 +00:00
utilService . textToSafeHtml = function ( text ) {
var adjusted = text . replace ( /&/g , "&" )
2014-04-01 04:23:53 +00:00
. replace ( /</g , "<" )
. replace ( />/g , ">" )
. replace ( /"/g , """ )
. replace ( /'/g , "'" ) ;
2014-01-14 21:01:37 +00:00
return $sanitize ( adjusted ) ;
} ;
2014-04-01 04:23:53 +00:00
2014-01-14 21:01:37 +00:00
return utilService ;
} ] ) ;
2014-02-25 23:22:55 +00:00
2014-04-01 04:23:53 +00:00
$provide . factory ( 'TriggerDescriptionBuilder' , [ 'UtilService' , '$sanitize' , function ( UtilService , $sanitize ) {
2014-02-25 23:22:55 +00:00
var builderService = { } ;
builderService . getDescription = function ( name , config ) {
switch ( name ) {
case 'github' :
var source = $sanitize ( UtilService . textToSafeHtml ( config [ 'build_source' ] ) ) ;
var desc = '<i class="fa fa-github fa-lg" style="margin-left: 2px; margin-right: 2px"></i> Push to Github Repository ' ;
desc += '<a href="https://github.com/' + source + '" target="_blank">' + source + '</a>' ;
2014-02-26 00:53:41 +00:00
desc += '<br>Dockerfile folder: //' + UtilService . textToSafeHtml ( config [ 'subdir' ] ) ;
2014-02-25 23:22:55 +00:00
return desc ;
default :
return 'Unknown' ;
}
} ;
return builderService ;
} ] ) ;
2014-03-12 04:49:46 +00:00
$provide . factory ( 'StringBuilderService' , [ '$sce' , function ( $sce ) {
var stringBuilderService = { } ;
stringBuilderService . buildString = function ( value _or _func , metadata ) {
var fieldIcons = {
'username' : 'user' ,
'activating_username' : 'user' ,
'delegate_user' : 'user' ,
'delegate_team' : 'group' ,
'team' : 'group' ,
'token' : 'key' ,
'repo' : 'hdd-o' ,
'robot' : 'wrench' ,
'tag' : 'tag' ,
'role' : 'th-large' ,
2014-03-20 19:46:13 +00:00
'original_role' : 'th-large' ,
'application_name' : 'cloud' ,
'client_id' : 'chain'
2014-03-12 04:49:46 +00:00
} ;
var description = value _or _func ;
if ( typeof description != 'string' ) {
description = description ( metadata ) ;
}
for ( var key in metadata ) {
if ( metadata . hasOwnProperty ( key ) ) {
var value = metadata [ key ] != null ? metadata [ key ] . toString ( ) : '(Unknown)' ;
var markedDown = getMarkedDown ( value ) ;
markedDown = markedDown . substr ( '<p>' . length , markedDown . length - '<p></p>' . length ) ;
var icon = fieldIcons [ key ] ;
if ( icon ) {
markedDown = '<i class="fa fa-' + icon + '"></i>' + markedDown ;
}
description = description . replace ( '{' + key + '}' , '<code>' + markedDown + '</code>' ) ;
}
}
return $sce . trustAsHtml ( description . replace ( '\n' , '<br>' ) ) ;
} ;
return stringBuilderService ;
} ] ) ;
2014-02-25 23:22:55 +00:00
2014-01-14 21:01:37 +00:00
$provide . factory ( 'ImageMetadataService' , [ 'UtilService' , function ( UtilService ) {
var metadataService = { } ;
metadataService . getFormattedCommand = function ( image ) {
if ( ! image || ! image . command || ! image . command . length ) {
return '' ;
}
var getCommandStr = function ( command ) {
// Handle /bin/sh commands specially.
if ( command . length > 2 && command [ 0 ] == '/bin/sh' && command [ 1 ] == '-c' ) {
return command [ 2 ] ;
}
return command . join ( ' ' ) ;
} ;
return getCommandStr ( image . command ) ;
} ;
metadataService . getEscapedFormattedCommand = function ( image ) {
return UtilService . textToSafeHtml ( metadataService . getFormattedCommand ( image ) ) ;
} ;
return metadataService ;
} ] ) ;
2014-04-05 03:26:10 +00:00
$provide . factory ( 'Features' , [ function ( ) {
if ( ! window . _ _features ) {
return { } ;
}
var features = window . _ _features ;
features . getFeature = function ( name , opt _defaultValue ) {
var value = features [ name ] ;
if ( value == null ) {
return opt _defaultValue ;
}
return value ;
} ;
features . hasFeature = function ( name ) {
return ! ! features . getFeature ( name ) ;
} ;
features . matchesFeatures = function ( list ) {
for ( var i = 0 ; i < list . length ; ++ i ) {
var value = features . getFeature ( list [ i ] ) ;
if ( ! value ) {
return false ;
}
}
return true ;
} ;
return features ;
} ] ) ;
2014-04-08 23:14:24 +00:00
$provide . factory ( 'Config' , [ function ( ) {
if ( ! window . _ _config ) {
return { } ;
}
var config = window . _ _config ;
2014-04-09 00:33:20 +00:00
config . getDomain = function ( ) {
return config [ 'SERVER_NAME' ] ;
} ;
config . getUrl = function ( opt _path ) {
var path = opt _path || '' ;
return config [ 'PREFERRED_URL_SCHEME' ] + '://' + config [ 'SERVER_NAME' ] + path ;
} ;
2014-04-08 23:14:24 +00:00
config . getValue = function ( name , opt _defaultValue ) {
var value = config [ name ] ;
if ( value == null ) {
return opt _defaultValue ;
}
return value ;
} ;
return config ;
} ] ) ;
2013-12-26 22:45:16 +00:00
$provide . factory ( 'ApiService' , [ 'Restangular' , function ( Restangular ) {
var apiService = { } ;
2014-02-11 03:43:48 +00:00
var getResource = function ( path , opt _background ) {
2013-12-26 22:45:16 +00:00
var resource = { } ;
resource . url = path ;
resource . withOptions = function ( options ) {
this . options = options ;
return this ;
} ;
resource . get = function ( processor , opt _errorHandler ) {
var options = this . options ;
var performer = Restangular . one ( this . url ) ;
var result = {
'loading' : true ,
'value' : null ,
'hasError' : false
} ;
2014-02-11 03:43:48 +00:00
if ( opt _background ) {
performer . withHttpConfig ( {
'ignoreLoadingBar' : true
} ) ;
}
2013-12-26 22:45:16 +00:00
performer . get ( options ) . then ( function ( resp ) {
result . value = processor ( resp ) ;
result . loading = false ;
} , function ( resp ) {
result . hasError = true ;
result . loading = false ;
if ( opt _errorHandler ) {
opt _errorHandler ( resp ) ;
}
} ) ;
return result ;
} ;
return resource ;
} ;
var buildUrl = function ( path , parameters ) {
2014-03-15 03:40:41 +00:00
// We already have /api/v1/ on the URLs, so remove them from the paths.
path = path . substr ( '/api/v1/' . length , path . length ) ;
2013-12-26 22:45:16 +00:00
var url = '' ;
for ( var i = 0 ; i < path . length ; ++ i ) {
var c = path [ i ] ;
2014-03-15 03:40:41 +00:00
if ( c == '{' ) {
var end = path . indexOf ( '}' , i ) ;
2013-12-26 22:45:16 +00:00
var varName = path . substr ( i + 1 , end - i - 1 ) ;
if ( ! parameters [ varName ] ) {
throw new Error ( 'Missing parameter: ' + varName ) ;
}
2014-03-15 03:40:41 +00:00
url += parameters [ varName ] ;
2013-12-26 22:45:16 +00:00
i = end ;
continue ;
}
url += c ;
}
return url ;
} ;
2014-03-15 03:40:41 +00:00
var getGenericOperationName = function ( userOperationName ) {
return userOperationName . replace ( 'User' , '' ) ;
2013-12-26 22:45:16 +00:00
} ;
2014-03-15 03:40:41 +00:00
var getMatchingUserOperationName = function ( orgOperationName , method , userRelatedResource ) {
if ( userRelatedResource ) {
var operations = userRelatedResource [ 'operations' ] ;
for ( var i = 0 ; i < operations . length ; ++ i ) {
var operation = operations [ i ] ;
if ( operation [ 'method' ] . toLowerCase ( ) == method ) {
return operation [ 'nickname' ] ;
}
}
}
throw new Error ( 'Could not find user operation matching org operation: ' + orgOperationName ) ;
} ;
var buildMethodsForEndpointResource = function ( endpointResource , resourceMap ) {
var name = endpointResource [ 'name' ] ;
var operations = endpointResource [ 'operations' ] ;
for ( var i = 0 ; i < operations . length ; ++ i ) {
var operation = operations [ i ] ;
buildMethodsForOperation ( operation , endpointResource , resourceMap ) ;
}
} ;
var buildMethodsForOperation = function ( operation , resource , resourceMap ) {
var method = operation [ 'method' ] . toLowerCase ( ) ;
var operationName = operation [ 'nickname' ] ;
var path = resource [ 'path' ] ;
// Add the operation itself.
apiService [ operationName ] = function ( opt _options , opt _parameters , opt _background ) {
var one = Restangular . one ( buildUrl ( path , opt _parameters ) ) ;
2014-02-11 03:43:48 +00:00
if ( opt _background ) {
one . withHttpConfig ( {
'ignoreLoadingBar' : true
} ) ;
}
return one [ 'custom' + method . toUpperCase ( ) ] ( opt _options ) ;
2013-12-26 22:45:16 +00:00
} ;
2014-03-15 03:40:41 +00:00
// If the method for the operation is a GET, add an operationAsResource method.
2013-12-26 22:45:16 +00:00
if ( method == 'get' ) {
2014-03-15 03:40:41 +00:00
apiService [ operationName + 'AsResource' ] = function ( opt _parameters , opt _background ) {
return getResource ( buildUrl ( path , opt _parameters ) , opt _background ) ;
2013-12-26 22:45:16 +00:00
} ;
}
2014-03-15 03:40:41 +00:00
// If the resource has a user-related resource, then make a generic operation for this operation
// that can call both the user and the organization versions of the operation, depending on the
// parameters given.
if ( resource [ 'quayUserRelated' ] ) {
var userOperationName = getMatchingUserOperationName ( operationName , method , resourceMap [ resource [ 'quayUserRelated' ] ] ) ;
var genericOperationName = getGenericOperationName ( userOperationName ) ;
apiService [ genericOperationName ] = function ( orgname , opt _options , opt _parameters , opt _background ) {
2013-12-26 22:45:16 +00:00
if ( orgname ) {
if ( orgname . name ) {
orgname = orgname . name ;
}
2014-02-11 03:43:48 +00:00
var params = jQuery . extend ( { 'orgname' : orgname } , opt _parameters || { } , opt _background ) ;
2014-03-15 03:40:41 +00:00
return apiService [ operationName ] ( opt _options , params ) ;
2013-12-26 22:45:16 +00:00
} else {
2014-03-15 03:40:41 +00:00
return apiService [ userOperationName ] ( opt _options , opt _parameters , opt _background ) ;
2013-12-26 22:45:16 +00:00
}
} ;
}
} ;
if ( ! window . _ _endpoints ) {
return apiService ;
}
2014-03-15 03:40:41 +00:00
var resourceMap = { } ;
// Build the map of resource names to their objects.
for ( var i = 0 ; i < window . _ _endpoints . length ; ++ i ) {
var endpointResource = window . _ _endpoints [ i ] ;
resourceMap [ endpointResource [ 'name' ] ] = endpointResource ;
}
// Construct the methods for each API endpoint.
2013-12-26 22:45:16 +00:00
for ( var i = 0 ; i < window . _ _endpoints . length ; ++ i ) {
2014-03-15 03:40:41 +00:00
var endpointResource = window . _ _endpoints [ i ] ;
buildMethodsForEndpointResource ( endpointResource , resourceMap ) ;
2013-12-26 22:45:16 +00:00
}
return apiService ;
} ] ) ;
2013-12-18 22:26:56 +00:00
2013-12-17 20:03:34 +00:00
$provide . factory ( 'CookieService' , [ '$cookies' , '$cookieStore' , function ( $cookies , $cookieStore ) {
var cookieService = { } ;
cookieService . putPermanent = function ( name , value ) {
document . cookie = escape ( name ) + "=" + escape ( value ) + "; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/" ;
} ;
cookieService . putSession = function ( name , value ) {
$cookies [ name ] = value ;
} ;
cookieService . clear = function ( name ) {
$cookies [ name ] = '' ;
} ;
cookieService . get = function ( name ) {
return $cookies [ name ] ;
} ;
return cookieService ;
} ] ) ;
2014-03-19 18:27:33 +00:00
$provide . factory ( 'UserService' , [ 'ApiService' , 'CookieService' , '$rootScope' ,
function ( ApiService , CookieService , $rootScope ) {
2013-09-26 23:59:58 +00:00
var userResponse = {
verified : false ,
anonymous : true ,
username : null ,
2013-10-10 17:44:34 +00:00
email : null ,
2014-01-14 20:23:44 +00:00
organizations : [ ] ,
logins : [ ]
2013-09-26 23:59:58 +00:00
}
var userService = { }
2013-12-11 23:27:35 +00:00
userService . hasEverLoggedIn = function ( ) {
2013-12-17 20:03:34 +00:00
return CookieService . get ( 'quay.loggedin' ) == 'true' ;
2013-12-11 23:27:35 +00:00
} ;
2013-12-17 18:19:59 +00:00
userService . updateUserIn = function ( scope , opt _callback ) {
scope . $watch ( function ( ) { return userService . currentUser ( ) ; } , function ( currentUser ) {
scope . user = currentUser ;
if ( opt _callback ) {
opt _callback ( currentUser ) ;
}
} , true ) ;
2013-12-11 23:27:35 +00:00
} ;
2013-11-12 00:26:56 +00:00
userService . load = function ( opt _callback ) {
2014-03-19 18:04:42 +00:00
var handleUserResponse = function ( loadedUser ) {
2013-09-26 23:59:58 +00:00
userResponse = loadedUser ;
2013-10-03 19:46:22 +00:00
2013-10-03 20:16:10 +00:00
if ( ! userResponse . anonymous ) {
mixpanel . identify ( userResponse . username ) ;
mixpanel . people . set ( {
'$email' : userResponse . email ,
'$username' : userResponse . username ,
'verified' : userResponse . verified
} ) ;
2013-10-10 20:34:59 +00:00
mixpanel . people . set _once ( {
'$created' : new Date ( )
} )
2013-12-11 23:27:35 +00:00
2014-01-10 19:29:18 +00:00
if ( window . olark !== undefined ) {
olark ( 'api.visitor.getDetails' , function ( details ) {
if ( details . fullName === null ) {
olark ( 'api.visitor.updateFullName' , { fullName : userResponse . username } ) ;
}
} ) ;
olark ( 'api.visitor.updateEmailAddress' , { emailAddress : userResponse . email } ) ;
olark ( 'api.chat.updateVisitorStatus' , { snippet : 'username: ' + userResponse . username } ) ;
}
2013-12-17 20:03:34 +00:00
CookieService . putPermanent ( 'quay.loggedin' , 'true' ) ;
2013-10-03 20:16:10 +00:00
}
2013-11-12 00:26:56 +00:00
if ( opt _callback ) {
opt _callback ( ) ;
}
2014-03-19 18:04:42 +00:00
} ;
ApiService . getLoggedInUser ( ) . then ( function ( loadedUser ) {
handleUserResponse ( loadedUser ) ;
} , function ( ) {
handleUserResponse ( { 'anonymous' : true } ) ;
2013-09-26 23:59:58 +00:00
} ) ;
} ;
2013-11-08 22:50:42 +00:00
userService . getOrganization = function ( name ) {
if ( ! userResponse || ! userResponse . organizations ) { return null ; }
for ( var i = 0 ; i < userResponse . organizations . length ; ++ i ) {
var org = userResponse . organizations [ i ] ;
if ( org . name == name ) {
return org ;
}
}
2013-11-01 23:13:58 +00:00
2013-11-08 22:50:42 +00:00
return null ;
2013-11-01 23:13:58 +00:00
} ;
2013-12-10 20:22:22 +00:00
userService . isNamespaceAdmin = function ( namespace ) {
if ( namespace == userResponse . username ) {
return true ;
}
var org = userService . getOrganization ( namespace ) ;
if ( ! org ) {
return false ;
}
return org . is _org _admin ;
} ;
2014-01-31 18:52:35 +00:00
userService . isKnownNamespace = function ( namespace ) {
if ( namespace == userResponse . username ) {
return true ;
}
var org = userService . getOrganization ( namespace ) ;
return ! ! org ;
} ;
2013-12-10 20:22:22 +00:00
2013-09-26 23:59:58 +00:00
userService . currentUser = function ( ) {
return userResponse ;
2013-11-08 22:50:42 +00:00
} ;
2013-09-26 23:59:58 +00:00
2014-03-19 18:27:33 +00:00
// Update the user in the root scope.
userService . updateUserIn ( $rootScope ) ;
2013-09-26 23:59:58 +00:00
// Load the user the first time.
userService . load ( ) ;
return userService ;
2013-10-04 18:35:51 +00:00
} ] ) ;
2014-04-08 23:14:24 +00:00
$provide . factory ( 'NotificationService' , [ '$rootScope' , '$interval' , 'UserService' , 'ApiService' , 'StringBuilderService' , 'PlanService' , 'UserService' , 'Config' ,
function ( $rootScope , $interval , UserService , ApiService , StringBuilderService , PlanService , UserService , Config ) {
2014-03-12 04:49:46 +00:00
var notificationService = {
'user' : null ,
'notifications' : [ ] ,
'notificationClasses' : [ ] ,
'notificationSummaries' : [ ]
} ;
var pollTimerHandle = null ;
var notificationKinds = {
'test_notification' : {
'level' : 'primary' ,
2014-03-12 20:05:32 +00:00
'message' : 'This notification is a long message for testing' ,
'page' : '/about/'
} ,
'password_required' : {
'level' : 'error' ,
'message' : 'In order to begin pushing and pulling repositories to Quay.io, a password must be set for your account' ,
'page' : '/user?tab=password'
2014-03-12 23:19:39 +00:00
} ,
'over_private_usage' : {
'level' : 'error' ,
'message' : 'Namespace {namespace} is over its allowed private repository count. ' +
'<br><br>Please upgrade your plan to avoid disruptions in service.' ,
'page' : function ( metadata ) {
var organization = UserService . getOrganization ( metadata [ 'namespace' ] ) ;
if ( organization ) {
return '/organization/' + metadata [ 'namespace' ] + '/admin' ;
} else {
return '/user' ;
}
}
2014-03-12 04:49:46 +00:00
}
} ;
2014-03-12 20:05:32 +00:00
notificationService . getPage = function ( notification ) {
2014-03-12 23:19:39 +00:00
var page = notificationKinds [ notification [ 'kind' ] ] [ 'page' ] ;
if ( typeof page != 'string' ) {
page = page ( notification [ 'metadata' ] ) ;
}
return page ;
2014-03-12 20:05:32 +00:00
} ;
notificationService . getMessage = function ( notification ) {
var kindInfo = notificationKinds [ notification [ 'kind' ] ] ;
return StringBuilderService . buildString ( kindInfo [ 'message' ] , notification [ 'metadata' ] ) ;
} ;
notificationService . getClass = function ( notification ) {
return 'notification-' + notificationKinds [ notification [ 'kind' ] ] [ 'level' ] ;
} ;
2014-03-12 04:49:46 +00:00
notificationService . getClasses = function ( notifications ) {
var classes = [ ] ;
for ( var i = 0 ; i < notifications . length ; ++ i ) {
var notification = notifications [ i ] ;
2014-03-12 20:05:32 +00:00
classes . push ( notificationService . getClass ( notification ) ) ;
2014-03-12 04:49:46 +00:00
}
return classes . join ( ' ' ) ;
} ;
notificationService . update = function ( ) {
2014-03-12 20:05:32 +00:00
var user = UserService . currentUser ( ) ;
if ( ! user || user . anonymous ) {
return ;
}
2014-03-12 04:49:46 +00:00
ApiService . listUserNotifications ( ) . then ( function ( resp ) {
notificationService . notifications = resp [ 'notifications' ] ;
notificationService . notificationClasses = notificationService . getClasses ( notificationService . notifications ) ;
} ) ;
} ;
notificationService . reset = function ( ) {
$interval . cancel ( pollTimerHandle ) ;
pollTimerHandle = $interval ( notificationService . update , 5 * 60 * 1000 /* five minutes */ ) ;
} ;
2014-03-12 23:19:39 +00:00
// Watch for plan changes and update.
PlanService . registerListener ( this , function ( plan ) {
notificationService . reset ( ) ;
notificationService . update ( ) ;
} ) ;
2014-03-12 04:49:46 +00:00
// Watch for user changes and update.
$rootScope . $watch ( function ( ) { return UserService . currentUser ( ) ; } , function ( currentUser ) {
notificationService . reset ( ) ;
notificationService . update ( ) ;
} ) ;
return notificationService ;
} ] ) ;
2014-04-08 23:14:24 +00:00
$provide . factory ( 'KeyService' , [ '$location' , 'Config' , function ( $location , Config ) {
2013-10-08 17:57:48 +00:00
var keyService = { }
2014-04-08 23:14:24 +00:00
keyService [ 'stripePublishableKey' ] = Config [ 'STRIPE_PUBLISHABLE_KEY' ] ;
keyService [ 'githubClientId' ] = Config [ 'GITHUB_CLIENT_ID' ] ;
keyService [ 'githubLoginClientId' ] = Config [ 'GITHUB_LOGIN_CLIENT_ID' ] ;
2014-04-09 00:33:20 +00:00
keyService [ 'githubRedirectUri' ] = Config . getUrl ( '/oauth2/github/callback' ) ;
2013-10-08 17:57:48 +00:00
return keyService ;
} ] ) ;
2014-04-06 04:36:19 +00:00
$provide . factory ( 'PlanService' , [ 'KeyService' , 'UserService' , 'CookieService' , 'ApiService' , 'Features' ,
function ( KeyService , UserService , CookieService , ApiService , Features ) {
2013-11-01 23:13:58 +00:00
var plans = null ;
2013-10-04 18:35:51 +00:00
var planDict = { } ;
2013-11-15 23:17:12 +00:00
var planService = { } ;
var listeners = [ ] ;
2013-10-04 18:35:51 +00:00
2014-02-03 23:18:33 +00:00
var previousSubscribeFailure = false ;
2013-12-20 02:51:46 +00:00
planService . getFreePlan = function ( ) {
return 'free' ;
} ;
2013-11-15 23:17:12 +00:00
planService . registerListener = function ( obj , callback ) {
listeners . push ( { 'obj' : obj , 'callback' : callback } ) ;
} ;
planService . unregisterListener = function ( obj ) {
for ( var i = 0 ; i < listeners . length ; ++ i ) {
if ( listeners [ i ] . obj == obj ) {
listeners . splice ( i , 1 ) ;
break ;
}
}
} ;
2013-12-11 22:50:48 +00:00
planService . notePlan = function ( planId ) {
2014-04-06 04:36:19 +00:00
if ( Features . BILLING ) {
CookieService . putSession ( 'quay.notedplan' , planId ) ;
}
2013-12-11 22:50:48 +00:00
} ;
2013-12-20 02:51:46 +00:00
planService . isOrgCompatible = function ( plan ) {
return plan [ 'stripeId' ] == planService . getFreePlan ( ) || plan [ 'bus_features' ] ;
} ;
planService . getMatchingBusinessPlan = function ( callback ) {
planService . getPlans ( function ( ) {
planService . getSubscription ( null , function ( sub ) {
var plan = planDict [ sub . plan ] ;
if ( ! plan ) {
planService . getMinimumPlan ( 0 , true , callback ) ;
return ;
}
var count = Math . max ( sub . usedPrivateRepos , plan . privateRepos ) ;
planService . getMinimumPlan ( count , true , callback ) ;
} , function ( ) {
planService . getMinimumPlan ( 0 , true , callback ) ;
} ) ;
} ) ;
2013-12-11 22:50:48 +00:00
} ;
planService . handleNotedPlan = function ( ) {
var planId = planService . getAndResetNotedPlan ( ) ;
2014-04-06 04:36:19 +00:00
if ( ! planId || ! Features . BILLING ) { return false ; }
2013-12-11 22:50:48 +00:00
2013-12-11 23:20:24 +00:00
UserService . load ( function ( ) {
if ( UserService . currentUser ( ) . anonymous ) {
return ;
2013-12-11 22:50:48 +00:00
}
2013-12-18 23:16:32 +00:00
planService . getPlan ( planId , function ( plan ) {
2013-12-20 02:51:46 +00:00
if ( planService . isOrgCompatible ( plan ) ) {
2013-12-11 23:20:24 +00:00
document . location = '/organizations/new/?plan=' + planId ;
} else {
document . location = '/user?plan=' + planId ;
}
} ) ;
2013-12-11 22:50:48 +00:00
} ) ;
2014-03-19 18:27:33 +00:00
return true ;
2013-12-11 22:50:48 +00:00
} ;
planService . getAndResetNotedPlan = function ( ) {
2013-12-17 20:03:34 +00:00
var planId = CookieService . get ( 'quay.notedplan' ) ;
CookieService . clear ( 'quay.notedplan' ) ;
2013-12-11 22:50:48 +00:00
return planId ;
} ;
2013-11-19 22:06:17 +00:00
planService . handleCardError = function ( resp ) {
if ( ! planService . isCardError ( resp ) ) { return ; }
bootbox . dialog ( {
"message" : resp . data . carderror ,
"title" : "Credit card issue" ,
"buttons" : {
"close" : {
"label" : "Close" ,
"className" : "btn-primary"
}
}
} ) ;
} ;
planService . isCardError = function ( resp ) {
return resp && resp . data && resp . data . carderror ;
} ;
2013-11-01 23:13:58 +00:00
planService . verifyLoaded = function ( callback ) {
2014-04-06 04:36:19 +00:00
if ( ! Features . BILLING ) { return ; }
2013-11-01 23:13:58 +00:00
if ( plans ) {
callback ( plans ) ;
return ;
}
2013-12-26 22:45:16 +00:00
ApiService . listPlans ( ) . then ( function ( data ) {
2013-11-05 19:40:45 +00:00
var i = 0 ;
2013-12-20 02:51:46 +00:00
for ( i = 0 ; i < data . plans . length ; i ++ ) {
planDict [ data . plans [ i ] . stripeId ] = data . plans [ i ] ;
2013-11-05 19:40:45 +00:00
}
2013-12-20 02:51:46 +00:00
plans = data . plans ;
2013-11-01 23:13:58 +00:00
callback ( plans ) ;
} , function ( ) { callback ( [ ] ) ; } ) ;
} ;
2013-12-20 02:51:46 +00:00
planService . getPlans = function ( callback , opt _includePersonal ) {
2013-12-11 22:50:48 +00:00
planService . verifyLoaded ( function ( ) {
2013-12-20 02:51:46 +00:00
var filtered = [ ] ;
for ( var i = 0 ; i < plans . length ; ++ i ) {
var plan = plans [ i ] ;
if ( plan [ 'deprecated' ] ) { continue ; }
if ( ! opt _includePersonal && ! planService . isOrgCompatible ( plan ) ) { continue ; }
filtered . push ( plan ) ;
2013-12-11 22:50:48 +00:00
}
2013-12-20 02:51:46 +00:00
callback ( filtered ) ;
2013-12-11 22:50:48 +00:00
} ) ;
} ;
2013-12-20 02:51:46 +00:00
planService . getPlan = function ( planId , callback ) {
planService . getPlanIncludingDeprecated ( planId , function ( plan ) {
if ( ! plan [ 'deprecated' ] ) {
callback ( plan ) ;
}
} ) ;
2013-10-28 21:08:26 +00:00
} ;
2013-10-04 18:35:51 +00:00
2013-12-20 02:51:46 +00:00
planService . getPlanIncludingDeprecated = function ( planId , callback ) {
2013-11-01 23:13:58 +00:00
planService . verifyLoaded ( function ( ) {
2013-12-20 02:51:46 +00:00
if ( planDict [ planId ] ) {
2013-11-06 23:14:22 +00:00
callback ( planDict [ planId ] ) ;
}
2013-11-01 23:13:58 +00:00
} ) ;
2013-10-28 21:08:26 +00:00
} ;
2013-11-05 19:40:45 +00:00
planService . getMinimumPlan = function ( privateCount , isBusiness , callback ) {
2013-12-21 03:41:00 +00:00
planService . getPlans ( function ( plans ) {
2013-12-20 02:51:46 +00:00
for ( var i = 0 ; i < plans . length ; i ++ ) {
var plan = plans [ i ] ;
2013-11-01 23:13:58 +00:00
if ( plan . privateRepos >= privateCount ) {
callback ( plan ) ;
return ;
}
2013-10-28 21:08:26 +00:00
}
2013-11-01 23:13:58 +00:00
callback ( null ) ;
2014-03-06 23:36:52 +00:00
} , /* include personal */ ! isBusiness ) ;
2013-10-28 21:08:26 +00:00
} ;
2013-11-15 23:17:12 +00:00
planService . getSubscription = function ( orgname , success , failure ) {
2014-04-06 04:36:19 +00:00
if ( ! Features . BILLING ) { return ; }
ApiService . getSubscription ( orgname ) . then ( success , failure ) ;
2013-11-06 22:30:20 +00:00
} ;
planService . setSubscription = function ( orgname , planId , success , failure , opt _token ) {
2014-04-06 04:36:19 +00:00
if ( ! Features . BILLING ) { return ; }
2013-11-06 22:30:20 +00:00
var subscriptionDetails = {
plan : planId
} ;
2013-10-28 21:08:26 +00:00
2013-11-06 22:30:20 +00:00
if ( opt _token ) {
subscriptionDetails [ 'token' ] = opt _token . id ;
}
2013-12-26 22:45:16 +00:00
ApiService . updateSubscription ( orgname , subscriptionDetails ) . then ( function ( resp ) {
2013-11-15 23:17:12 +00:00
success ( resp ) ;
planService . getPlan ( planId , function ( plan ) {
for ( var i = 0 ; i < listeners . length ; ++ i ) {
listeners [ i ] [ 'callback' ] ( plan ) ;
}
} ) ;
} , failure ) ;
2013-11-06 22:30:20 +00:00
} ;
2013-11-15 23:58:47 +00:00
planService . getCardInfo = function ( orgname , callback ) {
2014-04-06 04:36:19 +00:00
if ( ! Features . BILLING ) { return ; }
2013-12-26 22:45:16 +00:00
ApiService . getCard ( orgname ) . then ( function ( resp ) {
2013-11-15 23:58:47 +00:00
callback ( resp . card ) ;
} , function ( ) {
callback ( { 'is_valid' : false } ) ;
} ) ;
} ;
planService . changePlan = function ( $scope , orgname , planId , callbacks ) {
2014-04-06 04:36:19 +00:00
if ( ! Features . BILLING ) { return ; }
2013-11-09 01:32:56 +00:00
if ( callbacks [ 'started' ] ) {
2013-11-15 23:58:47 +00:00
callbacks [ 'started' ] ( ) ;
2013-11-09 01:32:56 +00:00
}
2013-11-15 23:58:47 +00:00
planService . getPlan ( planId , function ( plan ) {
2013-12-20 02:51:46 +00:00
if ( orgname && ! planService . isOrgCompatible ( plan ) ) { return ; }
2013-11-15 23:58:47 +00:00
planService . getCardInfo ( orgname , function ( cardInfo ) {
2014-02-03 23:18:33 +00:00
if ( plan . price > 0 && ( previousSubscribeFailure || ! cardInfo . last4 ) ) {
2013-11-15 23:58:47 +00:00
planService . showSubscribeDialog ( $scope , orgname , planId , callbacks ) ;
return ;
}
2014-02-03 23:18:33 +00:00
previousSubscribeFailure = false ;
2013-11-19 22:06:17 +00:00
planService . setSubscription ( orgname , planId , callbacks [ 'success' ] , function ( resp ) {
2014-02-03 23:18:33 +00:00
previousSubscribeFailure = true ;
2013-11-19 22:06:17 +00:00
planService . handleCardError ( resp ) ;
callbacks [ 'failure' ] ( resp ) ;
} ) ;
2013-11-15 23:58:47 +00:00
} ) ;
} ) ;
2013-11-06 22:30:20 +00:00
} ;
2013-11-15 23:17:12 +00:00
planService . changeCreditCard = function ( $scope , orgname , callbacks ) {
2014-04-06 04:36:19 +00:00
if ( ! Features . BILLING ) { return ; }
2013-11-15 23:17:12 +00:00
if ( callbacks [ 'opening' ] ) {
callbacks [ 'opening' ] ( ) ;
}
2013-11-19 22:06:17 +00:00
var submitted = false ;
2013-11-15 23:17:12 +00:00
var submitToken = function ( token ) {
2013-11-19 22:06:17 +00:00
if ( submitted ) { return ; }
submitted = true ;
2013-11-15 23:17:12 +00:00
$scope . $apply ( function ( ) {
if ( callbacks [ 'started' ] ) {
callbacks [ 'started' ] ( ) ;
}
var cardInfo = {
'token' : token . id
} ;
2013-12-26 22:45:16 +00:00
ApiService . setCard ( orgname , cardInfo ) . then ( callbacks [ 'success' ] , function ( resp ) {
planService . handleCardError ( resp ) ;
callbacks [ 'failure' ] ( resp ) ;
} ) ;
2013-11-15 23:17:12 +00:00
} ) ;
} ;
var email = planService . getEmail ( orgname ) ;
StripeCheckout . open ( {
key : KeyService . stripePublishableKey ,
address : false ,
email : email ,
currency : 'usd' ,
name : 'Update credit card' ,
description : 'Enter your credit card number' ,
panelLabel : 'Update' ,
token : submitToken ,
image : 'static/img/quay-icon-stripe.png' ,
opened : function ( ) { $scope . $apply ( function ( ) { callbacks [ 'opened' ] ( ) } ) ; } ,
closed : function ( ) { $scope . $apply ( function ( ) { callbacks [ 'closed' ] ( ) } ) ; }
} ) ;
} ;
planService . getEmail = function ( orgname ) {
var email = null ;
if ( UserService . currentUser ( ) ) {
email = UserService . currentUser ( ) . email ;
if ( orgname ) {
org = UserService . getOrganization ( orgname ) ;
if ( org ) {
emaiil = org . email ;
}
}
}
return email ;
} ;
2013-11-09 01:32:56 +00:00
planService . showSubscribeDialog = function ( $scope , orgname , planId , callbacks ) {
2014-04-06 04:36:19 +00:00
if ( ! Features . BILLING ) { return ; }
2013-11-09 01:32:56 +00:00
if ( callbacks [ 'opening' ] ) {
callbacks [ 'opening' ] ( ) ;
}
2013-11-19 22:06:17 +00:00
var submitted = false ;
2013-11-06 22:30:20 +00:00
var submitToken = function ( token ) {
2013-11-19 22:06:17 +00:00
if ( submitted ) { return ; }
submitted = true ;
2013-11-06 22:30:20 +00:00
mixpanel . track ( 'plan_subscribe' ) ;
2013-10-28 21:08:26 +00:00
$scope . $apply ( function ( ) {
2013-11-09 01:32:56 +00:00
if ( callbacks [ 'started' ] ) {
callbacks [ 'started' ] ( ) ;
}
2013-11-15 20:31:05 +00:00
planService . setSubscription ( orgname , planId , callbacks [ 'success' ] , callbacks [ 'failure' ] , token ) ;
2013-10-28 21:08:26 +00:00
} ) ;
} ;
2013-11-01 23:13:58 +00:00
planService . getPlan ( planId , function ( planDetails ) {
2013-11-15 23:17:12 +00:00
var email = planService . getEmail ( orgname ) ;
2013-11-01 23:13:58 +00:00
StripeCheckout . open ( {
key : KeyService . stripePublishableKey ,
address : false ,
2013-11-08 22:50:42 +00:00
email : email ,
2013-11-01 23:13:58 +00:00
amount : planDetails . price ,
currency : 'usd' ,
2014-04-09 00:33:20 +00:00
name : 'Quay.io ' + planDetails . title + ' Subscription' ,
2013-11-01 23:13:58 +00:00
description : 'Up to ' + planDetails . privateRepos + ' private repositories' ,
panelLabel : 'Subscribe' ,
2013-11-06 19:55:40 +00:00
token : submitToken ,
2013-11-09 01:32:56 +00:00
image : 'static/img/quay-icon-stripe.png' ,
opened : function ( ) { $scope . $apply ( function ( ) { callbacks [ 'opened' ] ( ) } ) ; } ,
closed : function ( ) { $scope . $apply ( function ( ) { callbacks [ 'closed' ] ( ) } ) ; }
2013-11-01 23:13:58 +00:00
} ) ;
2013-10-28 21:08:26 +00:00
} ) ;
} ;
2013-10-04 18:35:51 +00:00
return planService ;
} ] ) ;
2013-09-26 23:59:58 +00:00
} ) .
2013-10-01 23:37:33 +00:00
directive ( 'match' , function ( $parse ) {
return {
require : 'ngModel' ,
link : function ( scope , elem , attrs , ctrl ) {
scope . $watch ( function ( ) {
return $parse ( attrs . match ) ( scope ) === ctrl . $modelValue ;
} , function ( currentValue ) {
ctrl . $setValidity ( 'mismatch' , currentValue ) ;
} ) ;
}
} ;
} ) .
2013-10-17 18:29:47 +00:00
directive ( 'onresize' , function ( $window , $parse ) {
return function ( scope , element , attr ) {
var fn = $parse ( attr . onresize ) ;
var notifyResized = function ( ) {
scope . $apply ( function ( ) {
fn ( scope ) ;
} ) ;
} ;
angular . element ( $window ) . on ( 'resize' , null , notifyResized ) ;
scope . $on ( '$destroy' , function ( ) {
angular . element ( $window ) . off ( 'resize' , null , notifyResized ) ;
} ) ;
} ;
} ) .
2013-10-03 19:46:22 +00:00
config ( [ '$routeProvider' , '$locationProvider' , '$analyticsProvider' ,
function ( $routeProvider , $locationProvider , $analyticsProvider ) {
2013-10-03 20:16:10 +00:00
$analyticsProvider . virtualPageviews ( true ) ;
2013-10-10 23:06:04 +00:00
$locationProvider . html5Mode ( true ) ;
2013-10-14 02:06:31 +00:00
// WARNING WARNING WARNING
// If you add a route here, you must add a corresponding route in thr endpoints/web.py
// index rule to make sure that deep links directly deep into the app continue to work.
// WARNING WARNING WARNING
2013-10-03 20:16:10 +00:00
$routeProvider .
2013-11-20 21:17:47 +00:00
when ( '/repository/:namespace/:name' , { templateUrl : '/static/partials/view-repo.html' , controller : RepoCtrl ,
2013-12-18 03:56:28 +00:00
fixFooter : false , reloadOnSearch : false } ) .
2013-11-20 21:17:47 +00:00
when ( '/repository/:namespace/:name/tag/:tag' , { templateUrl : '/static/partials/view-repo.html' , controller : RepoCtrl ,
2013-12-18 03:56:28 +00:00
fixFooter : false } ) .
2013-12-17 18:19:59 +00:00
when ( '/repository/:namespace/:name/image/:image' , { templateUrl : '/static/partials/image-view.html' , controller : ImageViewCtrl , reloadOnSearch : false } ) .
when ( '/repository/:namespace/:name/admin' , { templateUrl : '/static/partials/repo-admin.html' , controller : RepoAdminCtrl , reloadOnSearch : false } ) .
2014-02-10 20:15:23 +00:00
when ( '/repository/:namespace/:name/build' , { templateUrl : '/static/partials/repo-build.html' , controller : RepoBuildCtrl , reloadOnSearch : false } ) .
2014-02-17 22:28:20 +00:00
when ( '/repository/:namespace/:name/build/:buildid/buildpack' , { templateUrl : '/static/partials/build-package.html' , controller : BuildPackageCtrl , reloadOnSearch : false } ) .
2013-11-19 00:03:35 +00:00
when ( '/repository/' , { title : 'Repositories' , description : 'Public and private docker repositories list' ,
templateUrl : '/static/partials/repo-list.html' , controller : RepoListCtrl } ) .
2013-12-10 04:33:28 +00:00
when ( '/user/' , { title : 'Account Settings' , description : 'Account settings for Quay.io' , templateUrl : '/static/partials/user-admin.html' ,
reloadOnSearch : false , controller : UserAdminCtrl } ) .
when ( '/guide/' , { title : 'Guide' , description : 'Guide to using private docker repositories on Quay.io' , templateUrl : '/static/partials/guide.html' ,
controller : GuideCtrl } ) .
2014-02-06 02:00:04 +00:00
when ( '/tutorial/' , { title : 'Tutorial' , description : 'Interactive tutorial for using Quay.io' , templateUrl : '/static/partials/tutorial.html' ,
controller : TutorialCtrl } ) .
2013-12-17 22:02:37 +00:00
when ( '/contact/' , { title : 'Contact Us' , description : 'Different ways for you to get a hold of us when you need us most.' , templateUrl : '/static/partials/contact.html' ,
controller : ContactCtrl } ) .
2014-02-07 00:20:19 +00:00
when ( '/about/' , { title : 'About Us' , description : 'Information about the Quay.io team and the company.' , templateUrl : '/static/partials/about.html' } ) .
2013-11-22 20:54:23 +00:00
when ( '/plans/' , { title : 'Plans and Pricing' , description : 'Plans and pricing for private docker repositories on Quay.io' ,
2013-11-19 00:03:35 +00:00
templateUrl : '/static/partials/plans.html' , controller : PlansCtrl } ) .
2013-11-22 20:54:23 +00:00
when ( '/security/' , { title : 'Security' , description : 'Security features used when transmitting and storing data' ,
2014-02-07 00:20:19 +00:00
templateUrl : '/static/partials/security.html' } ) .
when ( '/signin/' , { title : 'Sign In' , description : 'Sign into Quay.io' , templateUrl : '/static/partials/signin.html' } ) .
2013-11-19 00:03:35 +00:00
when ( '/new/' , { title : 'Create new repository' , description : 'Create a new public or private docker repository, optionally constructing from a dockerfile' ,
templateUrl : '/static/partials/new-repo.html' , controller : NewRepoCtrl } ) .
when ( '/organizations/' , { title : 'Organizations' , description : 'Private docker repository hosting for businesses and organizations' ,
templateUrl : '/static/partials/organizations.html' , controller : OrgsCtrl } ) .
2013-11-22 20:54:23 +00:00
when ( '/organizations/new/' , { title : 'New Organization' , description : 'Create a new organization on Quay.io' ,
2013-11-19 00:03:35 +00:00
templateUrl : '/static/partials/new-organization.html' , controller : NewOrgCtrl } ) .
2013-10-31 22:17:26 +00:00
when ( '/organization/:orgname' , { templateUrl : '/static/partials/org-view.html' , controller : OrgViewCtrl } ) .
2013-12-10 04:33:28 +00:00
when ( '/organization/:orgname/admin' , { templateUrl : '/static/partials/org-admin.html' , controller : OrgAdminCtrl , reloadOnSearch : false } ) .
2013-10-31 22:17:26 +00:00
when ( '/organization/:orgname/teams/:teamname' , { templateUrl : '/static/partials/team-view.html' , controller : TeamViewCtrl } ) .
2013-12-07 00:25:27 +00:00
when ( '/organization/:orgname/logs/:membername' , { templateUrl : '/static/partials/org-member-logs.html' , controller : OrgMemberLogsCtrl } ) .
2014-03-20 19:46:13 +00:00
when ( '/organization/:orgname/application/:clientid' , { templateUrl : '/static/partials/manage-application.html' ,
controller : ManageApplicationCtrl , reloadOnSearch : false } ) .
2013-10-17 21:45:08 +00:00
when ( '/v1/' , { title : 'Activation information' , templateUrl : '/static/partials/v1-page.html' , controller : V1Ctrl } ) .
2013-10-09 22:33:25 +00:00
when ( '/' , { title : 'Hosted Private Docker Registry' , templateUrl : '/static/partials/landing.html' , controller : LandingCtrl } ) .
2013-10-03 20:16:10 +00:00
otherwise ( { redirectTo : '/' } ) ;
} ] ) .
2013-09-24 22:21:14 +00:00
config ( function ( RestangularProvider ) {
2014-03-15 03:40:41 +00:00
RestangularProvider . setBaseUrl ( '/api/v1/' ) ;
2013-09-26 21:59:20 +00:00
} ) ;
2013-11-05 00:36:56 +00:00
2014-04-05 03:26:10 +00:00
function buildConditionalLinker ( $animate , name , evaluator ) {
// Based off of a solution found here: http://stackoverflow.com/questions/20325480/angularjs-whats-the-best-practice-to-add-ngif-to-a-directive-programmatically
return function ( $scope , $element , $attr , ctrl , $transclude ) {
var block ;
var childScope ;
var roles ;
$attr . $observe ( name , function ( value ) {
if ( evaluator ( $scope . $eval ( value ) ) ) {
if ( ! childScope ) {
childScope = $scope . $new ( ) ;
$transclude ( childScope , function ( clone ) {
block = {
startNode : clone [ 0 ] ,
endNode : clone [ clone . length ++ ] = document . createComment ( ' end ' + name + ': ' + $attr [ name ] + ' ' )
} ;
$animate . enter ( clone , $element . parent ( ) , $element ) ;
} ) ;
}
} else {
if ( childScope ) {
childScope . $destroy ( ) ;
childScope = null ;
}
if ( block ) {
$animate . leave ( getBlockElements ( block ) ) ;
block = null ;
}
}
} ) ;
}
}
quayApp . directive ( 'quayRequire' , function ( $animate , Features ) {
return {
transclude : 'element' ,
priority : 600 ,
terminal : true ,
restrict : 'A' ,
link : buildConditionalLinker ( $animate , 'quayRequire' , function ( value ) {
return Features . matchesFeatures ( value ) ;
} )
} ;
} ) ;
2014-04-08 23:14:24 +00:00
quayApp . directive ( 'quayShow' , function ( $animate , Features , Config ) {
2014-04-05 03:26:10 +00:00
return {
priority : 590 ,
restrict : 'A' ,
link : function ( $scope , $element , $attr , ctrl , $transclude ) {
$scope . Features = Features ;
2014-04-08 23:14:24 +00:00
$scope . Config = Config ;
2014-04-05 03:26:10 +00:00
$scope . $watch ( $attr . quayShow , function ( result ) {
$animate [ ! ! result ? 'removeClass' : 'addClass' ] ( $element , 'ng-hide' ) ;
} ) ;
}
} ;
} ) ;
2014-04-08 23:14:24 +00:00
quayApp . directive ( 'quayClasses' , function ( Features , Config ) {
2014-04-05 03:26:10 +00:00
return {
priority : 580 ,
restrict : 'A' ,
link : function ( $scope , $element , $attr , ctrl , $transclude ) {
// Borrowed from ngClass.
function flattenClasses ( classVal ) {
if ( angular . isArray ( classVal ) ) {
return classVal . join ( ' ' ) ;
} else if ( angular . isObject ( classVal ) ) {
var classes = [ ] , i = 0 ;
angular . forEach ( classVal , function ( v , k ) {
if ( v ) {
classes . push ( k ) ;
}
} ) ;
return classes . join ( ' ' ) ;
}
return classVal ;
}
function removeClass ( classVal ) {
$attr . $removeClass ( flattenClasses ( classVal ) ) ;
}
function addClass ( classVal ) {
$attr . $addClass ( flattenClasses ( classVal ) ) ;
}
$scope . $watch ( $attr . quayClasses , function ( result ) {
2014-04-08 23:14:24 +00:00
var scopeVals = {
'Features' : Features ,
'Config' : Config
} ;
2014-04-05 03:26:10 +00:00
for ( var expr in result ) {
if ( ! result . hasOwnProperty ( expr ) ) { continue ; }
// Evaluate the expression with the entire features list added.
2014-04-08 23:14:24 +00:00
var value = $scope . $eval ( expr , scopeVals ) ;
2014-04-05 03:26:10 +00:00
if ( value ) {
addClass ( result [ expr ] ) ;
} else {
removeClass ( result [ expr ] ) ;
}
}
} ) ;
}
} ;
} ) ;
2014-04-08 23:14:24 +00:00
quayApp . directive ( 'quayInclude' , function ( $compile , $templateCache , $http , Features , Config ) {
2014-04-06 18:48:58 +00:00
return {
priority : 595 ,
restrict : 'A' ,
link : function ( $scope , $element , $attr , ctrl ) {
var getTemplate = function ( templateName ) {
var templateUrl = '/static/partials/' + templateName ;
return $http . get ( templateUrl , { cache : $templateCache } ) ;
} ;
var result = $scope . $eval ( $attr . quayInclude ) ;
if ( ! result ) {
return ;
}
2014-04-08 23:14:24 +00:00
var scopeVals = {
'Features' : Features ,
'Config' : Config
} ;
2014-04-06 18:48:58 +00:00
var templatePath = null ;
for ( var expr in result ) {
if ( ! result . hasOwnProperty ( expr ) ) { continue ; }
// Evaluate the expression with the entire features list added.
2014-04-08 23:14:24 +00:00
var value = $scope . $eval ( expr , scopeVals ) ;
2014-04-06 18:48:58 +00:00
if ( value ) {
templatePath = result [ expr ] ;
break ;
}
}
if ( ! templatePath ) {
return ;
}
var promise = getTemplate ( templatePath ) . success ( function ( html ) {
$element . html ( html ) ;
} ) . then ( function ( response ) {
$element . replaceWith ( $compile ( $element . html ( ) ) ( $scope ) ) ;
if ( $attr . onload ) {
$scope . $eval ( $attr . onload ) ;
}
} ) ;
}
} ;
} ) ;
2013-11-26 19:37:55 +00:00
quayApp . directive ( 'entityReference' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/entity-reference.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
2014-01-21 21:23:00 +00:00
'entity' : '=entity' ,
'namespace' : '=namespace'
2013-11-26 19:37:55 +00:00
} ,
2014-02-25 17:32:56 +00:00
controller : function ( $scope , $element , UserService , $sanitize ) {
2014-01-21 21:23:00 +00:00
$scope . getIsAdmin = function ( namespace ) {
return UserService . isNamespaceAdmin ( namespace ) ;
2013-12-10 20:22:22 +00:00
} ;
2014-02-25 17:32:56 +00:00
$scope . getRobotUrl = function ( name ) {
var namespace = $scope . getPrefix ( name ) ;
if ( ! namespace ) {
return '' ;
}
if ( ! $scope . getIsAdmin ( namespace ) ) {
return '' ;
}
var org = UserService . getOrganization ( namespace ) ;
if ( ! org ) {
// This robot is owned by the user.
return '/user/?tab=robots&showRobot=' + $sanitize ( name ) ;
}
return '/organization/' + org [ 'name' ] + '/admin?tab=robots&showRobot=' + $sanitize ( name ) ;
} ;
2013-11-26 19:37:55 +00:00
$scope . getPrefix = function ( name ) {
if ( ! name ) { return '' ; }
var plus = name . indexOf ( '+' ) ;
2014-02-25 17:32:56 +00:00
return name . substr ( 0 , plus ) ;
2013-11-26 19:37:55 +00:00
} ;
$scope . getShortenedName = function ( name ) {
if ( ! name ) { return '' ; }
var plus = name . indexOf ( '+' ) ;
return name . substr ( plus + 1 ) ;
} ;
}
} ;
return directiveDefinitionObject ;
} ) ;
2014-03-20 16:06:29 +00:00
quayApp . directive ( 'applicationInfo' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/application-info.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'application' : '=application'
} ,
controller : function ( $scope , $element , ApiService ) {
}
} ;
return directiveDefinitionObject ;
} ) ;
2014-03-18 20:45:18 +00:00
quayApp . directive ( 'applicationReference' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/application-reference.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'title' : '=title' ,
'clientId' : '=clientId'
} ,
controller : function ( $scope , $element , ApiService , $modal ) {
$scope . showAppDetails = function ( ) {
var params = {
'client_id' : $scope . clientId
} ;
ApiService . getApplicationInformation ( null , params ) . then ( function ( resp ) {
2014-03-20 16:06:29 +00:00
$scope . applicationInfo = resp ;
$modal ( {
title : 'Application Information' ,
scope : $scope ,
template : '/static/directives/application-reference-dialog.html' ,
show : true
} ) ;
2014-03-18 20:45:18 +00:00
} , function ( ) {
bootbox . dialog ( {
"message" : 'The application could not be found; it might have been deleted.' ,
"title" : "Cannot find application" ,
"buttons" : {
"close" : {
"label" : "Close" ,
"className" : "btn-primary"
}
}
} ) ;
} ) ;
} ;
}
} ;
return directiveDefinitionObject ;
} ) ;
2013-11-05 00:36:56 +00:00
quayApp . directive ( 'markdownView' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/markdown-view.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'content' : '=content' ,
'firstLineOnly' : '=firstLineOnly'
} ,
2013-11-19 00:03:35 +00:00
controller : function ( $scope , $element , $sce ) {
2013-11-05 00:36:56 +00:00
$scope . getMarkedDown = function ( content , firstLineOnly ) {
if ( firstLineOnly ) {
content = getFirstTextLine ( content ) ;
}
2013-11-19 00:03:35 +00:00
return $sce . trustAsHtml ( getMarkedDown ( content ) ) ;
2013-11-05 00:36:56 +00:00
} ;
}
} ;
return directiveDefinitionObject ;
} ) ;
2014-01-31 18:52:35 +00:00
quayApp . directive ( 'repoBreadcrumb' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/repo-breadcrumb.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'repo' : '=repo' ,
2014-02-13 23:58:28 +00:00
'image' : '=image' ,
'subsection' : '=subsection' ,
'subsectionIcon' : '=subsectionIcon'
2014-01-31 18:52:35 +00:00
} ,
2014-01-31 19:00:42 +00:00
controller : function ( $scope , $element ) {
2014-01-31 18:52:35 +00:00
}
} ;
return directiveDefinitionObject ;
} ) ;
2013-10-22 05:26:14 +00:00
quayApp . directive ( 'repoCircle' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/repo-circle.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'repo' : '=repo'
} ,
controller : function ( $scope , $element ) {
2013-10-26 20:03:11 +00:00
}
} ;
return directiveDefinitionObject ;
} ) ;
2014-02-28 21:23:36 +00:00
quayApp . directive ( 'copyBox' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/copy-box.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'value' : '=value' ,
'hoveringMessage' : '=hoveringMessage'
} ,
controller : function ( $scope , $element , $rootScope ) {
var number = $rootScope . _ _copyBoxIdCounter || 0 ;
$rootScope . _ _copyBoxIdCounter = number + 1 ;
$scope . inputId = "copy-box-input-" + number ;
var button = $ ( $element ) . find ( '.input-group-addon' ) ;
var input = $ ( $element ) . find ( 'input' ) ;
input . attr ( 'id' , $scope . inputId ) ;
button . attr ( 'data-clipboard-target' , $scope . inputId ) ;
var clip = new ZeroClipboard ( $ ( button ) , { 'moviePath' : 'static/lib/ZeroClipboard.swf' } ) ;
clip . on ( 'complete' , function ( e ) {
var message = $ ( this . parentNode . parentNode . parentNode ) . find ( '.clipboard-copied-message' ) [ 0 ] ;
// Resets the animation.
var elem = message ;
elem . style . display = 'none' ;
elem . classList . remove ( 'animated' ) ;
// Show the notification.
setTimeout ( function ( ) {
elem . style . display = 'inline-block' ;
elem . classList . add ( 'animated' ) ;
} , 10 ) ;
// Reset the notification.
setTimeout ( function ( ) {
elem . style . display = 'none' ;
} , 5000 ) ;
} ) ;
}
} ;
return directiveDefinitionObject ;
} ) ;
2013-12-11 23:20:24 +00:00
quayApp . directive ( 'userSetup' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/user-setup.html' ,
replace : false ,
transclude : true ,
restrict : 'C' ,
scope : {
'redirectUrl' : '=redirectUrl' ,
'signInStarted' : '&signInStarted' ,
'signedIn' : '&signedIn'
} ,
2013-12-26 22:45:16 +00:00
controller : function ( $scope , $location , $timeout , ApiService , KeyService , UserService ) {
2013-12-11 23:20:24 +00:00
$scope . sendRecovery = function ( ) {
2013-12-26 22:45:16 +00:00
ApiService . requestRecoveryEmail ( $scope . recovery ) . then ( function ( ) {
2014-01-10 18:30:17 +00:00
$scope . invalidRecovery = false ;
$scope . errorMessage = '' ;
2013-12-11 23:20:24 +00:00
$scope . sent = true ;
} , function ( result ) {
2014-01-10 18:30:17 +00:00
$scope . invalidRecovery = true ;
$scope . errorMessage = result . data ;
2013-12-11 23:20:24 +00:00
$scope . sent = false ;
} ) ;
} ;
2013-12-11 23:27:35 +00:00
$scope . hasSignedIn = function ( ) {
return UserService . hasEverLoggedIn ( ) ;
} ;
2013-12-11 23:20:24 +00:00
}
} ;
return directiveDefinitionObject ;
} ) ;
2013-11-07 20:19:52 +00:00
quayApp . directive ( 'signinForm' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/signin-form.html' ,
replace : false ,
transclude : true ,
restrict : 'C' ,
scope : {
2013-12-11 22:50:48 +00:00
'redirectUrl' : '=redirectUrl' ,
'signInStarted' : '&signInStarted' ,
'signedIn' : '&signedIn'
2013-11-07 20:19:52 +00:00
} ,
2014-04-06 04:50:30 +00:00
controller : function ( $scope , $location , $timeout , ApiService , KeyService , UserService , CookieService , Features ) {
2013-12-11 22:50:48 +00:00
$scope . showGithub = function ( ) {
2014-04-06 04:50:30 +00:00
if ( ! Features . GITHUB _LOGIN ) { return ; }
2013-12-11 22:50:48 +00:00
$scope . markStarted ( ) ;
2013-11-07 20:19:52 +00:00
2013-12-11 22:50:48 +00:00
var mixpanelDistinctIdClause = '' ;
2013-11-07 20:19:52 +00:00
if ( mixpanel . get _distinct _id !== undefined ) {
2013-12-11 22:50:48 +00:00
$scope . mixpanelDistinctIdClause = "&state=" + encodeURIComponent ( mixpanel . get _distinct _id ( ) ) ;
2013-11-07 20:19:52 +00:00
}
2013-12-11 22:50:48 +00:00
2014-03-19 18:27:33 +00:00
// Save the redirect URL in a cookie so that we can redirect back after GitHub returns to us.
var redirectURL = $scope . redirectUrl || window . location . toString ( ) ;
CookieService . putPermanent ( 'quay.redirectAfterLoad' , redirectURL ) ;
2013-12-11 22:50:48 +00:00
// Needed to ensure that UI work done by the started callback is finished before the location
// changes.
$timeout ( function ( ) {
var url = 'https://github.com/login/oauth/authorize?client_id=' + encodeURIComponent ( KeyService . githubClientId ) +
'&scope=user:email' + mixpanelDistinctIdClause ;
document . location = url ;
} , 250 ) ;
2013-11-07 20:19:52 +00:00
} ;
2013-12-11 22:50:48 +00:00
$scope . markStarted = function ( ) {
if ( $scope . signInStarted != null ) {
$scope . signInStarted ( ) ;
}
} ;
2013-11-07 20:19:52 +00:00
$scope . signin = function ( ) {
2013-12-11 22:50:48 +00:00
$scope . markStarted ( ) ;
2013-12-26 22:45:16 +00:00
ApiService . signinUser ( $scope . user ) . then ( function ( ) {
2013-11-07 20:19:52 +00:00
$scope . needsEmailVerification = false ;
$scope . invalidCredentials = false ;
2013-12-11 22:50:48 +00:00
if ( $scope . signedIn != null ) {
$scope . signedIn ( ) ;
}
2013-11-07 20:19:52 +00:00
UserService . load ( ) ;
2013-12-11 22:50:48 +00:00
// Redirect to the specified page or the landing page
// Note: The timeout of 500ms is needed to ensure dialogs containing sign in
// forms get removed before the location changes.
$timeout ( function ( ) {
2014-03-20 16:06:29 +00:00
if ( $scope . redirectUrl == $location . path ( ) ) {
2013-12-11 22:50:48 +00:00
return ;
}
$location . path ( $scope . redirectUrl ? $scope . redirectUrl : '/' ) ;
} , 500 ) ;
2013-11-07 20:19:52 +00:00
} , function ( result ) {
$scope . needsEmailVerification = result . data . needsEmailVerification ;
$scope . invalidCredentials = result . data . invalidCredentials ;
} ) ;
} ;
}
} ;
return directiveDefinitionObject ;
} ) ;
2013-12-11 21:50:10 +00:00
quayApp . directive ( 'signupForm' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/signup-form.html' ,
replace : false ,
transclude : true ,
restrict : 'C' ,
scope : {
} ,
2013-12-26 22:45:16 +00:00
controller : function ( $scope , $location , $timeout , ApiService , KeyService , UserService ) {
2013-12-11 21:50:10 +00:00
$ ( '.form-signup' ) . popover ( ) ;
angulartics . waitForVendorApi ( mixpanel , 500 , function ( loadedMixpanel ) {
var mixpanelId = loadedMixpanel . get _distinct _id ( ) ;
$scope . github _state _clause = '&state=' + mixpanelId ;
} ) ;
$scope . githubClientId = KeyService . githubClientId ;
$scope . awaitingConfirmation = false ;
$scope . registering = false ;
$scope . register = function ( ) {
$ ( '.form-signup' ) . popover ( 'hide' ) ;
$scope . registering = true ;
2013-12-26 22:45:16 +00:00
ApiService . createNewUser ( $scope . newUser ) . then ( function ( ) {
2013-12-11 21:50:10 +00:00
$scope . awaitingConfirmation = true ;
$scope . registering = false ;
mixpanel . alias ( $scope . newUser . username ) ;
} , function ( result ) {
$scope . registering = false ;
$scope . registerError = result . data . message ;
$timeout ( function ( ) {
$ ( '.form-signup' ) . popover ( 'show' ) ;
} ) ;
} ) ;
} ;
}
} ;
return directiveDefinitionObject ;
} ) ;
2013-11-08 03:08:23 +00:00
quayApp . directive ( 'plansTable' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/plans-table.html' ,
replace : false ,
2013-11-23 01:14:44 +00:00
transclude : false ,
2013-11-08 03:08:23 +00:00
restrict : 'C' ,
scope : {
'plans' : '=plans' ,
'currentPlan' : '=currentPlan'
} ,
controller : function ( $scope , $element ) {
$scope . setPlan = function ( plan ) {
$scope . currentPlan = plan ;
} ;
}
} ;
return directiveDefinitionObject ;
} ) ;
2014-04-09 00:33:20 +00:00
quayApp . directive ( 'dockerAuthDialog' , function ( Config ) {
2013-11-23 01:14:44 +00:00
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/docker-auth-dialog.html' ,
replace : false ,
transclude : true ,
restrict : 'C' ,
scope : {
'username' : '=username' ,
'token' : '=token' ,
'shown' : '=shown' ,
'counter' : '=counter'
} ,
2013-12-26 22:45:16 +00:00
controller : function ( $scope , $element ) {
2013-11-23 01:14:44 +00:00
$scope . isDownloadSupported = function ( ) {
try { return ! ! new Blob ( ) ; } catch ( e ) { }
return false ;
} ;
$scope . downloadCfg = function ( ) {
var auth = $ . base64 . encode ( $scope . username + ":" + $scope . token ) ;
2014-04-09 00:33:20 +00:00
config = { }
config [ Config . getUrl ( '/v1/' ) ] = {
"auth" : auth ,
"email" : ""
2013-11-23 01:14:44 +00:00
} ;
var file = JSON . stringify ( config , null , ' ' ) ;
var blob = new Blob ( [ file ] ) ;
saveAs ( blob , '.dockercfg' ) ;
} ;
var show = function ( r ) {
if ( ! $scope . shown || ! $scope . username || ! $scope . token ) {
$ ( '#dockerauthmodal' ) . modal ( 'hide' ) ;
return ;
}
$ ( '#copyClipboard' ) . clipboardCopy ( ) ;
$ ( '#dockerauthmodal' ) . modal ( { } ) ;
} ;
$scope . $watch ( 'counter' , show ) ;
$scope . $watch ( 'shown' , show ) ;
$scope . $watch ( 'username' , show ) ;
$scope . $watch ( 'token' , show ) ;
}
} ;
return directiveDefinitionObject ;
} ) ;
2013-11-22 23:20:51 +00:00
2014-02-14 23:46:20 +00:00
quayApp . filter ( 'reverse' , function ( ) {
return function ( items ) {
return items . slice ( ) . reverse ( ) ;
} ;
} ) ;
2014-01-03 21:32:00 +00:00
quayApp . filter ( 'bytes' , function ( ) {
return function ( bytes , precision ) {
2014-01-03 21:42:38 +00:00
if ( ! bytes || isNaN ( parseFloat ( bytes ) ) || ! isFinite ( bytes ) ) return 'Unknown' ;
2014-01-03 21:32:00 +00:00
if ( typeof precision === 'undefined' ) precision = 1 ;
var units = [ 'bytes' , 'kB' , 'MB' , 'GB' , 'TB' , 'PB' ] ,
number = Math . floor ( Math . log ( bytes ) / Math . log ( 1024 ) ) ;
return ( bytes / Math . pow ( 1024 , Math . floor ( number ) ) ) . toFixed ( precision ) + ' ' + units [ number ] ;
}
} ) ;
2013-11-27 07:29:31 +00:00
quayApp . filter ( 'visibleLogFilter' , function ( ) {
return function ( logs , allowed ) {
if ( ! allowed ) {
return logs ;
}
var filtered = [ ] ;
angular . forEach ( logs , function ( log ) {
if ( allowed [ log . kind ] ) {
filtered . push ( log ) ;
}
} ) ;
return filtered ;
} ;
} ) ;
2013-12-21 03:38:53 +00:00
quayApp . directive ( 'billingInvoices' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/billing-invoices.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'organization' : '=organization' ,
'user' : '=user' ,
'visible' : '=visible'
} ,
2013-12-26 22:45:16 +00:00
controller : function ( $scope , $element , $sce , ApiService ) {
2013-12-21 03:38:53 +00:00
$scope . loading = false ;
$scope . invoiceExpanded = { } ;
$scope . toggleInvoice = function ( id ) {
$scope . invoiceExpanded [ id ] = ! $scope . invoiceExpanded [ id ] ;
} ;
var update = function ( ) {
var hasValidUser = ! ! $scope . user ;
var hasValidOrg = ! ! $scope . organization ;
var isValid = hasValidUser || hasValidOrg ;
if ( ! $scope . visible || ! isValid ) {
return ;
}
$scope . loading = true ;
2013-12-26 22:45:16 +00:00
ApiService . listInvoices ( $scope . organization ) . then ( function ( resp ) {
2013-12-21 03:38:53 +00:00
$scope . invoices = resp . invoices ;
$scope . loading = false ;
} ) ;
} ;
$scope . $watch ( 'organization' , update ) ;
$scope . $watch ( 'user' , update ) ;
$scope . $watch ( 'visible' , update ) ;
}
} ;
return directiveDefinitionObject ;
} ) ;
2013-11-27 07:29:31 +00:00
quayApp . directive ( 'logsView' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/logs-view.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'organization' : '=organization' ,
'user' : '=user' ,
2013-12-02 19:55:04 +00:00
'visible' : '=visible' ,
2013-12-07 00:25:27 +00:00
'repository' : '=repository' ,
'performer' : '=performer'
2013-11-27 07:29:31 +00:00
} ,
2014-03-12 04:49:46 +00:00
controller : function ( $scope , $element , $sce , Restangular , ApiService , TriggerDescriptionBuilder , StringBuilderService ) {
2013-11-27 07:29:31 +00:00
$scope . loading = true ;
$scope . logs = null ;
$scope . kindsAllowed = null ;
$scope . chartVisible = true ;
2013-12-06 20:59:59 +00:00
$scope . logsPath = '' ;
2013-11-27 07:29:31 +00:00
2013-12-09 22:28:23 +00:00
var datetime = new Date ( ) ;
$scope . logStartDate = new Date ( datetime . getUTCFullYear ( ) , datetime . getUTCMonth ( ) , datetime . getUTCDate ( ) - 7 ) ;
$scope . logEndDate = new Date ( datetime . getUTCFullYear ( ) , datetime . getUTCMonth ( ) , datetime . getUTCDate ( ) ) ;
2014-01-21 23:39:42 +00:00
var defaultPermSuffix = function ( metadata ) {
if ( metadata . activating _username ) {
return ', when creating user is {activating_username}' ;
}
return '' ;
} ;
2013-11-29 05:04:50 +00:00
var logDescriptions = {
'account_change_plan' : 'Change plan' ,
'account_change_cc' : 'Update credit card' ,
'account_change_password' : 'Change password' ,
'account_convert' : 'Convert account to organization' ,
'create_robot' : 'Create Robot Account: {robot}' ,
'delete_robot' : 'Delete Robot Account: {robot}' ,
'create_repo' : 'Create Repository: {repo}' ,
'push_repo' : 'Push to repository: {repo}' ,
'pull_repo' : function ( metadata ) {
2013-12-02 19:55:04 +00:00
if ( metadata . token ) {
return 'Pull repository {repo} via token {token}' ;
} else if ( metadata . username ) {
return 'Pull repository {repo} by {username}' ;
} else {
return 'Public pull of repository {repo} by {_ip}' ;
}
2013-11-29 05:04:50 +00:00
} ,
'delete_repo' : 'Delete repository: {repo}' ,
2013-12-02 19:55:04 +00:00
'change_repo_permission' : function ( metadata ) {
if ( metadata . username ) {
return 'Change permission for user {username} in repository {repo} to {role}' ;
} else if ( metadata . team ) {
return 'Change permission for team {team} in repository {repo} to {role}' ;
} else if ( metadata . token ) {
return 'Change permission for token {token} in repository {repo} to {role}' ;
}
} ,
'delete_repo_permission' : function ( metadata ) {
if ( metadata . username ) {
return 'Remove permission for user {username} from repository {repo}' ;
} else if ( metadata . team ) {
return 'Remove permission for team {team} from repository {repo}' ;
} else if ( metadata . token ) {
return 'Remove permission for token {token} from repository {repo}' ;
}
} ,
2014-01-21 22:04:00 +00:00
'delete_tag' : 'Tag {tag} deleted in repository {repo} by user {username}' ,
2013-11-29 05:04:50 +00:00
'change_repo_visibility' : 'Change visibility for repository {repo} to {visibility}' ,
'add_repo_accesstoken' : 'Create access token {token} in repository {repo}' ,
'delete_repo_accesstoken' : 'Delete access token {token} in repository {repo}' ,
'add_repo_webhook' : 'Add webhook in repository {repo}' ,
'delete_repo_webhook' : 'Delete webhook in repository {repo}' ,
'set_repo_description' : 'Change description for repository {repo}: {description}' ,
2014-02-25 23:22:55 +00:00
'build_dockerfile' : function ( metadata ) {
if ( metadata . trigger _id ) {
var triggerDescription = TriggerDescriptionBuilder . getDescription (
metadata [ 'service' ] , metadata [ 'config' ] ) ;
return 'Build image from Dockerfile for repository {repo} triggered by ' + triggerDescription ;
}
return 'Build image from Dockerfile for repository {repo}' ;
} ,
2013-11-29 05:04:50 +00:00
'org_create_team' : 'Create team: {team}' ,
'org_delete_team' : 'Delete team: {team}' ,
'org_add_team_member' : 'Add member {member} to team {team}' ,
'org_remove_team_member' : 'Remove member {member} from team {team}' ,
'org_set_team_description' : 'Change description of team {team}: {description}' ,
2014-01-21 22:04:00 +00:00
'org_set_team_role' : 'Change permission of team {team} to {role}' ,
'create_prototype_permission' : function ( metadata ) {
if ( metadata . delegate _user ) {
2014-01-21 23:39:42 +00:00
return 'Create default permission: {role} for {delegate_user}' + defaultPermSuffix ( metadata ) ;
2014-01-21 22:04:00 +00:00
} else if ( metadata . delegate _team ) {
2014-01-21 23:39:42 +00:00
return 'Create default permission: {role} for {delegate_team}' + defaultPermSuffix ( metadata ) ;
2014-01-21 22:04:00 +00:00
}
} ,
'modify_prototype_permission' : function ( metadata ) {
if ( metadata . delegate _user ) {
2014-01-21 23:39:42 +00:00
return 'Modify default permission: {role} (from {original_role}) for {delegate_user}' + defaultPermSuffix ( metadata ) ;
2014-01-21 22:04:00 +00:00
} else if ( metadata . delegate _team ) {
2014-01-21 23:39:42 +00:00
return 'Modify default permission: {role} (from {original_role}) for {delegate_team}' + defaultPermSuffix ( metadata ) ;
2014-01-21 22:04:00 +00:00
}
} ,
'delete_prototype_permission' : function ( metadata ) {
if ( metadata . delegate _user ) {
2014-01-21 23:39:42 +00:00
return 'Delete default permission: {role} for {delegate_user}' + defaultPermSuffix ( metadata ) ;
2014-01-21 22:04:00 +00:00
} else if ( metadata . delegate _team ) {
2014-01-21 23:39:42 +00:00
return 'Delete default permission: {role} for {delegate_team}' + defaultPermSuffix ( metadata ) ;
2014-01-21 22:04:00 +00:00
}
2014-02-25 23:22:55 +00:00
} ,
'setup_repo_trigger' : function ( metadata ) {
var triggerDescription = TriggerDescriptionBuilder . getDescription (
metadata [ 'service' ] , metadata [ 'config' ] ) ;
return 'Setup build trigger - ' + triggerDescription ;
} ,
'delete_repo_trigger' : function ( metadata ) {
var triggerDescription = TriggerDescriptionBuilder . getDescription (
metadata [ 'service' ] , metadata [ 'config' ] ) ;
return 'Delete build trigger - ' + triggerDescription ;
2014-03-20 19:46:13 +00:00
} ,
'create_application' : 'Create application {application_name} with client ID {client_id}' ,
'update_application' : 'Update application to {application_name} for client ID {client_id}' ,
'delete_application' : 'Delete application {application_name} with client ID {client_id}' ,
'reset_application_client_secret' : 'Reset the Client Secret of application {application_name} ' +
'with client ID {client_id}'
2013-11-29 05:04:50 +00:00
} ;
2013-11-27 07:29:31 +00:00
var logKinds = {
2014-02-25 23:22:55 +00:00
'account_change_plan' : 'Change plan' ,
'account_change_cc' : 'Update credit card' ,
'account_change_password' : 'Change password' ,
'account_convert' : 'Convert account to organization' ,
'create_robot' : 'Create Robot Account' ,
'delete_robot' : 'Delete Robot Account' ,
'create_repo' : 'Create Repository' ,
'push_repo' : 'Push to repository' ,
'pull_repo' : 'Pull repository' ,
'delete_repo' : 'Delete repository' ,
'change_repo_permission' : 'Change repository permission' ,
'delete_repo_permission' : 'Remove user permission from repository' ,
'change_repo_visibility' : 'Change repository visibility' ,
'add_repo_accesstoken' : 'Create access token' ,
'delete_repo_accesstoken' : 'Delete access token' ,
'add_repo_webhook' : 'Add webhook' ,
'delete_repo_webhook' : 'Delete webhook' ,
'set_repo_description' : 'Change repository description' ,
'build_dockerfile' : 'Build image from Dockerfile' ,
'delete_tag' : 'Delete Tag' ,
'org_create_team' : 'Create team' ,
'org_delete_team' : 'Delete team' ,
'org_add_team_member' : 'Add team member' ,
'org_remove_team_member' : 'Remove team member' ,
'org_set_team_description' : 'Change team description' ,
'org_set_team_role' : 'Change team permission' ,
'create_prototype_permission' : 'Create default permission' ,
'modify_prototype_permission' : 'Modify default permission' ,
'delete_prototype_permission' : 'Delete default permission' ,
'setup_repo_trigger' : 'Setup build trigger' ,
2014-03-20 19:46:13 +00:00
'delete_repo_trigger' : 'Delete build trigger' ,
'create_application' : 'Create Application' ,
'update_application' : 'Update Application' ,
'delete_application' : 'Delete Application' ,
'reset_application_client_secret' : 'Reset Client Secret'
2013-11-27 07:29:31 +00:00
} ;
2013-12-09 22:28:23 +00:00
var getDateString = function ( date ) {
return ( date . getMonth ( ) + 1 ) + '/' + date . getDate ( ) + '/' + date . getFullYear ( ) ;
} ;
var getOffsetDate = function ( date , days ) {
return new Date ( date . getFullYear ( ) , date . getMonth ( ) , date . getDate ( ) + days ) ;
} ;
2013-11-27 07:29:31 +00:00
var update = function ( ) {
2013-12-10 04:33:28 +00:00
var hasValidUser = ! ! $scope . user ;
var hasValidOrg = ! ! $scope . organization ;
var hasValidRepo = $scope . repository && $scope . repository . namespace ;
var isValid = hasValidUser || hasValidOrg || hasValidRepo ;
if ( ! $scope . visible || ! isValid ) {
2013-11-27 07:29:31 +00:00
return ;
}
2013-12-09 22:28:23 +00:00
var twoWeeksAgo = getOffsetDate ( $scope . logEndDate , - 14 ) ;
2013-12-10 02:13:21 +00:00
if ( $scope . logStartDate > $scope . logEndDate || $scope . logStartDate < twoWeeksAgo ) {
2013-12-09 22:28:23 +00:00
$scope . logStartDate = twoWeeksAgo ;
}
2013-11-27 21:56:07 +00:00
$scope . loading = true ;
2013-11-27 07:29:31 +00:00
2013-12-26 22:45:16 +00:00
// Note: We construct the URLs here manually because we also use it for the download
// path.
2013-12-02 19:55:04 +00:00
var url = getRestUrl ( 'user/logs' ) ;
if ( $scope . organization ) {
url = getRestUrl ( 'organization' , $scope . organization . name , 'logs' ) ;
}
if ( $scope . repository ) {
url = getRestUrl ( 'repository' , $scope . repository . namespace , $scope . repository . name , 'logs' ) ;
}
2013-12-07 00:25:27 +00:00
2013-12-09 22:28:23 +00:00
url += '?starttime=' + encodeURIComponent ( getDateString ( $scope . logStartDate ) ) ;
url += '&endtime=' + encodeURIComponent ( getDateString ( $scope . logEndDate ) ) ;
2013-12-07 00:25:27 +00:00
if ( $scope . performer ) {
2013-12-09 22:28:23 +00:00
url += '&performer=' + encodeURIComponent ( $scope . performer . username ) ;
2013-12-07 00:25:27 +00:00
}
2013-12-09 22:28:23 +00:00
2013-11-27 07:29:31 +00:00
var loadLogs = Restangular . one ( url ) ;
loadLogs . customGET ( ) . then ( function ( resp ) {
2014-03-15 03:40:41 +00:00
$scope . logsPath = '/api/v1/' + url ;
2013-12-06 20:59:59 +00:00
2013-11-27 07:29:31 +00:00
if ( ! $scope . chart ) {
2013-11-27 21:56:07 +00:00
$scope . chart = new LogUsageChart ( logKinds ) ;
2013-11-27 07:29:31 +00:00
$ ( $scope . chart ) . bind ( 'filteringChanged' , function ( e ) {
$scope . $apply ( function ( ) { $scope . kindsAllowed = e . allowed ; } ) ;
} ) ;
}
2013-12-09 22:28:23 +00:00
$scope . chart . draw ( 'bar-chart' , resp . logs , $scope . logStartDate , $scope . logEndDate ) ;
2013-11-29 05:04:50 +00:00
$scope . kindsAllowed = null ;
2013-11-27 07:29:31 +00:00
$scope . logs = resp . logs ;
$scope . loading = false ;
} ) ;
} ;
$scope . toggleChart = function ( ) {
$scope . chartVisible = ! $scope . chartVisible ;
} ;
$scope . isVisible = function ( allowed , kind ) {
return allowed == null || allowed . hasOwnProperty ( kind ) ;
} ;
$scope . getColor = function ( kind ) {
return $scope . chart . getColor ( kind ) ;
} ;
2014-03-12 04:49:46 +00:00
$scope . getDescription = function ( log ) {
2014-01-08 23:44:50 +00:00
log . metadata [ '_ip' ] = log . ip ? log . ip : null ;
2014-03-12 04:49:46 +00:00
return StringBuilderService . buildString ( logDescriptions [ log . kind ] || log . kind , log . metadata ) ;
2013-11-27 07:29:31 +00:00
} ;
$scope . $watch ( 'organization' , update ) ;
$scope . $watch ( 'user' , update ) ;
2013-12-02 19:55:04 +00:00
$scope . $watch ( 'repository' , update ) ;
2013-11-27 07:29:31 +00:00
$scope . $watch ( 'visible' , update ) ;
2013-12-07 00:25:27 +00:00
$scope . $watch ( 'performer' , update ) ;
2013-12-09 22:28:23 +00:00
$scope . $watch ( 'logStartDate' , update ) ;
$scope . $watch ( 'logEndDate' , update ) ;
2013-11-27 07:29:31 +00:00
}
} ;
return directiveDefinitionObject ;
} ) ;
2014-03-20 19:46:13 +00:00
quayApp . directive ( 'applicationManager' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/application-manager.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'organization' : '=organization' ,
'visible' : '=visible'
} ,
controller : function ( $scope , $element , ApiService ) {
$scope . loading = false ;
$scope . createApplication = function ( appName ) {
if ( ! appName ) { return ; }
var params = {
'orgname' : $scope . organization . name
} ;
var data = {
'name' : appName
} ;
ApiService . createOrganizationApplication ( data , params ) . then ( function ( resp ) {
$scope . applications . push ( resp ) ;
} , function ( resp ) {
bootbox . dialog ( {
"message" : resp [ 'message' ] || 'The application could not be created' ,
"title" : "Cannot create application" ,
"buttons" : {
"close" : {
"label" : "Close" ,
"className" : "btn-primary"
}
}
} ) ;
} ) ;
} ;
var update = function ( ) {
if ( ! $scope . organization || ! $scope . visible ) { return ; }
if ( $scope . loading ) { return ; }
$scope . loading = true ;
var params = {
'orgname' : $scope . organization . name
} ;
ApiService . getOrganizationApplications ( null , params ) . then ( function ( resp ) {
$scope . loading = false ;
$scope . applications = resp [ 'applications' ] ;
} ) ;
} ;
$scope . $watch ( 'organization' , update ) ;
$scope . $watch ( 'visible' , update ) ;
}
} ;
return directiveDefinitionObject ;
} ) ;
2013-11-22 23:20:51 +00:00
quayApp . directive ( 'robotsManager' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/robots-manager.html' ,
replace : false ,
2013-11-23 01:14:44 +00:00
transclude : false ,
2013-11-22 23:20:51 +00:00
restrict : 'C' ,
scope : {
'organization' : '=organization' ,
'user' : '=user'
} ,
2014-02-25 17:32:56 +00:00
controller : function ( $scope , $element , ApiService , $routeParams ) {
2013-12-10 06:38:05 +00:00
$scope . ROBOT _PATTERN = ROBOT _PATTERN ;
2013-11-22 23:20:51 +00:00
$scope . robots = null ;
$scope . loading = false ;
2013-11-23 01:14:44 +00:00
$scope . shownRobot = null ;
$scope . showRobotCounter = 0 ;
$scope . showRobot = function ( info ) {
$scope . shownRobot = info ;
$scope . showRobotCounter ++ ;
} ;
2014-02-25 17:32:56 +00:00
$scope . findRobotIndexByName = function ( name ) {
for ( var i = 0 ; i < $scope . robots . length ; ++ i ) {
if ( $scope . robots [ i ] . name == name ) {
return i ;
}
}
return - 1 ;
} ;
2013-11-22 23:20:51 +00:00
$scope . getShortenedName = function ( name ) {
var plus = name . indexOf ( '+' ) ;
return name . substr ( plus + 1 ) ;
} ;
$scope . getPrefix = function ( name ) {
var plus = name . indexOf ( '+' ) ;
return name . substr ( 0 , plus ) ;
} ;
2013-11-23 01:14:44 +00:00
$scope . createRobot = function ( name ) {
if ( ! name ) { return ; }
2013-12-26 22:45:16 +00:00
createRobotAccount ( ApiService , ! ! $scope . organization , $scope . organization ? $scope . organization . name : '' , name ,
2013-12-10 06:38:05 +00:00
function ( created ) {
$scope . robots . push ( created ) ;
} ) ;
2013-11-23 01:14:44 +00:00
} ;
2013-11-22 23:20:51 +00:00
$scope . deleteRobot = function ( info ) {
var shortName = $scope . getShortenedName ( info . name ) ;
2013-12-26 22:45:16 +00:00
ApiService . deleteRobot ( $scope . organization , null , { 'robot_shortname' : shortName } ) . then ( function ( resp ) {
2014-02-25 17:32:56 +00:00
var index = $scope . findRobotIndexByName ( info . name ) ;
if ( index >= 0 ) {
$scope . robots . splice ( index , 1 ) ;
2013-11-22 23:20:51 +00:00
}
} , function ( ) {
2013-11-23 01:14:44 +00:00
bootbox . dialog ( {
"message" : 'The selected robot account could not be deleted' ,
"title" : "Cannot delete robot account" ,
"buttons" : {
"close" : {
"label" : "Close" ,
"className" : "btn-primary"
}
}
} ) ;
2013-11-22 23:20:51 +00:00
} ) ;
} ;
var update = function ( ) {
if ( ! $scope . user && ! $scope . organization ) { return ; }
if ( $scope . loading ) { return ; }
$scope . loading = true ;
2013-12-26 22:45:16 +00:00
ApiService . getRobots ( $scope . organization ) . then ( function ( resp ) {
2013-11-22 23:20:51 +00:00
$scope . robots = resp . robots ;
$scope . loading = false ;
2014-02-25 17:32:56 +00:00
if ( $routeParams . showRobot ) {
var index = $scope . findRobotIndexByName ( $routeParams . showRobot ) ;
if ( index >= 0 ) {
$scope . showRobot ( $scope . robots [ index ] ) ;
}
}
2013-11-22 23:20:51 +00:00
} ) ;
} ;
$scope . $watch ( 'organization' , update ) ;
$scope . $watch ( 'user' , update ) ;
}
} ;
return directiveDefinitionObject ;
} ) ;
2014-01-21 19:18:20 +00:00
quayApp . directive ( 'prototypeManager' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/prototype-manager.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'organization' : '=organization'
} ,
controller : function ( $scope , $element , ApiService ) {
$scope . loading = false ;
2014-01-21 20:09:47 +00:00
$scope . activatingForNew = null ;
$scope . delegateForNew = null ;
$scope . clearCounter = 0 ;
2014-01-21 23:34:54 +00:00
$scope . newForWholeOrg = true ;
2014-01-21 19:18:20 +00:00
$scope . roles = [
{ 'id' : 'read' , 'title' : 'Read' , 'kind' : 'success' } ,
{ 'id' : 'write' , 'title' : 'Write' , 'kind' : 'success' } ,
{ 'id' : 'admin' , 'title' : 'Admin' , 'kind' : 'primary' }
] ;
$scope . setRole = function ( role , prototype ) {
var params = {
'orgname' : $scope . organization . name ,
'prototypeid' : prototype . id
} ;
var data = {
'id' : prototype . id ,
'role' : role
} ;
ApiService . updateOrganizationPrototypePermission ( data , params ) . then ( function ( resp ) {
prototype . role = role ;
} , function ( resp ) {
bootbox . dialog ( {
"message" : resp . data ? resp . data : 'The permission could not be modified' ,
"title" : "Cannot modify permission" ,
"buttons" : {
"close" : {
"label" : "Close" ,
"className" : "btn-primary"
}
}
} ) ;
} ) ;
} ;
2014-01-21 23:34:54 +00:00
$scope . comparePrototypes = function ( p ) {
return p . activating _user ? p . activating _user . name : ' ' ;
} ;
2014-01-21 20:09:47 +00:00
$scope . setRoleForNew = function ( role ) {
$scope . newRole = role ;
} ;
2014-01-21 23:34:54 +00:00
$scope . setNewForWholeOrg = function ( value ) {
$scope . newForWholeOrg = value ;
} ;
2014-01-21 20:09:47 +00:00
$scope . showAddDialog = function ( ) {
$scope . activatingForNew = null ;
$scope . delegateForNew = null ;
$scope . newRole = 'read' ;
$scope . clearCounter ++ ;
2014-01-21 23:34:54 +00:00
$scope . newForWholeOrg = true ;
2014-01-21 20:09:47 +00:00
$ ( '#addPermissionDialogModal' ) . modal ( { } ) ;
} ;
$scope . createPrototype = function ( ) {
$scope . loading = true ;
var params = {
'orgname' : $scope . organization . name
} ;
var data = {
'delegate' : $scope . delegateForNew ,
'role' : $scope . newRole
} ;
2014-03-17 19:04:12 +00:00
if ( ! $scope . newForWholeOrg ) {
data [ 'activating_user' ] = $scope . activatingForNew ;
}
2014-01-21 20:09:47 +00:00
ApiService . createOrganizationPrototypePermission ( data , params ) . then ( function ( resp ) {
$scope . prototypes . push ( resp ) ;
$scope . loading = false ;
$ ( '#addPermissionDialogModal' ) . modal ( 'hide' ) ;
} , function ( resp ) {
$ ( '#addPermissionDialogModal' ) . modal ( 'hide' ) ;
bootbox . dialog ( {
"message" : resp . data ? resp . data : 'The permission could not be created' ,
"title" : "Cannot create permission" ,
"buttons" : {
"close" : {
"label" : "Close" ,
"className" : "btn-primary"
}
}
} ) ;
} ) ;
} ;
2014-01-21 19:18:20 +00:00
$scope . deletePrototype = function ( prototype ) {
$scope . loading = true ;
var params = {
'orgname' : $scope . organization . name ,
'prototypeid' : prototype . id
} ;
ApiService . deleteOrganizationPrototypePermission ( null , params ) . then ( function ( resp ) {
$scope . prototypes . splice ( $scope . prototypes . indexOf ( prototype ) , 1 ) ;
$scope . loading = false ;
} , function ( resp ) {
bootbox . dialog ( {
"message" : resp . data ? resp . data : 'The permission could not be deleted' ,
"title" : "Cannot delete permission" ,
"buttons" : {
"close" : {
"label" : "Close" ,
"className" : "btn-primary"
}
}
2014-01-21 20:09:47 +00:00
} ) ;
2014-01-21 19:18:20 +00:00
} ) ;
} ;
var update = function ( ) {
if ( ! $scope . organization ) { return ; }
if ( $scope . loading ) { return ; }
var params = { 'orgname' : $scope . organization . name } ;
$scope . loading = true ;
ApiService . getOrganizationPrototypePermissions ( null , params ) . then ( function ( resp ) {
$scope . prototypes = resp . prototypes ;
$scope . loading = false ;
} ) ;
} ;
$scope . $watch ( 'organization' , update ) ;
}
} ;
return directiveDefinitionObject ;
} ) ;
2014-03-12 04:49:46 +00:00
quayApp . directive ( 'deleteUi' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/delete-ui.html' ,
replace : false ,
transclude : true ,
restrict : 'C' ,
scope : {
'deleteTitle' : '=deleteTitle' ,
'buttonTitle' : '=buttonTitle' ,
'performDelete' : '&performDelete'
} ,
controller : function ( $scope , $element ) {
$scope . buttonTitleInternal = $scope . buttonTitle || 'Delete' ;
$element . children ( ) . attr ( 'tabindex' , 0 ) ;
$scope . focus = function ( ) {
$element [ 0 ] . firstChild . focus ( ) ;
} ;
}
} ;
return directiveDefinitionObject ;
} ) ;
2013-11-23 01:14:44 +00:00
quayApp . directive ( 'popupInputButton' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/popup-input-button.html' ,
replace : false ,
transclude : true ,
restrict : 'C' ,
scope : {
'placeholder' : '=placeholder' ,
'pattern' : '=pattern' ,
'submitted' : '&submitted'
} ,
controller : function ( $scope , $element ) {
$scope . popupShown = function ( ) {
setTimeout ( function ( ) {
var box = $ ( '#input-box' ) ;
box [ 0 ] . value = '' ;
box . focus ( ) ;
2014-03-12 04:49:46 +00:00
} , 40 ) ;
2013-11-23 01:14:44 +00:00
} ;
$scope . getRegexp = function ( pattern ) {
if ( ! pattern ) {
pattern = '.*' ;
}
return new RegExp ( pattern ) ;
} ;
$scope . inputSubmit = function ( ) {
var box = $ ( '#input-box' ) ;
if ( box . hasClass ( 'ng-invalid' ) ) { return ; }
var entered = box [ 0 ] . value ;
if ( ! entered ) {
return ;
}
if ( $scope . submitted ) {
$scope . submitted ( { 'value' : entered } ) ;
}
} ;
}
} ;
return directiveDefinitionObject ;
} ) ;
2013-11-22 23:20:51 +00:00
2013-12-17 18:19:59 +00:00
quayApp . directive ( 'resourceView' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/resource-view.html' ,
replace : false ,
transclude : true ,
restrict : 'C' ,
scope : {
'resource' : '=resource' ,
'errorMessage' : '=errorMessage'
} ,
controller : function ( $scope , $element ) {
}
} ;
return directiveDefinitionObject ;
} ) ;
2013-12-18 03:56:28 +00:00
quayApp . directive ( 'quaySpinner' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/spinner.html' ,
replace : false ,
transclude : true ,
restrict : 'C' ,
scope : { } ,
controller : function ( $scope , $element ) {
}
} ;
return directiveDefinitionObject ;
} ) ;
2013-11-04 19:56:54 +00:00
quayApp . directive ( 'organizationHeader' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/organization-header.html' ,
replace : false ,
2013-11-05 21:15:04 +00:00
transclude : true ,
2013-11-04 19:56:54 +00:00
restrict : 'C' ,
scope : {
'organization' : '=organization' ,
2013-12-02 22:19:19 +00:00
'teamName' : '=teamName' ,
'clickable' : '=clickable'
2013-11-04 19:56:54 +00:00
} ,
controller : function ( $scope , $element ) {
}
} ;
return directiveDefinitionObject ;
} ) ;
2013-11-05 00:59:28 +00:00
quayApp . directive ( 'markdownInput' , function ( ) {
var counter = 0 ;
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/markdown-input.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'content' : '=content' ,
'canWrite' : '=canWrite' ,
'contentChanged' : '=contentChanged' ,
'fieldTitle' : '=fieldTitle'
} ,
controller : function ( $scope , $element ) {
var elm = $element [ 0 ] ;
$scope . id = ( counter ++ ) ;
$scope . editContent = function ( ) {
if ( ! $scope . canWrite ) { return ; }
if ( ! $scope . markdownDescriptionEditor ) {
var converter = Markdown . getSanitizingConverter ( ) ;
var editor = new Markdown . Editor ( converter , '-description-' + $scope . id ) ;
editor . run ( ) ;
$scope . markdownDescriptionEditor = editor ;
}
$ ( '#wmd-input-description-' + $scope . id ) [ 0 ] . value = $scope . content ;
$ ( elm ) . find ( '.modal' ) . modal ( { } ) ;
} ;
$scope . saveContent = function ( ) {
$scope . content = $ ( '#wmd-input-description-' + $scope . id ) [ 0 ] . value ;
$ ( elm ) . find ( '.modal' ) . modal ( 'hide' ) ;
if ( $scope . contentChanged ) {
$scope . contentChanged ( $scope . content ) ;
}
} ;
}
} ;
return directiveDefinitionObject ;
} ) ;
2013-11-19 00:03:35 +00:00
quayApp . directive ( 'repoSearch' , function ( ) {
var number = 0 ;
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/repo-search.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
} ,
controller : function ( $scope , $element , $location , UserService , Restangular ) {
var searchToken = 0 ;
$scope . $watch ( function ( ) { return UserService . currentUser ( ) ; } , function ( currentUser ) {
++ searchToken ;
} , true ) ;
2014-03-08 02:06:31 +00:00
var repoHound = new Bloodhound ( {
2013-11-19 00:03:35 +00:00
name : 'repositories' ,
remote : {
2014-03-15 03:40:41 +00:00
url : '/api/v1/find/repository?query=%QUERY' ,
2013-11-19 00:03:35 +00:00
replace : function ( url , uriEncodedQuery ) {
url = url . replace ( '%QUERY' , uriEncodedQuery ) ;
url += '&cb=' + searchToken ;
return url ;
} ,
filter : function ( data ) {
var datums = [ ] ;
for ( var i = 0 ; i < data . repositories . length ; ++ i ) {
var repo = data . repositories [ i ] ;
datums . push ( {
'value' : repo . name ,
'tokens' : [ repo . name , repo . namespace ] ,
'repo' : repo
} ) ;
}
return datums ;
}
} ,
2014-03-08 02:06:31 +00:00
datumTokenizer : function ( d ) {
return Bloodhound . tokenizers . whitespace ( d . val ) ;
} ,
queryTokenizer : Bloodhound . tokenizers . whitespace
} ) ;
repoHound . initialize ( ) ;
var element = $ ( $element [ 0 ] . childNodes [ 0 ] ) ;
element . typeahead ( { 'highlight' : true } , {
source : repoHound . ttAdapter ( ) ,
templates : {
'suggestion' : function ( datum ) {
template = '<div class="repo-mini-listing">' ;
template += '<i class="fa fa-hdd-o fa-lg"></i>'
template += '<span class="name">' + datum . repo . namespace + '/' + datum . repo . name + '</span>'
if ( datum . repo . description ) {
template += '<span class="description">' + getFirstTextLine ( datum . repo . description ) + '</span>'
}
2013-11-19 00:03:35 +00:00
2014-03-08 02:06:31 +00:00
template += '</div>'
return template ;
}
2013-11-19 00:03:35 +00:00
}
} ) ;
element . on ( 'typeahead:selected' , function ( e , datum ) {
2014-03-08 02:06:31 +00:00
element . typeahead ( 'val' , '' ) ;
$scope . $apply ( function ( ) {
$location . path ( '/repository/' + datum . repo . namespace + '/' + datum . repo . name ) ;
} ) ;
2013-11-19 00:03:35 +00:00
} ) ;
}
} ;
return directiveDefinitionObject ;
} ) ;
quayApp . directive ( 'headerBar' , function ( ) {
var number = 0 ;
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/header-bar.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
} ,
2014-03-12 04:49:46 +00:00
controller : function ( $scope , $element , $location , UserService , PlanService , ApiService , NotificationService ) {
$scope . notificationService = NotificationService ;
2013-12-19 04:03:19 +00:00
// Monitor any user changes and place the current user into the scope.
2014-03-12 04:49:46 +00:00
UserService . updateUserIn ( $scope ) ;
2013-12-19 04:03:19 +00:00
2013-11-19 00:03:35 +00:00
$scope . signout = function ( ) {
2013-12-26 22:45:16 +00:00
ApiService . logout ( ) . then ( function ( ) {
UserService . load ( ) ;
$location . path ( '/' ) ;
2013-11-19 00:03:35 +00:00
} ) ;
} ;
$scope . appLinkTarget = function ( ) {
if ( $ ( "div[ng-view]" ) . length === 0 ) {
return "_self" ;
}
return "" ;
2013-12-19 04:03:19 +00:00
} ;
2013-11-19 00:03:35 +00:00
}
} ;
return directiveDefinitionObject ;
} ) ;
2013-11-02 01:48:10 +00:00
quayApp . directive ( 'entitySearch' , function ( ) {
var number = 0 ;
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/entity-search.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
2013-11-20 23:23:59 +00:00
'namespace' : '=namespace' ,
2013-11-02 01:48:10 +00:00
'inputTitle' : '=inputTitle' ,
2013-11-20 23:23:59 +00:00
'entitySelected' : '=entitySelected' ,
2013-12-10 06:38:05 +00:00
'includeTeams' : '=includeTeams' ,
2014-01-21 20:09:47 +00:00
'isOrganization' : '=isOrganization' ,
'isPersistent' : '=isPersistent' ,
'currentEntity' : '=currentEntity' ,
2014-03-27 22:33:13 +00:00
'clearNow' : '=clearNow' ,
'filter' : '=filter' ,
2013-11-02 01:48:10 +00:00
} ,
2013-12-26 22:45:16 +00:00
controller : function ( $scope , $element , Restangular , UserService , ApiService ) {
2013-12-10 06:38:05 +00:00
$scope . lazyLoading = true ;
$scope . isAdmin = false ;
$scope . lazyLoad = function ( ) {
if ( ! $scope . namespace || ! $scope . lazyLoading ) { return ; }
2014-02-25 17:32:56 +00:00
// Determine whether we can admin this namespace.
2013-12-10 20:49:34 +00:00
$scope . isAdmin = UserService . isNamespaceAdmin ( $scope . namespace ) ;
2014-02-25 17:32:56 +00:00
// If the scope is an organization and we are not part of it, then nothing more we can do.
if ( ! $scope . isAdmin && $scope . isOrganization && ! UserService . getOrganization ( $scope . namespace ) ) {
$scope . teams = null ;
$scope . robots = null ;
$scope . lazyLoading = false ;
return ;
}
2013-12-10 06:38:05 +00:00
if ( $scope . isOrganization && $scope . includeTeams ) {
2013-12-26 22:45:16 +00:00
ApiService . getOrganization ( null , { 'orgname' : $scope . namespace } ) . then ( function ( resp ) {
2013-12-10 06:38:05 +00:00
$scope . teams = resp . teams ;
} ) ;
}
2013-12-26 22:45:16 +00:00
ApiService . getRobots ( $scope . isOrganization ? $scope . namespace : null ) . then ( function ( resp ) {
2013-12-10 20:49:34 +00:00
$scope . robots = resp . robots ;
2013-12-10 06:38:05 +00:00
$scope . lazyLoading = false ;
2013-12-10 20:49:34 +00:00
} , function ( ) {
$scope . lazyLoading = false ;
} ) ;
2013-12-10 06:38:05 +00:00
} ;
$scope . createTeam = function ( ) {
2013-12-10 20:49:34 +00:00
if ( ! $scope . isAdmin ) { return ; }
2013-12-10 06:38:05 +00:00
bootbox . prompt ( 'Enter the name of the new team' , function ( teamname ) {
if ( ! teamname ) { return ; }
var regex = new RegExp ( TEAM _PATTERN ) ;
if ( ! regex . test ( teamname ) ) {
bootbox . alert ( 'Invalid team name' ) ;
return ;
}
2013-12-26 22:45:16 +00:00
createOrganizationTeam ( ApiService , $scope . namespace , teamname , function ( created ) {
2013-12-10 06:38:05 +00:00
$scope . setEntity ( created . name , 'team' , false ) ;
$scope . teams [ teamname ] = created ;
} ) ;
} ) ;
} ;
$scope . createRobot = function ( ) {
2013-12-10 20:49:34 +00:00
if ( ! $scope . isAdmin ) { return ; }
2013-12-10 06:38:05 +00:00
bootbox . prompt ( 'Enter the name of the new robot account' , function ( robotname ) {
if ( ! robotname ) { return ; }
var regex = new RegExp ( ROBOT _PATTERN ) ;
if ( ! regex . test ( robotname ) ) {
bootbox . alert ( 'Invalid robot account name' ) ;
return ;
}
2013-12-26 22:45:16 +00:00
createRobotAccount ( ApiService , $scope . isOrganization , $scope . namespace , robotname , function ( created ) {
2013-12-10 06:38:05 +00:00
$scope . setEntity ( created . name , 'user' , true ) ;
$scope . robots . push ( created ) ;
} ) ;
} ) ;
} ;
$scope . setEntity = function ( name , kind , is _robot ) {
var entity = {
'name' : name ,
'kind' : kind ,
'is_robot' : is _robot
} ;
if ( $scope . is _organization ) {
entity [ 'is_org_member' ] = true ;
}
2014-04-01 23:33:11 +00:00
$scope . setEntityInternal ( entity , false ) ;
2013-12-10 06:38:05 +00:00
} ;
2014-01-21 20:09:47 +00:00
$scope . clearEntityInternal = function ( ) {
$scope . currentEntity = null ;
if ( $scope . entitySelected ) {
$scope . entitySelected ( null ) ;
}
} ;
2014-04-01 23:33:11 +00:00
$scope . setEntityInternal = function ( entity , updateTypeahead ) {
if ( updateTypeahead ) {
$ ( input ) . typeahead ( 'val' , $scope . isPersistent ? entity . name : '' ) ;
} else {
$ ( input ) . val ( $scope . isPersistent ? entity . name : '' ) ;
}
2014-01-21 20:09:47 +00:00
if ( $scope . isPersistent ) {
$scope . currentEntity = entity ;
}
if ( $scope . entitySelected ) {
$scope . entitySelected ( entity ) ;
}
} ;
2013-11-02 01:48:10 +00:00
number ++ ;
2014-03-08 02:06:31 +00:00
var entitySearchB = new Bloodhound ( {
2013-11-02 01:48:10 +00:00
name : 'entities' + number ,
remote : {
2014-03-15 03:40:41 +00:00
url : '/api/v1/entities/%QUERY' ,
2013-11-02 01:48:10 +00:00
replace : function ( url , uriEncodedQuery ) {
2013-12-18 03:56:28 +00:00
var namespace = $scope . namespace || '' ;
url = url . replace ( '%QUERY' , uriEncodedQuery ) ;
url += '?namespace=' + encodeURIComponent ( namespace ) ;
if ( $scope . includeTeams ) {
url += '&includeTeams=true'
}
return url ;
2013-11-02 01:48:10 +00:00
} ,
filter : function ( data ) {
var datums = [ ] ;
for ( var i = 0 ; i < data . results . length ; ++ i ) {
var entity = data . results [ i ] ;
2014-03-27 22:33:13 +00:00
if ( $scope . filter ) {
var allowed = $scope . filter ;
var found = 'user' ;
if ( entity . kind == 'user' ) {
found = entity . is _robot ? 'robot' : 'user' ;
} else if ( entity . kind == 'team' ) {
found = 'team' ;
}
if ( allowed . indexOf ( found ) ) {
continue ;
}
}
2013-11-02 01:48:10 +00:00
datums . push ( {
'value' : entity . name ,
'tokens' : [ entity . name ] ,
'entity' : entity
} ) ;
}
return datums ;
}
} ,
2014-03-08 02:06:31 +00:00
datumTokenizer : function ( d ) {
return Bloodhound . tokenizers . whitespace ( d . val ) ;
} ,
queryTokenizer : Bloodhound . tokenizers . whitespace
} ) ;
entitySearchB . initialize ( ) ;
2013-11-02 01:48:10 +00:00
2014-03-08 02:06:31 +00:00
var counter = 0 ;
var input = $element [ 0 ] . firstChild . firstChild ;
$ ( input ) . typeahead ( {
'highlight' : true
} , {
source : entitySearchB . ttAdapter ( ) ,
templates : {
'empty' : function ( info ) {
// Only display the empty dialog if the server load has finished.
if ( info . resultKind == 'remote' ) {
var val = $ ( input ) . val ( ) ;
if ( ! val ) {
return null ;
}
2013-11-02 01:48:10 +00:00
2014-03-08 02:06:31 +00:00
if ( val . indexOf ( '@' ) > 0 ) {
return '<div class="tt-empty">A Quay.io username (not an e-mail address) must be specified</div>' ;
}
var robots = $scope . isOrganization ? ', robot accounts' : '' ;
var teams = ( $scope . includeTeams && $scope . isOrganization ) ? ' or teams' : '' ;
return '<div class="tt-empty">No matching Quay.io users' + robots + teams + ' found</div>' ;
}
return null ;
} ,
'suggestion' : function ( datum ) {
template = '<div class="entity-mini-listing">' ;
if ( datum . entity . kind == 'user' && ! datum . entity . is _robot ) {
template += '<i class="fa fa-user fa-lg"></i>' ;
} else if ( datum . entity . kind == 'user' && datum . entity . is _robot ) {
template += '<i class="fa fa-wrench fa-lg"></i>' ;
} else if ( datum . entity . kind == 'team' ) {
template += '<i class="fa fa-group fa-lg"></i>' ;
}
template += '<span class="name">' + datum . value + '</span>' ;
if ( datum . entity . is _org _member === false && datum . entity . kind == 'user' ) {
template += '<i class="fa fa-exclamation-triangle" title="User is outside the organization"></i>' ;
}
template += '</div>' ;
return template ;
} }
2013-11-02 01:48:10 +00:00
} ) ;
2014-01-21 20:09:47 +00:00
$ ( input ) . on ( 'input' , function ( e ) {
$scope . $apply ( function ( ) {
if ( $scope . isPersistent ) {
$scope . clearEntityInternal ( ) ;
}
} ) ;
} ) ;
2013-11-02 01:48:10 +00:00
$ ( input ) . on ( 'typeahead:selected' , function ( e , datum ) {
2013-12-10 06:38:05 +00:00
$scope . $apply ( function ( ) {
2014-04-01 23:33:11 +00:00
$scope . setEntityInternal ( datum . entity , true ) ;
2013-12-10 06:38:05 +00:00
} ) ;
2013-11-02 01:48:10 +00:00
} ) ;
2014-01-21 20:09:47 +00:00
$scope . $watch ( 'clearNow' , function ( ) {
2014-03-08 02:06:31 +00:00
$ ( input ) . typeahead ( 'val' , '' ) ;
2014-01-21 20:09:47 +00:00
$scope . clearEntityInternal ( ) ;
} ) ;
2013-11-02 01:48:10 +00:00
$scope . $watch ( 'inputTitle' , function ( title ) {
input . setAttribute ( 'placeholder' , title ) ;
} ) ;
}
} ;
return directiveDefinitionObject ;
} ) ;
2013-11-05 19:47:46 +00:00
quayApp . directive ( 'roleGroup' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/role-group.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'roles' : '=roles' ,
'currentRole' : '=currentRole' ,
'roleChanged' : '&roleChanged'
} ,
controller : function ( $scope , $element ) {
$scope . setRole = function ( role ) {
2013-11-08 04:35:27 +00:00
if ( $scope . currentRole == role ) { return ; }
if ( $scope . roleChanged ) {
2013-11-05 19:47:46 +00:00
$scope . roleChanged ( { 'role' : role } ) ;
2013-11-08 04:35:27 +00:00
} else {
$scope . currentRole = role ;
2013-11-05 19:47:46 +00:00
}
} ;
}
} ;
return directiveDefinitionObject ;
} ) ;
2013-11-15 19:42:31 +00:00
quayApp . directive ( 'billingOptions' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/billing-options.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'user' : '=user' ,
'organization' : '=organization'
} ,
2013-12-26 22:45:16 +00:00
controller : function ( $scope , $element , PlanService , ApiService ) {
2013-11-15 19:42:31 +00:00
$scope . invoice _email = false ;
2013-11-15 23:17:12 +00:00
$scope . currentCard = null ;
// Listen to plan changes.
PlanService . registerListener ( this , function ( plan ) {
if ( plan && plan . price > 0 ) {
update ( ) ;
}
} ) ;
$scope . $on ( '$destroy' , function ( ) {
PlanService . unregisterListener ( this ) ;
} ) ;
$scope . changeCard = function ( ) {
2013-11-19 22:06:17 +00:00
var previousCard = $scope . currentCard ;
2013-11-15 23:17:12 +00:00
$scope . changingCard = true ;
var callbacks = {
'opened' : function ( ) { $scope . changingCard = true ; } ,
'closed' : function ( ) { $scope . changingCard = false ; } ,
'started' : function ( ) { $scope . currentCard = null ; } ,
'success' : function ( resp ) {
$scope . currentCard = resp . card ;
$scope . changingCard = false ;
} ,
2013-11-19 22:06:17 +00:00
'failure' : function ( resp ) {
2013-11-15 23:17:12 +00:00
$scope . changingCard = false ;
2013-11-19 22:06:17 +00:00
$scope . currentCard = previousCard ;
if ( ! PlanService . isCardError ( resp ) ) {
$ ( '#cannotchangecardModal' ) . modal ( { } ) ;
}
2013-11-15 23:17:12 +00:00
}
} ;
PlanService . changeCreditCard ( $scope , $scope . organization ? $scope . organization . name : null , callbacks ) ;
} ;
$scope . getCreditImage = function ( creditInfo ) {
2013-11-15 23:34:54 +00:00
if ( ! creditInfo || ! creditInfo . type ) { return 'credit.png' ; }
2013-11-15 23:17:12 +00:00
var kind = creditInfo . type . toLowerCase ( ) || 'credit' ;
var supported = {
'american express' : 'amex' ,
'credit' : 'credit' ,
'diners club' : 'diners' ,
'discover' : 'discover' ,
'jcb' : 'jcb' ,
'mastercard' : 'mastercard' ,
'visa' : 'visa'
} ;
kind = supported [ kind ] || 'credit' ;
return kind + '.png' ;
} ;
2013-11-15 19:42:31 +00:00
var update = function ( ) {
if ( ! $scope . user && ! $scope . organization ) { return ; }
$scope . obj = $scope . user ? $scope . user : $scope . organization ;
$scope . invoice _email = $scope . obj . invoice _email ;
2013-11-15 23:17:12 +00:00
// Load the credit card information.
2013-11-15 23:58:47 +00:00
PlanService . getCardInfo ( $scope . organization ? $scope . organization . name : null , function ( card ) {
$scope . currentCard = card ;
2013-11-15 23:17:12 +00:00
} ) ;
2013-11-15 19:42:31 +00:00
} ;
var save = function ( ) {
$scope . working = true ;
2013-12-26 22:45:16 +00:00
ApiService . changeDetails ( $scope . organization , $scope . obj ) . then ( function ( resp ) {
2013-11-15 19:42:31 +00:00
$scope . working = false ;
} ) ;
} ;
var checkSave = function ( ) {
if ( ! $scope . obj ) { return ; }
if ( $scope . obj . invoice _email != $scope . invoice _email ) {
$scope . obj . invoice _email = $scope . invoice _email ;
save ( ) ;
}
} ;
$scope . $watch ( 'invoice_email' , checkSave ) ;
$scope . $watch ( 'organization' , update ) ;
$scope . $watch ( 'user' , update ) ;
}
} ;
return directiveDefinitionObject ;
} ) ;
2013-11-06 22:30:20 +00:00
quayApp . directive ( 'planManager' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/plan-manager.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'user' : '=user' ,
2013-11-06 23:14:22 +00:00
'organization' : '=organization' ,
2013-11-15 23:17:12 +00:00
'readyForPlan' : '&readyForPlan' ,
'planChanged' : '&planChanged'
2013-11-06 22:30:20 +00:00
} ,
2013-12-26 22:45:16 +00:00
controller : function ( $scope , $element , PlanService , ApiService ) {
2013-11-06 22:30:20 +00:00
var hasSubscription = false ;
2013-12-20 02:51:46 +00:00
$scope . isPlanVisible = function ( plan , subscribedPlan ) {
if ( plan [ 'deprecated' ] ) {
return plan == subscribedPlan ;
}
if ( $scope . organization && ! PlanService . isOrgCompatible ( plan ) ) {
return false ;
}
return true ;
2013-11-06 22:30:20 +00:00
} ;
$scope . changeSubscription = function ( planId ) {
if ( $scope . planChanging ) { return ; }
2013-11-09 01:32:56 +00:00
var callbacks = {
'opening' : function ( ) { $scope . planChanging = true ; } ,
'started' : function ( ) { $scope . planChanging = true ; } ,
'opened' : function ( ) { $scope . planChanging = true ; } ,
'closed' : function ( ) { $scope . planChanging = false ; } ,
'success' : subscribedToPlan ,
2013-11-19 22:06:17 +00:00
'failure' : function ( resp ) {
$scope . planChanging = false ;
}
2013-11-09 01:32:56 +00:00
} ;
2013-11-15 23:58:47 +00:00
PlanService . changePlan ( $scope , $scope . organization , planId , callbacks ) ;
2013-11-06 22:30:20 +00:00
} ;
$scope . cancelSubscription = function ( ) {
2013-12-20 02:51:46 +00:00
$scope . changeSubscription ( PlanService . getFreePlan ( ) ) ;
2013-11-06 22:30:20 +00:00
} ;
var subscribedToPlan = function ( sub ) {
$scope . subscription = sub ;
2013-12-20 02:51:46 +00:00
if ( sub . plan != PlanService . getFreePlan ( ) ) {
2013-11-06 22:30:20 +00:00
hasSubscription = true ;
}
2013-12-20 02:51:46 +00:00
PlanService . getPlanIncludingDeprecated ( sub . plan , function ( subscribedPlan ) {
2013-11-06 22:30:20 +00:00
$scope . subscribedPlan = subscribedPlan ;
$scope . planUsagePercent = sub . usedPrivateRepos * 100 / $scope . subscribedPlan . privateRepos ;
2013-11-15 23:17:12 +00:00
if ( $scope . planChanged ) {
$scope . planChanged ( { 'plan' : subscribedPlan } ) ;
}
2013-11-06 22:30:20 +00:00
if ( sub . usedPrivateRepos > $scope . subscribedPlan . privateRepos ) {
2013-11-06 22:59:16 +00:00
$scope . limit = 'over' ;
} else if ( sub . usedPrivateRepos == $scope . subscribedPlan . privateRepos ) {
$scope . limit = 'at' ;
2013-11-06 22:30:20 +00:00
} else if ( sub . usedPrivateRepos >= $scope . subscribedPlan . privateRepos * 0.7 ) {
2013-11-06 22:59:16 +00:00
$scope . limit = 'near' ;
2013-11-06 22:30:20 +00:00
} else {
2013-11-06 22:59:16 +00:00
$scope . limit = 'none' ;
2013-11-06 22:30:20 +00:00
}
if ( ! $scope . chart ) {
$scope . chart = new RepositoryUsageChart ( ) ;
$scope . chart . draw ( 'repository-usage-chart' ) ;
}
$scope . chart . update ( sub . usedPrivateRepos || 0 , $scope . subscribedPlan . privateRepos || 0 ) ;
$scope . planChanging = false ;
$scope . planLoading = false ;
} ) ;
} ;
var update = function ( ) {
$scope . planLoading = true ;
if ( ! $scope . plans ) { return ; }
PlanService . getSubscription ( $scope . organization , subscribedToPlan , function ( ) {
// User/Organization has no subscription.
2013-12-20 02:51:46 +00:00
subscribedToPlan ( { 'plan' : PlanService . getFreePlan ( ) } ) ;
2013-11-06 22:30:20 +00:00
} ) ;
} ;
var loadPlans = function ( ) {
2013-11-07 20:33:56 +00:00
if ( $scope . plans || $scope . loadingPlans ) { return ; }
2013-11-06 22:59:16 +00:00
if ( ! $scope . user && ! $scope . organization ) { return ; }
2013-11-06 23:14:22 +00:00
2013-11-07 20:33:56 +00:00
$scope . loadingPlans = true ;
2013-12-20 02:51:46 +00:00
PlanService . verifyLoaded ( function ( plans ) {
$scope . plans = plans ;
2013-11-06 22:30:20 +00:00
update ( ) ;
2013-11-06 23:14:22 +00:00
if ( $scope . readyForPlan ) {
var planRequested = $scope . readyForPlan ( ) ;
2013-12-20 02:51:46 +00:00
if ( planRequested && planRequested != PlanService . getFreePlan ( ) ) {
2013-11-06 23:14:22 +00:00
$scope . changeSubscription ( planRequested ) ;
}
}
2013-11-06 22:30:20 +00:00
} ) ;
} ;
// Start the initial download.
2013-11-06 22:59:16 +00:00
$scope . planLoading = true ;
loadPlans ( ) ;
2013-11-06 22:30:20 +00:00
2013-11-07 00:19:21 +00:00
$scope . $watch ( 'organization' , loadPlans ) ;
$scope . $watch ( 'user' , loadPlans ) ;
2013-11-06 22:30:20 +00:00
}
} ;
return directiveDefinitionObject ;
} ) ;
2013-11-01 21:35:26 +00:00
quayApp . directive ( 'namespaceSelector' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/namespace-selector.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'user' : '=user' ,
2013-11-07 05:49:13 +00:00
'namespace' : '=namespace' ,
'requireCreate' : '=requireCreate'
2013-11-01 21:35:26 +00:00
} ,
2013-12-17 20:03:34 +00:00
controller : function ( $scope , $element , $routeParams , CookieService ) {
2013-11-07 05:49:13 +00:00
$scope . namespaces = { } ;
$scope . initialize = function ( user ) {
2014-01-16 00:15:38 +00:00
var preferredNamespace = user . username ;
2013-11-07 05:49:13 +00:00
var namespaces = { } ;
namespaces [ user . username ] = user ;
2013-11-07 06:48:58 +00:00
if ( user . organizations ) {
for ( var i = 0 ; i < user . organizations . length ; ++ i ) {
namespaces [ user . organizations [ i ] . name ] = user . organizations [ i ] ;
2014-01-16 00:15:38 +00:00
if ( user . organizations [ i ] . preferred _namespace ) {
preferredNamespace = user . organizations [ i ] . name ;
}
2013-11-07 06:48:58 +00:00
}
2013-11-07 05:49:13 +00:00
}
2014-01-16 00:15:38 +00:00
var initialNamespace = $routeParams [ 'namespace' ] || CookieService . get ( 'quay.namespace' ) ||
preferredNamespace || $scope . user . username ;
2013-11-07 05:49:13 +00:00
$scope . namespaces = namespaces ;
$scope . setNamespace ( $scope . namespaces [ initialNamespace ] ) ;
} ;
2013-11-01 21:35:26 +00:00
$scope . setNamespace = function ( namespaceObj ) {
if ( ! namespaceObj ) {
2013-11-07 05:49:13 +00:00
namespaceObj = $scope . namespaces [ $scope . user . username ] ;
}
if ( $scope . requireCreate && ! namespaceObj . can _create _repo ) {
namespaceObj = $scope . namespaces [ $scope . user . username ] ;
2013-11-01 21:35:26 +00:00
}
2013-11-07 05:49:13 +00:00
var newNamespace = namespaceObj . name || namespaceObj . username ;
2013-11-01 21:35:26 +00:00
$scope . namespaceObj = namespaceObj ;
2013-11-07 05:49:13 +00:00
$scope . namespace = newNamespace ;
2013-12-17 20:03:34 +00:00
if ( newNamespace ) {
CookieService . putPermanent ( 'quay.namespace' , newNamespace ) ;
}
2013-11-01 21:35:26 +00:00
} ;
2014-01-16 00:15:38 +00:00
$scope . $watch ( 'namespace' , function ( namespace ) {
if ( $scope . namespaceObj && namespace && namespace != $scope . namespaceObj . username ) {
$scope . setNamespace ( $scope . namespaces [ namespace ] ) ;
}
} ) ;
2013-11-01 21:35:26 +00:00
$scope . $watch ( 'user' , function ( user ) {
2013-11-07 05:49:13 +00:00
$scope . user = user ;
$scope . initialize ( user ) ;
2013-11-01 21:35:26 +00:00
} ) ;
}
} ;
return directiveDefinitionObject ;
} ) ;
2014-02-13 02:16:11 +00:00
quayApp . directive ( 'buildLogPhase' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/build-log-phase.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'phase' : '=phase'
} ,
controller : function ( $scope , $element ) {
}
} ;
return directiveDefinitionObject ;
} ) ;
quayApp . directive ( 'buildLogError' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/build-log-error.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'error' : '=error'
} ,
controller : function ( $scope , $element ) {
}
} ;
return directiveDefinitionObject ;
} ) ;
2014-02-20 18:27:59 +00:00
quayApp . directive ( 'triggerDescription' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/trigger-description.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
2014-03-05 21:27:56 +00:00
'trigger' : '=trigger' ,
'short' : '=short'
2014-02-20 18:27:59 +00:00
} ,
controller : function ( $scope , $element ) {
}
} ;
return directiveDefinitionObject ;
} ) ;
2014-02-25 01:36:54 +00:00
quayApp . directive ( 'dropdownSelect' , function ( $compile ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/dropdown-select.html' ,
replace : true ,
transclude : true ,
restrict : 'C' ,
scope : {
'selectedItem' : '=selectedItem' ,
'placeholder' : '=placeholder' ,
'lookaheadItems' : '=lookaheadItems' ,
'handleItemSelected' : '&handleItemSelected' ,
'handleInput' : '&handleInput'
} ,
controller : function ( $scope , $element , $rootScope ) {
if ( ! $rootScope . _ _dropdownSelectCounter ) {
$rootScope . _ _dropdownSelectCounter = 1 ;
}
$scope . placeholder = $scope . placeholder || '' ;
$scope . internalItem = null ;
// Setup lookahead.
var input = $ ( $element ) . find ( '.lookahead-input' ) ;
$scope . $watch ( 'selectedItem' , function ( item ) {
if ( $scope . selectedItem == $scope . internalItem ) {
// The item has already been set due to an internal action.
return ;
}
if ( $scope . selectedItem != null ) {
$ ( input ) . val ( item . toString ( ) ) ;
} else {
$ ( input ) . val ( '' ) ;
}
} ) ;
$scope . $watch ( 'lookaheadItems' , function ( items ) {
$ ( input ) . off ( ) ;
if ( ! items ) {
return ;
}
2014-03-08 02:06:31 +00:00
var formattedItems = [ ] ;
for ( var i = 0 ; i < items . length ; ++ i ) {
var formattedItem = items [ i ] ;
if ( typeof formattedItem == 'string' ) {
formattedItem = {
'value' : formattedItem
} ;
}
formattedItems . push ( formattedItem ) ;
}
var dropdownHound = new Bloodhound ( {
2014-02-25 01:36:54 +00:00
name : 'dropdown-items-' + $rootScope . _ _dropdownSelectCounter ,
2014-03-08 02:06:31 +00:00
local : formattedItems ,
datumTokenizer : function ( d ) {
return Bloodhound . tokenizers . whitespace ( d . val || d . value || '' ) ;
} ,
queryTokenizer : Bloodhound . tokenizers . whitespace
} ) ;
dropdownHound . initialize ( ) ;
$ ( input ) . typeahead ( { } , {
source : dropdownHound . ttAdapter ( ) ,
templates : {
'suggestion' : function ( datum ) {
template = datum [ 'template' ] ? datum [ 'template' ] ( datum ) : datum [ 'value' ] ;
return template ;
}
2014-02-25 01:36:54 +00:00
}
} ) ;
$ ( input ) . on ( 'input' , function ( e ) {
$scope . $apply ( function ( ) {
$scope . internalItem = null ;
$scope . selectedItem = null ;
if ( $scope . handleInput ) {
$scope . handleInput ( { 'input' : $ ( input ) . val ( ) } ) ;
}
} ) ;
} ) ;
$ ( input ) . on ( 'typeahead:selected' , function ( e , datum ) {
$scope . $apply ( function ( ) {
$scope . internalItem = datum [ 'item' ] || datum [ 'value' ] ;
$scope . selectedItem = datum [ 'item' ] || datum [ 'value' ] ;
if ( $scope . handleItemSelected ) {
$scope . handleItemSelected ( { 'datum' : datum } ) ;
}
} ) ;
} ) ;
$rootScope . _ _dropdownSelectCounter ++ ;
} ) ;
} ,
link : function ( scope , element , attrs ) {
var transcludedBlock = element . find ( 'div.transcluded' ) ;
var transcludedElements = transcludedBlock . children ( ) ;
var iconContainer = element . find ( 'div.dropdown-select-icon-transclude' ) ;
var menuContainer = element . find ( 'div.dropdown-select-menu-transclude' ) ;
angular . forEach ( transcludedElements , function ( elem ) {
if ( angular . element ( elem ) . hasClass ( 'dropdown-select-icon' ) ) {
iconContainer . append ( elem ) ;
} else if ( angular . element ( elem ) . hasClass ( 'dropdown-select-menu' ) ) {
menuContainer . replaceWith ( elem ) ;
}
} ) ;
transcludedBlock . remove ( ) ;
}
} ;
return directiveDefinitionObject ;
} ) ;
quayApp . directive ( 'dropdownSelectIcon' , function ( ) {
var directiveDefinitionObject = {
priority : 1 ,
require : '^dropdownSelect' ,
templateUrl : '/static/directives/dropdown-select-icon.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
} ,
controller : function ( $scope , $element ) {
}
} ;
return directiveDefinitionObject ;
} ) ;
quayApp . directive ( 'dropdownSelectMenu' , function ( ) {
var directiveDefinitionObject = {
priority : 1 ,
require : '^dropdownSelect' ,
templateUrl : '/static/directives/dropdown-select-menu.html' ,
replace : true ,
transclude : true ,
restrict : 'C' ,
scope : {
} ,
controller : function ( $scope , $element ) {
}
} ;
return directiveDefinitionObject ;
} ) ;
2014-02-20 23:57:49 +00:00
quayApp . directive ( 'triggerSetupGithub' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/trigger-setup-github.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'repository' : '=repository' ,
'trigger' : '=trigger'
} ,
controller : function ( $scope , $element , ApiService ) {
$scope . setupReady = false ;
$scope . loading = true ;
2014-02-25 01:36:54 +00:00
2014-02-25 05:19:38 +00:00
$scope . handleLocationInput = function ( location ) {
$scope . trigger [ 'config' ] [ 'subdir' ] = location || '' ;
2014-02-25 05:42:33 +00:00
$scope . isInvalidLocation = $scope . locations . indexOf ( location ) < 0 ;
2014-02-25 05:19:38 +00:00
} ;
2014-03-08 02:06:31 +00:00
$scope . handleLocationSelected = function ( datum ) {
$scope . setLocation ( datum [ 'value' ] ) ;
} ;
2014-02-25 01:36:54 +00:00
$scope . setLocation = function ( location ) {
$scope . currentLocation = location ;
$scope . trigger [ 'config' ] [ 'subdir' ] = location || '' ;
2014-02-25 05:42:33 +00:00
$scope . isInvalidLocation = false ;
2014-02-20 23:57:49 +00:00
} ;
2014-02-25 01:36:54 +00:00
2014-02-20 23:57:49 +00:00
$scope . selectRepo = function ( repo , org ) {
2014-02-25 01:36:54 +00:00
$scope . currentRepo = {
'repo' : repo ,
'avatar_url' : org [ 'info' ] [ 'avatar_url' ] ,
'toString' : function ( ) {
return this . repo ;
}
} ;
2014-02-20 23:57:49 +00:00
} ;
2014-02-25 01:36:54 +00:00
$scope . selectRepoInternal = function ( currentRepo ) {
if ( ! currentRepo ) {
$scope . trigger . $ready = false ;
return ;
}
var params = {
'repository' : $scope . repository . namespace + '/' + $scope . repository . name ,
'trigger_uuid' : $scope . trigger [ 'id' ]
2014-02-20 23:57:49 +00:00
} ;
2014-02-25 01:36:54 +00:00
var repo = currentRepo [ 'repo' ] ;
2014-02-20 23:57:49 +00:00
$scope . trigger [ 'config' ] = {
2014-02-25 01:36:54 +00:00
'build_source' : repo ,
'subdir' : ''
2014-02-20 23:57:49 +00:00
} ;
2014-02-25 01:36:54 +00:00
// Lookup the possible Dockerfile locations.
$scope . locations = null ;
if ( repo ) {
ApiService . listBuildTriggerSubdirs ( $scope . trigger [ 'config' ] , params ) . then ( function ( resp ) {
if ( resp [ 'status' ] == 'error' ) {
$scope . locationError = resp [ 'message' ] || 'Could not load Dockerfile locations' ;
$scope . locations = null ;
$scope . trigger . $ready = false ;
2014-02-25 05:42:33 +00:00
$scope . isInvalidLocation = false ;
2014-02-25 01:36:54 +00:00
return ;
}
$scope . locationError = null ;
$scope . locations = resp [ 'subdir' ] || [ ] ;
$scope . trigger . $ready = true ;
2014-02-25 20:25:24 +00:00
if ( $scope . locations . length > 0 ) {
$scope . setLocation ( $scope . locations [ 0 ] ) ;
} else {
$scope . currentLocation = null ;
$scope . isInvalidLocation = resp [ 'subdir' ] . indexOf ( '' ) < 0 ;
}
2014-02-25 01:36:54 +00:00
} , function ( resp ) {
$scope . locationError = resp [ 'message' ] || 'Could not load Dockerfile locations' ;
$scope . locations = null ;
$scope . trigger . $ready = false ;
2014-02-25 05:42:33 +00:00
$scope . isInvalidLocation = false ;
2014-02-25 01:36:54 +00:00
} ) ;
}
2014-02-20 23:57:49 +00:00
} ;
2014-02-25 01:36:54 +00:00
var setupTypeahead = function ( ) {
2014-02-20 23:57:49 +00:00
var repos = [ ] ;
for ( var i = 0 ; i < $scope . orgs . length ; ++ i ) {
var org = $scope . orgs [ i ] ;
var orepos = org [ 'repos' ] ;
for ( var j = 0 ; j < orepos . length ; ++ j ) {
2014-02-25 01:36:54 +00:00
var repoValue = {
'repo' : orepos [ j ] ,
'avatar_url' : org [ 'info' ] [ 'avatar_url' ] ,
'toString' : function ( ) {
return this . repo ;
}
} ;
var datum = {
'name' : orepos [ j ] ,
'org' : org ,
'value' : orepos [ j ] ,
'title' : orepos [ j ] ,
'item' : repoValue
} ;
repos . push ( datum ) ;
2014-02-20 23:57:49 +00:00
}
}
2014-02-25 01:36:54 +00:00
$scope . repoLookahead = repos ;
2014-02-20 23:57:49 +00:00
} ;
var loadSources = function ( ) {
var params = {
'repository' : $scope . repository . namespace + '/' + $scope . repository . name ,
'trigger_uuid' : $scope . trigger . id
} ;
ApiService . listTriggerBuildSources ( null , params ) . then ( function ( resp ) {
$scope . orgs = resp [ 'sources' ] ;
setupTypeahead ( ) ;
$scope . loading = false ;
} ) ;
} ;
loadSources ( ) ;
2014-02-25 01:36:54 +00:00
$scope . $watch ( 'currentRepo' , function ( repo ) {
$scope . selectRepoInternal ( repo ) ;
2014-02-20 23:57:49 +00:00
} ) ;
}
} ;
return directiveDefinitionObject ;
} ) ;
2014-02-13 02:16:11 +00:00
quayApp . directive ( 'buildLogCommand' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/build-log-command.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'command' : '=command'
} ,
2014-02-17 23:31:45 +00:00
controller : function ( $scope , $element ) {
$scope . getWithoutStep = function ( fullTitle ) {
var colon = fullTitle . indexOf ( ':' ) ;
if ( colon <= 0 ) {
return '' ;
}
return $ . trim ( fullTitle . substring ( colon + 1 ) ) ;
} ;
}
} ;
return directiveDefinitionObject ;
} ) ;
quayApp . directive ( 'dockerfileCommand' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/dockerfile-command.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'command' : '=command'
} ,
2014-04-09 00:33:20 +00:00
controller : function ( $scope , $element , $sanitize , Config ) {
2014-02-13 02:16:11 +00:00
var registryHandlers = {
'quay.io' : function ( pieces ) {
var rnamespace = pieces [ pieces . length - 2 ] ;
2014-02-17 23:31:45 +00:00
var rname = pieces [ pieces . length - 1 ] . split ( ':' ) [ 0 ] ;
2014-02-13 02:16:11 +00:00
return '/repository/' + rnamespace + '/' + rname + '/' ;
} ,
'' : function ( pieces ) {
var rnamespace = pieces . length == 1 ? '_' : pieces [ 0 ] ;
2014-02-17 23:31:45 +00:00
var rname = pieces [ pieces . length - 1 ] . split ( ':' ) [ 0 ] ;
2014-02-13 02:32:46 +00:00
return 'https://index.docker.io/u/' + rnamespace + '/' + rname + '/' ;
2014-02-13 02:16:11 +00:00
}
} ;
2014-04-09 00:33:20 +00:00
registryHandlers [ Config . getDomain ( ) ] = registryHandlers [ 'quay.io' ] ;
2014-02-13 02:16:11 +00:00
var kindHandlers = {
'FROM' : function ( title ) {
var pieces = title . split ( '/' ) ;
2014-02-13 02:32:46 +00:00
var registry = pieces . length < 3 ? '' : pieces [ 0 ] ;
2014-02-13 02:16:11 +00:00
if ( ! registryHandlers [ registry ] ) {
return title ;
}
2014-02-13 20:08:10 +00:00
return '<i class="fa fa-hdd-o"></i> <a href="' + registryHandlers [ registry ] ( pieces ) + '" target="_blank">' + title + '</a>' ;
2014-02-13 02:16:11 +00:00
}
} ;
2014-02-17 23:31:45 +00:00
$scope . getCommandKind = function ( title ) {
2014-02-13 02:16:11 +00:00
var space = title . indexOf ( ' ' ) ;
return title . substring ( 0 , space ) ;
} ;
2014-02-17 23:31:45 +00:00
$scope . getCommandTitleHtml = function ( title ) {
2014-02-13 02:16:11 +00:00
var space = title . indexOf ( ' ' ) ;
if ( space <= 0 ) {
return $sanitize ( title ) ;
}
2014-02-17 23:31:45 +00:00
var kind = $scope . getCommandKind ( title ) ;
2014-02-13 02:16:11 +00:00
var sanitized = $sanitize ( title . substring ( space + 1 ) ) ;
var handler = kindHandlers [ kind || '' ] ;
if ( handler ) {
return handler ( sanitized ) ;
} else {
return sanitized ;
}
2014-02-17 23:31:45 +00:00
} ;
}
} ;
return directiveDefinitionObject ;
} ) ;
2014-02-13 02:16:11 +00:00
2014-02-17 23:31:45 +00:00
quayApp . directive ( 'dockerfileView' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/dockerfile-view.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'contents' : '=contents'
} ,
controller : function ( $scope , $element , $sanitize ) {
$scope . $watch ( 'contents' , function ( contents ) {
$scope . lines = [ ] ;
var lines = contents ? contents . split ( '\n' ) : [ ] ;
for ( var i = 0 ; i < lines . length ; ++ i ) {
var line = $ . trim ( lines [ i ] ) ;
var kind = 'text' ;
if ( line && line [ 0 ] == '#' ) {
kind = 'comment' ;
} else if ( line . match ( /^([A-Z]+\s)/ ) ) {
kind = 'command' ;
}
var lineInfo = {
'text' : $sanitize ( line ) ,
'kind' : kind
} ;
$scope . lines . push ( lineInfo ) ;
2014-02-13 02:16:11 +00:00
}
2014-02-17 23:31:45 +00:00
} ) ;
2014-02-13 02:16:11 +00:00
}
} ;
return directiveDefinitionObject ;
} ) ;
2014-02-17 23:31:45 +00:00
2013-10-26 20:03:11 +00:00
quayApp . directive ( 'buildStatus' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/build-status.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'build' : '=build'
} ,
controller : function ( $scope , $element ) {
2014-02-10 20:15:23 +00:00
}
} ;
return directiveDefinitionObject ;
} ) ;
quayApp . directive ( 'buildMessage' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/build-message.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
2014-02-13 02:16:11 +00:00
'phase' : '=phase'
2014-02-10 20:15:23 +00:00
} ,
controller : function ( $scope , $element ) {
2014-02-13 02:16:11 +00:00
$scope . getBuildMessage = function ( phase ) {
switch ( phase ) {
2014-03-25 17:29:06 +00:00
case 'cannot_load' :
return 'Cannot load build status - Please report this error' ;
2014-02-10 20:15:23 +00:00
case 'starting' :
case 'initializing' :
return 'Starting Dockerfile build' ;
case 'waiting' :
2014-02-13 02:16:11 +00:00
return 'Waiting for available build worker' ;
2014-02-10 20:15:23 +00:00
case 'building' :
return 'Building image from Dockerfile' ;
case 'pushing' :
return 'Pushing image built from Dockerfile' ;
2013-10-26 20:03:11 +00:00
2014-02-10 20:15:23 +00:00
case 'complete' :
return 'Dockerfile build completed and pushed' ;
case 'error' :
2014-02-13 02:16:11 +00:00
return 'Dockerfile build failed' ;
2014-02-10 20:15:23 +00:00
}
} ;
}
} ;
return directiveDefinitionObject ;
} ) ;
2013-10-26 20:03:11 +00:00
2014-02-10 20:15:23 +00:00
quayApp . directive ( 'buildProgress' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/build-progress.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'build' : '=build'
} ,
controller : function ( $scope , $element ) {
$scope . getPercentage = function ( buildInfo ) {
2014-02-06 00:59:57 +00:00
switch ( buildInfo . phase ) {
case 'building' :
return ( buildInfo . status . current _command / buildInfo . status . total _commands ) * 100 ;
break ;
2013-10-26 20:03:11 +00:00
2014-02-06 00:59:57 +00:00
case 'pushing' :
return buildInfo . status . push _completion * 100 ;
break ;
2013-10-26 20:03:11 +00:00
2014-02-06 00:59:57 +00:00
case 'complete' :
return 100 ;
break ;
case 'initializing' :
case 'starting' :
case 'waiting' :
2014-03-25 17:29:06 +00:00
case 'cannot_load' :
2014-02-06 00:59:57 +00:00
return 0 ;
break ;
}
return - 1 ;
} ;
2013-10-22 05:26:14 +00:00
}
} ;
return directiveDefinitionObject ;
} ) ;
2014-02-15 03:59:44 +00:00
2014-03-12 20:05:32 +00:00
quayApp . directive ( 'notificationView' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/notification-view.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'notification' : '=notification' ,
'parent' : '=parent'
} ,
2014-03-12 23:00:24 +00:00
controller : function ( $scope , $element , $location , UserService , NotificationService ) {
2014-03-12 20:05:32 +00:00
$scope . getMessage = function ( notification ) {
return NotificationService . getMessage ( notification ) ;
} ;
2014-03-12 23:00:24 +00:00
$scope . getGravatar = function ( orgname ) {
var organization = UserService . getOrganization ( orgname ) ;
return organization [ 'gravatar' ] || '' ;
} ;
2014-03-12 20:05:32 +00:00
$scope . parseDate = function ( dateString ) {
return Date . parse ( dateString ) ;
} ;
$scope . showNotification = function ( ) {
var url = NotificationService . getPage ( $scope . notification ) ;
if ( url ) {
var parts = url . split ( '?' )
$location . path ( parts [ 0 ] ) ;
if ( parts . length > 1 ) {
$location . search ( parts [ 1 ] ) ;
}
$scope . parent . $hide ( ) ;
}
} ;
$scope . getClass = function ( notification ) {
return NotificationService . getClass ( notification ) ;
} ;
}
} ;
return directiveDefinitionObject ;
} ) ;
2014-02-15 03:59:44 +00:00
quayApp . directive ( 'dockerfileBuildDialog' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/dockerfile-build-dialog.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'repository' : '=repository' ,
'showNow' : '=showNow' ,
'buildStarted' : '&buildStarted'
} ,
controller : function ( $scope , $element ) {
$scope . building = false ;
$scope . uploading = false ;
$scope . startCounter = 0 ;
$scope . handleBuildStarted = function ( build ) {
$ ( '#dockerfilebuildModal' ) . modal ( 'hide' ) ;
if ( $scope . buildStarted ) {
$scope . buildStarted ( { 'build' : build } ) ;
}
} ;
$scope . handleBuildFailed = function ( message ) {
$scope . errorMessage = message ;
} ;
$scope . startBuild = function ( ) {
$scope . errorMessage = null ;
$scope . startCounter ++ ;
} ;
$scope . $watch ( 'showNow' , function ( sn ) {
if ( sn && $scope . repository ) {
$ ( '#dockerfilebuildModal' ) . modal ( { } ) ;
}
} ) ;
}
} ;
return directiveDefinitionObject ;
} ) ;
quayApp . directive ( 'dockerfileBuildForm' , function ( ) {
var directiveDefinitionObject = {
priority : 0 ,
templateUrl : '/static/directives/dockerfile-build-form.html' ,
replace : false ,
transclude : false ,
restrict : 'C' ,
scope : {
'repository' : '=repository' ,
'startNow' : '=startNow' ,
'hasDockerfile' : '=hasDockerfile' ,
'uploadFailed' : '&uploadFailed' ,
'uploadStarted' : '&uploadStarted' ,
'buildStarted' : '&buildStarted' ,
'buildFailed' : '&buildFailed' ,
'missingFile' : '&missingFile' ,
'uploading' : '=uploading' ,
'building' : '=building'
} ,
controller : function ( $scope , $element , ApiService ) {
$scope . internal = { 'hasDockerfile' : false } ;
var handleBuildFailed = function ( message ) {
message = message || 'Dockerfile build failed to start' ;
var result = false ;
if ( $scope . buildFailed ) {
result = $scope . buildFailed ( { 'message' : message } ) ;
}
if ( ! result ) {
bootbox . dialog ( {
"message" : message ,
"title" : "Cannot start Dockerfile build" ,
"buttons" : {
"close" : {
"label" : "Close" ,
"className" : "btn-primary"
}
}
} ) ;
}
} ;
var handleUploadFailed = function ( message ) {
message = message || 'Error with file upload' ;
var result = false ;
if ( $scope . uploadFailed ) {
result = $scope . uploadFailed ( { 'message' : message } ) ;
}
if ( ! result ) {
bootbox . dialog ( {
"message" : message ,
"title" : "Cannot upload file for Dockerfile build" ,
"buttons" : {
"close" : {
"label" : "Close" ,
"className" : "btn-primary"
}
}
} ) ;
}
} ;
var handleMissingFile = function ( ) {
var result = false ;
if ( $scope . missingFile ) {
result = $scope . missingFile ( { } ) ;
}
if ( ! result ) {
bootbox . dialog ( {
"message" : 'A Dockerfile or an archive containing a Dockerfile is required' ,
"title" : "Missing Dockerfile" ,
"buttons" : {
"close" : {
"label" : "Close" ,
"className" : "btn-primary"
}
}
} ) ;
}
} ;
var startBuild = function ( fileId ) {
$scope . building = true ;
var repo = $scope . repository ;
var data = {
'file_id' : fileId
} ;
var params = {
'repository' : repo . namespace + '/' + repo . name
} ;
ApiService . requestRepoBuild ( data , params ) . then ( function ( resp ) {
$scope . building = false ;
$scope . uploading = false ;
if ( $scope . buildStarted ) {
$scope . buildStarted ( { 'build' : resp } ) ;
}
} , function ( resp ) {
$scope . building = false ;
$scope . uploading = false ;
handleBuildFailed ( resp . message ) ;
} ) ;
} ;
var conductUpload = function ( file , url , fileId , mimeType ) {
if ( $scope . uploadStarted ) {
$scope . uploadStarted ( { } ) ;
}
var request = new XMLHttpRequest ( ) ;
request . open ( 'PUT' , url , true ) ;
request . setRequestHeader ( 'Content-Type' , mimeType ) ;
request . onprogress = function ( e ) {
$scope . $apply ( function ( ) {
var percentLoaded ;
if ( e . lengthComputable ) {
$scope . upload _progress = ( e . loaded / e . total ) * 100 ;
}
} ) ;
} ;
request . onerror = function ( ) {
$scope . $apply ( function ( ) {
handleUploadFailed ( ) ;
} ) ;
} ;
request . onreadystatechange = function ( ) {
var state = request . readyState ;
if ( state == 4 ) {
$scope . $apply ( function ( ) {
startBuild ( fileId ) ;
$scope . uploading = false ;
} ) ;
return ;
}
} ;
request . send ( file ) ;
} ;
var startFileUpload = function ( repo ) {
$scope . uploading = true ;
$scope . uploading _progress = 0 ;
var uploader = $ ( '#file-drop' ) [ 0 ] ;
if ( uploader . files . length == 0 ) {
handleMissingFile ( ) ;
$scope . uploading = false ;
return ;
}
var file = uploader . files [ 0 ] ;
$scope . upload _file = file . name ;
var mimeType = file . type || 'application/octet-stream' ;
var data = {
'mimeType' : mimeType
} ;
var getUploadUrl = ApiService . getFiledropUrl ( data ) . then ( function ( resp ) {
conductUpload ( file , resp . url , resp . file _id , mimeType ) ;
} , function ( ) {
handleUploadFailed ( 'Could not retrieve upload URL' ) ;
} ) ;
} ;
$scope . $watch ( 'internal.hasDockerfile' , function ( d ) {
$scope . hasDockerfile = d ;
} ) ;
$scope . $watch ( 'startNow' , function ( ) {
if ( $scope . startNow && $scope . repository && ! $scope . uploading && ! $scope . building ) {
startFileUpload ( ) ;
}
} ) ;
}
} ;
return directiveDefinitionObject ;
} ) ;
2013-11-05 22:20:43 +00:00
// Note: ngBlur is not yet in Angular stable, so we add it manaully here.
quayApp . directive ( 'ngBlur' , function ( ) {
return function ( scope , elem , attrs ) {
elem . bind ( 'blur' , function ( ) {
scope . $apply ( attrs . ngBlur ) ;
} ) ;
} ;
} ) ;
2014-02-15 03:59:44 +00:00
quayApp . directive ( "filePresent" , [ function ( ) {
return {
restrict : 'A' ,
scope : {
'filePresent' : "="
} ,
link : function ( scope , element , attributes ) {
element . bind ( "change" , function ( changeEvent ) {
scope . $apply ( function ( ) {
scope . filePresent = changeEvent . target . files . length > 0 ;
} ) ;
} ) ;
}
}
} ] ) ;
2014-02-10 06:18:14 +00:00
quayApp . directive ( 'ngVisible' , function ( ) {
return function ( scope , element , attr ) {
scope . $watch ( attr . ngVisible , function ( visible ) {
element . css ( 'visibility' , visible ? 'visible' : 'hidden' ) ;
} ) ;
} ;
} ) ;
2014-02-05 01:50:13 +00:00
2014-03-19 18:27:33 +00:00
quayApp . run ( [ '$location' , '$rootScope' , 'Restangular' , 'UserService' , 'PlanService' , '$http' , '$timeout' , 'CookieService' ,
function ( $location , $rootScope , Restangular , UserService , PlanService , $http , $timeout , CookieService ) {
2013-12-28 19:07:44 +00:00
// Handle session security.
2014-01-27 18:58:12 +00:00
Restangular . setDefaultRequestParams ( [ 'post' , 'put' , 'remove' , 'delete' ] , { '_csrf_token' : window . _ _token || '' } ) ;
2013-12-28 19:07:44 +00:00
2013-12-11 22:50:48 +00:00
// Handle session expiration.
2013-11-12 00:26:56 +00:00
Restangular . setErrorInterceptor ( function ( response ) {
if ( response . status == 401 ) {
2014-03-19 18:04:42 +00:00
if ( response . data [ 'session_required' ] == null || response . data [ 'session_required' ] === true ) {
$ ( '#sessionexpiredModal' ) . modal ( { } ) ;
return false ;
}
2013-11-12 00:03:18 +00:00
}
2013-11-12 00:26:56 +00:00
2014-03-10 21:01:36 +00:00
if ( response . status == 500 ) {
document . location = '/500' ;
return false ;
}
2013-11-12 00:26:56 +00:00
return true ;
2013-11-12 00:03:18 +00:00
} ) ;
2013-12-11 22:50:48 +00:00
// Check if we need to redirect based on a previously chosen plan.
2014-03-19 18:27:33 +00:00
var result = PlanService . handleNotedPlan ( ) ;
// Check to see if we need to show a redirection page.
var redirectUrl = CookieService . get ( 'quay.redirectAfterLoad' ) ;
CookieService . clear ( 'quay.redirectAfterLoad' ) ;
if ( ! result && redirectUrl && redirectUrl . indexOf ( window . location . href ) == 0 ) {
window . location = redirectUrl ;
return ;
}
2013-12-11 22:50:48 +00:00
2013-12-21 03:38:53 +00:00
var changeTab = function ( activeTab , opt _timeout ) {
var checkCount = 0 ;
2013-12-10 04:33:28 +00:00
$timeout ( function ( ) {
2013-12-21 03:38:53 +00:00
if ( checkCount > 5 ) { return ; }
checkCount ++ ;
2013-12-10 04:33:28 +00:00
$ ( 'a[data-toggle="tab"]' ) . each ( function ( index ) {
var tabName = this . getAttribute ( 'data-target' ) . substr ( 1 ) ;
2013-12-21 03:38:53 +00:00
if ( tabName != activeTab ) {
return ;
}
if ( this . clientWidth == 0 ) {
changeTab ( activeTab , 500 ) ;
return ;
2013-12-10 04:33:28 +00:00
}
2013-12-21 03:38:53 +00:00
2014-01-10 01:17:28 +00:00
clickElement ( this ) ;
2013-12-10 04:33:28 +00:00
} ) ;
2013-12-21 03:38:53 +00:00
} , opt _timeout ) ;
2013-12-10 04:33:28 +00:00
} ;
var resetDefaultTab = function ( ) {
$timeout ( function ( ) {
$ ( 'a[data-toggle="tab"]' ) . each ( function ( index ) {
if ( index == 0 ) {
2014-01-10 01:17:28 +00:00
clickElement ( this ) ;
2013-12-10 04:33:28 +00:00
}
} ) ;
} ) ;
} ;
$rootScope . $on ( '$routeUpdate' , function ( ) {
if ( $location . search ( ) [ 'tab' ] ) {
changeTab ( $location . search ( ) [ 'tab' ] ) ;
} else {
resetDefaultTab ( ) ;
}
} ) ;
2013-09-26 23:07:25 +00:00
$rootScope . $on ( '$routeChangeSuccess' , function ( event , current , previous ) {
if ( current . $$route . title ) {
$rootScope . title = current . $$route . title ;
}
2013-11-19 00:03:35 +00:00
if ( current . $$route . description ) {
$rootScope . description = current . $$route . description ;
} else {
$rootScope . description = 'Hosted private docker repositories. Includes full user management and history. Free for public repositories.' ;
}
2013-11-20 21:17:47 +00:00
2013-11-24 01:03:08 +00:00
$rootScope . fixFooter = ! ! current . $$route . fixFooter ;
2013-12-10 04:33:28 +00:00
$rootScope . current = current . $$route ;
} ) ;
$rootScope . $on ( '$viewContentLoaded' , function ( event , current ) {
var activeTab = $location . search ( ) [ 'tab' ] ;
// Setup deep linking of tabs. This will change the search field of the URL whenever a tab
// is changed in the UI.
$ ( 'a[data-toggle="tab"]' ) . on ( 'shown.bs.tab' , function ( e ) {
var tabName = e . target . getAttribute ( 'data-target' ) . substr ( 1 ) ;
$rootScope . $apply ( function ( ) {
var isDefaultTab = $ ( 'a[data-toggle="tab"]' ) [ 0 ] == e . target ;
2014-02-20 23:57:49 +00:00
var newSearch = $ . extend ( $location . search ( ) , { } ) ;
if ( isDefaultTab ) {
delete newSearch [ 'tab' ] ;
} else {
newSearch [ 'tab' ] = tabName ;
}
$location . search ( newSearch ) ;
2013-12-10 04:33:28 +00:00
} ) ;
e . preventDefault ( ) ;
} ) ;
if ( activeTab ) {
changeTab ( activeTab ) ;
}
2013-09-26 23:07:25 +00:00
} ) ;
2013-11-18 22:11:06 +00:00
var initallyChecked = false ;
window . _ _isLoading = function ( ) {
if ( ! initallyChecked ) {
initallyChecked = true ;
return true ;
}
return $http . pendingRequests . length > 0 ;
} ;
2013-09-27 20:12:51 +00:00
} ] ) ;