Commits

ryanackley  committed fc60371

Tincr 2.0 still a work in progress

  • Participants
  • Parent commits 501caff

Comments (0)

Files changed (18)

File AtlassianPluginProject.js

 AtlassianPluginProject = function(){
 	this.resourceMap = {};
 	this.matchedFileMap = {};
+	this.cachedUrlMap = {};
 	this.locationType = 'local';
 };
 AtlassianPluginProject.prototype = {
 			}, function(){callback('Can\'t read ' + root.fullPath + '/atlassian-plugin.xml');}); 
 		}, function(){callback('Can\'t read ' + root.fullPath + '/pom.xml');});
 	},
-	filePathForUrl: function (url){
+	filePathsForUrl: function (url){
+		if (this.cachedUrlMap[url]){
+			return this.cachedUrlMap[url];
+		}
 		var resourceMap = this.resourceMap;
 		for (var end in resourceMap){
 			var idx = url.indexOf(end);
 				var file = resourceMap[end];
 				var filePath = file.root.fullPath + file.path;
 				this.matchedFileMap[filePath] = url;
-				return filePath;
+				var obj;
+				if (filePath.indexOf('.soy') == filePath.length - '.soy'.length){
+					obj = {autoReload: [filePath]};
+					this.cachedUrlMap[url] = obj;
+				}
+				else{
+					obj = {autoSave: filePath};
+					this.cachedUrlMap[url] = obj;
+				}
+				return obj;
 			}
 		}
-		return null;
+		return {};
 	},
 	urlsForFilePath : function(path){
 		var single = this.matchedFileMap[path];
 		}
 		return [];
 	},
+	mapDataToUrl : function(url, obj){
+		ProjectUtils.cacheRelationship(url, obj, true, this.cachedUrlMap, this.matchedFileMap);
+	},
 	resetUrls : function(){
 		this.matchedFileMap = {};
+		this.cachedUrlMap = {};
 	}
 }
 ProjectTypes.push(

File BackgroundMsgSupport.js

 */
 
 var backgroundMsgSupport = {
-	launchFileSelect : function(index, url,  callback){
-		chrome.extension.sendRequest({key: 'launchFileSelect', index: index, url: url}, function(response){
-			callback(response);
-		});
+	launchFolderSelect : function(index, url,  callback){
+		chrome.extension.sendRequest({key: 'launchFolderSelect', index: index, url: url}, callback);
+	},
+	launchFileSelect : function(url, op, callback){
+		chrome.extension.sendRequest({key: 'launchFileSelect', url: url, op: op}, callback);
 	},
 	getProjectTypes : function(callback){
-		chrome.extension.sendRequest({key : 'ProjectTypes'}, function(response){
-			callback(response);
-		});
+		chrome.extension.sendRequest({key : 'ProjectTypes'}, callback);
 	},
 	checkResources : function(resources, callback){
-		chrome.extension.sendRequest({key : 'checkResources', resources: resources}, function(response){
-			callback(response);
-		});
+		chrome.extension.sendRequest({key : 'checkResources', resources: resources}, callback);
 	},
 	checkResourceContent : function(url, content, callback){
-		chrome.extension.sendRequest({key : 'checkResourceContent', url: url, content:content}, function(response){
-			callback(response);
-		});
+		chrome.extension.sendRequest({key : 'checkResourceContent', url: url, content:content}, callback);
 	},
 	updateResource : function(url, content, callback){
-		chrome.extension.sendRequest({key : 'updateResource', url: url, content:content}, function(response){
-			callback(response);
-		});
+		chrome.extension.sendRequest({key : 'updateResource', url: url, content:content}, callback);
 	},
 	pageChanged : function(callback){
 		chrome.extension.sendRequest({key: 'pageChanged'}, callback);
 	unwatchDirectory : function(callback){
 		chrome.extension.sendRequest({key: 'unwatchDirectory'}, callback); 
 	},
-	loadProject : function(type, path, url, callback){
-		chrome.extension.sendRequest({key: 'loadProject', type: type, path: path, url: url}, function(response){
-			callback(response);
-		});
+	loadProject : function(type, path, url, urlPathMap, callback){
+		chrome.extension.sendRequest({key: 'loadProject', type: type, path: path, url: url, urlPathMap: urlPathMap}, callback);
 	}
 }

File ChromeExtensionProject.js

 */
 var UrlToDirectoryMappedProject = function(routes){
 	this.projectUrls = {};
+	this.cachedUrlMap = {};
 	this.routes = routes;
 	this.isWin = navigator.platform.indexOf('Win') == 0;
 }
 
 UrlToDirectoryMappedProject.prototype = {
-	filePathForUrl : function(rawUrl){
+	filePathsForUrl : function(rawUrl){
 		// strip any query strings
 		var url = rawUrl.split("?")[0];
-		
+		if (this.cachedUrlMap[url]){
+			return this.cachedUrlMap[url];
+		}
 		var routes = this.routes;
 		for (var i = 0; i < routes.length; i++){
 			if (url.indexOf(routes[i].from) == 0){
 				if (this.isWin){
 					path = path.replace(/\//g, '\\');
 				}
+
 				this.projectUrls[path] = rawUrl;
-				return path;
+				this.cachedUrlMap[url] = path;
+				return {autoSave: path};
 			}
 		}
+		return {};
 	},
 	urlsForFilePath : function(path){
 		var url = this.projectUrls[path]
 			return null;
 		}
 	},
+	mapDataToUrl : function(url, obj){
+		ProjectUtils.cacheRelationship(url, obj, true, this.cachedUrlMap, this.projectUrls);
+	},
 	resetUrls : function(){
 		this.projectUrls = {};
+		this.cachedUrlMap = {};
 	}
 }
 var chromeUrlRegex = /chrome-extension\:\/\/[a-zA-Z]+\//;

File ConfigFileBasedProject.js

 		this.fromFileRoutes = fromFileRoutes;
 	}
 	this.projectUrls = {};
+	this.cachedUrlMap = {};
 	this.rootPath = root.fullPath;
 	this.url = url;
 	if (isWin){
 		}
 		return null;
 	},
-	filePathForUrl : function(url){
+	filePathsForUrl : function(url){
+		if (this.cachedUrlMap[url]){
+			return this.cachedUrlMap[url];
+		}
 		var routes = this.toFileRoutes;
 		for (var i = 0; i < routes.length; i++){
 			var path = this._doRegexConversion(url, this.rootPath, routes[i].from, routes[i].to);
 			if (path){
 				this.projectUrls[this.replaceSlashes(path)] = url;
-				return path;
+				var obj = {autoSave: path};
+				this.cachedUrlMap[url] = obj;
+				return obj;
 			}
 		}
-		return null;
+		return {};
 	},
 	
 	urlsForFilePath : function(path){
 		var routes = this.fromFileRoutes;
 		for (var i = 0; i < routes.length; i++){
-			var url = this._doRegexConversion(path, this.url, routes[i].from, routes[i].to);
+			url = this._doRegexConversion(path, this.url, routes[i].from, routes[i].to);
 			if (url){
 				return [url];
 			}
 		}
 	 	return [];
 	},
+	mapDataToUrl : function(url, data){
+		// do nothing , the project type doesn't support customization
+	},
 	resetUrls : function(){
 		this.projectUrls = {};
+		this.cachedUrlMap = {};
 	}
 }

File FileUrlProject.js

 	this.projectUrls = {};
 };
 FileUrlProject.prototype = {
-	filePathForUrl : function(rawUrl){
+	filePathsForUrl : function(rawUrl){
 		// strip the query string assuming that not many people put 
 		// question marks in their file names
 		var url = rawUrl.split("?")[0];
 				path = path.substring(1);
 			}
 			this.projectUrls[path] = rawUrl;
-			return path;
+			return {autoSave : path};
 		}
-		return null;
+		return {};
 	},
 	urlsForFilePath : function(path){
 		// It's not necessary to cache the urls since it's just a matter of tacking on the 'file://' 
 		}
 		return [];
 	},
+	mapDataToUrl : function(){
+		//do nothing. this function is for allowing custom mappings but it's not supported for this project type
+	},
 	resetUrls : function(){
 		this.projectUrls = {};
 	}

File FileWatcher.js

 		if (this.isMyChange(path)){
 			return;
 		}
-		var urls = this.project.urlsForFilePath(path)
+		var urls = this.project.urlsForFilePath(path);
 		if (urls && urls.length){
+			// check if we should do any sass, less, etc. rendering here
+			var preProcess = this.project.preProcessForFilePath(path);
+			if (preProcess != null){
+				preProcess(path);
+			}
 			//var port = chrome.tabs.connect(this.tabId, {name: 'fileChange'});
 			var ran = Math.round(Math.random()*10000000);
 			var self = this;

File NPAPIFileIOforChrome.plugin/Contents/MacOS/NPAPIFileIOforChrome

Binary file modified.

File ProjectManager.js

 }
 
 ProjectManager.prototype = {
-    _initProject : function(tabId, path, url, projectType, sendResponse){
+    _initProject : function(tabId, path, url, projectType, urlDataMap, sendResponse){
     	var self = this;
     	var projectsByTab = this.projectsByTab;
     	this.cleanUp(tabId);
     	window.requestFileSystem(window.PERMANENT, 5*1024*1024*1024, function(fs){
 			fs.root.getDirectory(path, {create:false}, function(dir){
-				projectType.createProject(dir,url,function(project, error){
+				projectType.createProject(dir,url, function(project, error){
 					self.fsRoot = fs.root;
 					if (project){
-						project.matchedResourceMap = {};
+						// // we map resources to files only once on initial loading of the tincr panel then cache it in this data structure
+						// project.matchedResourceMap = {};
+
+						// // This is for custom mapped resources
+						// project.customResourceMap = urlPathMap || {};
+
+						// this tells the project type to store the custom mappings for auto reloading purposes
+						for (var url in urlDataMap){
+							project.mapDataToUrl(url, urlDataMap[url]);
+						}
+
 						if (!project.compare){
 							project.compare = function(a,b){
 								return a === b;
 		});
     },
     
-	launchFileSelect : function(tabId, url, typeIndex, sendResponse){
+	launchFolderSelect : function(tabId, url, typeIndex, sendResponse){
 		var self = this;
-		nativeFileSupport.launchFileSelect(function(path){
+		nativeFileSupport.launchFolderSelect(function(path){
 			if (path && path.length){
 				var projectType = ProjectTypes[typeIndex];
-				self._initProject(tabId, path, url, projectType, sendResponse);
+				self._initProject(tabId, path, url, projectType, {}, sendResponse);
 			}
 		});
 	},
-	
+
+	launchFileSelect : function(tabId, url, op, sendResponse){
+		var currentProject = this.projectsByTab[tabId];
+		if (currentProject){
+			nativeFileSupport.launchFileSelect(function(path){
+				if (path && path.length){
+					if (op == 'autosave'){
+						var data = {autoSave: path};
+						currentProject.mapDataToUrl(url, data);
+						sendResponse(data);
+					}
+					else if (op == 'add-autorefresh'){
+						var data = currentProject.filePathsForUrl(url);
+						if (data.autoSave){
+							delete data.autoSave;
+						}
+						data.autoReload = data.autoReload || [];
+						data.autoReload.push(path);
+						currentProject.mapDataToUrl(url, data);
+						sendResponse(data);
+					}
+				}
+			});
+		}
+	},
+
+	removeAutoRefresh : function(tabId, url, index, sendResponse){
+		var currentProject = this.projectsByTab[tabId];
+		var data = currentProject.filePathsForUrl(url);
+		var index = opData.index;
+		data.autoReload = data.autoReload || [];
+		if (data.autoReload[index]){
+			data.autoReload.splice(index, 1);
+		}
+		currentProject.mapDataToUrl(url, data);	
+		sendResponse(data);		
+	},
 	checkResources : function(tabId, urls, sendResponse){
 		var currentProject = this.projectsByTab[tabId];
 		var retVal = [];
 			for (var i = 0; i < urls.length; i++){
 				if (urls[i].type == 'script' || urls[i].type == 'stylesheet'){
 					var url = urls[i].url;
-				    var filePath = currentProject.filePathForUrl(url);
-				    if (filePath){
-				    	currentProject.matchedResourceMap[url] = filePath;
-				    }
-				    retVal.push(filePath != null);
+				    var obj = currentProject.filePathsForUrl(url);
+				    retVal.push(obj);
 				}
 				else{
-					retVal.push(false);
+					retVal.push(null);
 				}
 			}
 		}
 	checkResourceContent : function(tabId, url, content, sendResponse){
 		var currentProject = this.projectsByTab[tabId];
 		if (currentProject){
-			var path = currentProject.matchedResourceMap[url];
+			var obj = currentProject.filePathsForUrl(url);//currentProject.matchedResourceMap[url];
+			var path = obj.autoSave;
 			if (path){
 				Gito.FileUtils.readFile(this.fsRoot, path, 'Text', function(localContent){
 					if (!currentProject.compare(content, localContent)){
 						var ran = Math.round(Math.random()*10000000);
 						var handleError = function(){
 							sendResponse({success: false, msg: 'Content for url ' + url + ' doesn\'t match local file ' + path});
-							delete currentProject.matchedResourceMap[url];
+							
+							//TODO handle this on the front end with a warning.
+							//delete currentProject.matchedResourceMap[url];
 						}
 						
 						var handleContent = function(newContent){
 		var currentProject = this.projectsByTab[tabId];
 		var watcher = this.watchersByTab[tabId];
 		//currentProject.commitResource(url, content, currentWatcher, sendResponse);
-		var localPath = currentProject.matchedResourceMap[url];
+		var obj = currentProject.filePathsForUrl(url);
+		var localPath = obj.autoSaveP;
 		if (localPath){
 			watcher.addChangedByMe(localPath);
 			Gito.FileUtils.mkfile(this.fsRoot, localPath, content, sendResponse);
 			sendResponse();
 		}
 	},
-	loadProject : function(tabId, projectTypeKey, path, url, sendResponse){
+	loadProject : function(tabId, projectTypeKey, path, url, urlPathMap,sendResponse){
 		if (projectTypeKey == 'fileUrl'){
-			this._initProject(tabId, path, url, FileUrlProjectFactory, sendResponse);
+			this._initProject(tabId, path, url, FileUrlProjectFactory, {}, sendResponse);
 		}
 		else{		
 			for (var i = 0; i < ProjectTypes.length; i++){
 				if (ProjectTypes[i].key === projectTypeKey){
-					this._initProject(tabId, path, url, ProjectTypes[i], sendResponse);
+					this._initProject(tabId, path, url, ProjectTypes[i], urlPathMap, sendResponse);
 					break;
 				}
 			}
 	resetProject : function(tabId, sendResponse){
 		var currentProject = this.projectsByTab[tabId];
 		currentProject.resetUrls();
-		currentProject.matchedResourceMap = {};
 		sendResponse();
 	},
 	watchDirectory : function(port){
 			//sendResponse();
 		}
 	},
+	
 	unwatchDirectory : function(tabId, sendResponse){
 		this._unwatch(tabId);
 		sendResponse();

File ResourceChecker.js

 					if (data.content){
 						doResourceUpdate(resource, data.content);
 					}
+					$(window).trigger('resourceConfirm', [resource]);
 				}
 				else{
 					logError(data.msg);
 	
 	backgroundMsgSupport.checkResources(resources, function(data){
 		for (var i = 0; i < data.length; i++){
-			if (data[i]){
+			if (projectState.urlPathMap[resources[i].url]){
+				resources[i].paths = projectState.urlPathMap[resources[i].url];
+				$(window).trigger('resourceConfirm', [resource, true]);
+			}
+			else if (data[i]){
 				confirmResourceContent(resources[i]);
+				resources[i].paths = data[i];
 			}
 		}
 	});
 }
 
-var checkResources = function(callback){
+var checkResources = function(){
 	matchedUrls = {};
+	$(window).trigger('resourceRefresh');
 
 	chrome.devtools.inspectedWindow.getResources(function(resources){
+		$(window).trigger('resourceItem', [resources]);
 		resourceCache = resources;
 		matchResourcesWithProject(resources);
 	});
 			});
 		});
 		chrome.devtools.inspectedWindow.onResourceAdded.addListener(function(resource){
+			$(window).trigger('resourceItem', [[resource]]);
 			resourceCache.push(resource);
 			matchResourcesWithProject([resource]);
 		});

File RubyOnRailsProject.js

 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
+var isWin = navigator.platform.indexOf('Win') == 0;
+
+var RubyOnRailsProject = function(){
+	this.routes = [
+					{from: new RegExp('/assets/(.+\\.js)'),
+				     to: '/app/assets/javascripts/$1'},
+					{from: new RegExp('/assets/(.+\\.css)'),
+					 to: '/app/assets/stylesheets/$1'}
+					]
+	this.cachedUrlMap = {};
+	this.matchedFileMap = {};
+	if (isWin){
+		this.replaceSlashes = function(str){
+			return str.replace(/\//g, '\\');
+		}
+	}
+	else{
+		this.replaceSlashes = function(str){return str;};
+	}
+}
+
+RubyOnRailsProject.prototype = {
+	init : function(root, url, callback){
+		var knownPaths = this.knownPaths = {};
+		var directoriesToScan = ['/app/assets/javascripts', '/app/assets/stylesheets'];
+		this.rootPath = root.fullPath;
+
+		directoriesToScan.asyncEach(function(dirPath, done){
+			ProjectUtils.scanDirectory(root, dirPath, function(file){
+				knownPaths[file.fullPath] = true;
+			}, done);
+		}, callback);
+	},
+	_doRegexConversion : function(str, root, from, to){
+		var match = str.match(from);
+		if (match){
+			var partialPath = match[0].replace(from, to);
+			var path = root + (partialPath.charAt(0) == '/' ? '' : '/') + partialPath;
+			return path;
+		}
+		return null;
+	},
+	filePathsForUrl : function(url){
+		if (this.cachedUrlMap[url]){
+			return this.cachedUrlMap[url];
+		}
+		var routes = this.routes;
+		for (var i = 0; i < routes.length; i++){
+			var path = this._doRegexConversion(url, this.rootPath, routes[i].from, routes[i].to);
+			if (path){
+				var path = this.replaceSlashes(path);
+				if (!this.knownPaths[path]){
+					var exts = ['scss', 'coffee'];
+					for (var j = 0; j < exts.length; j++){
+						if (this.knownPaths[path + '.' + exts[j]]){
+							var newPath = path + '.' + exts[j];
+							var obj = {autoReload: [newPath]};
+							this.cachedUrlMap[url] = obj;
+							this.matchedFileMap[newPath] = url;
+							return obj;
+						}
+					}
+				}
+				else{
+					var obj = {autoSave: path};
+					this.cachedUrlMap[url] = obj;
+					this.matchedFileMap[path] = url;
+					return obj;
+				}
+			}
+		}
+		return {};
+	},
+	urlsForFilePath : function(path){
+		var single = this.matchedFileMap[path];
+		if (single){
+			return [single];
+		}
+		return [];
+	},
+	mapDataToUrl : function(url, obj){
+		ProjectUtils.cacheRelationship(url, obj, false, this.cachedUrlMap, this.matchedFileMap);
+	},
+	resetUrls : function(){
+		this.cachedUrlMap = {};
+		this.matchedFileMap = {};
+	}
+}
 
 ProjectTypes.push(
     {
 		key: 'ror3.1',
 		locationType : 'local',
 		createProject : function(root, url, callback){
-			config = {
-					toFile : [
-						{from: '/assets/(.+\\.js)',
-						 to: '/app/assets/javascripts/$1'},
-						{from: '/assets/(.+\\.css)',
-						 to: '/app/assets/stylesheets/$1'}
-					],
-					fromFile : [
-						{from: '(\\\\|/)app\\1assets\\1(?:javascripts|stylesheets)\\1(.+(\\.js|\\.css))(\\.[a-zA-Z]+)?$',
-						 to: '/assets/$2?body=1'}
-					]
-			};
 			
-			var project = new ConfigFileBasedProject(config, root, url);
+			var project = new RubyOnRailsProject();
 			var trailer = /\s*;\s*$/;
 			project.compare = function(remote,local){
 				if (remote != local){
 				}
 				return false;
 			}
-			callback(project);
+			
+			project.init(root, url, function(){
+				callback(project);
+			});			
 		} 
     }
 );

File background.html

     <title>Background Page</title>
     	<script src="backgroundInit.js"></script>
     	<script src="file_utils.js"></script>
+      <script src="ProjectUtils.js"></script>
     	<script src="ConfigFileBasedProject.js"></script>
 		<script src="RubyOnRailsProject.js"></script>
 		<script src="ChromeExtensionProject.js"></script>
 		<script src="ConfigFileBasedProjectLoader.js"></script>
 		<script src="ProjectManager.js"></script>
     	<embed type="application/x-npapifileioforchrome" id="pluginId" style="position:absolute; top:0px;left:-10000px;width:5px;height:5px;">
-		<script src="nativeSupport.js"></script>
-		<script src="FauxFilesystem.js"></script>
+        <script src="nativeSupport.js"></script>
+        <script src="FauxFilesystem.js"></script>
+        <embed name="nacl_module"
+           id="ruby"
+           width="0" height="0"
+           src="sass/ruby.nmf"
+           type="application/x-nacl" />
+        <script src="TincrSassSupport.js"></script>
 		<script src="background.js"></script>
   </head>
   <body>
+
   </body>
 </html>

File background.js

         //}
         sendResponse(ProjectTypes);
     }
+    else if (request.key == 'launchFolderSelect'){
+        projectManager.launchFolderSelect(sender.tab.id, request.url, request.index, sendResponse);
+    }
     else if (request.key == 'launchFileSelect'){
-        projectManager.launchFileSelect(sender.tab.id, request.url, request.index, sendResponse);
+        projectManager.launchFileSelect(sender.tab.id, request.url, request.op, sendResponse);
     }
     else if (request.key == 'checkResources'){
         projectManager.checkResources(sender.tab.id, request.resources, sendResponse);
         projectManager.resetProject(sender.tab.id, sendResponse);
     }
     else if (request.key == 'loadProject'){
-        projectManager.loadProject(sender.tab.id, request.type, request.path, request.url, sendResponse);
+        projectManager.loadProject(sender.tab.id, request.type, request.path, request.url, request.urlPathMap, sendResponse);
     }
     else if (request.key == 'unwatchDirectory'){
         projectManager.unwatchDirectory(sender.tab.id, sendResponse);
     
 });
 chrome.extension.onConnect.addListener(function(port){
-projectManager.watchDirectory(port);
+    projectManager.watchDirectory(port);
 });
 chrome.tabs.onRemoved.addListener(function(tabId, removeInfo) {
     projectManager.cleanUp(tabId);
 var watchPort;
 
 var saveProjectState = function(){
+	var storeData = {};
 	if (projectState.type != 'fileUrl'){
-		localStorage[inspectedLocation.origin] = JSON.stringify(projectState);
+		storeData[inspectedLocation.origin] = projectState;
+		chrome.storage.local.set(storeData);
 	}
 	else{
 		var temp = {autosave: projectState.autosave, watchFiles: projectState.watchFiles};
-		localStorage[inspectedLocation.origin] = JSON.stringify(temp);
+		storeDate[inspectedLocation.origin] = temp;
+		chrome.storage.local.set(storeData);
 	}
 }
 
-var loadProject = function(type, path, autosave, watchFiles){
-	checkResources();
+var saveMapping = function(url, path){
+	projectState.urlPathMap = projectState.urlPathMap || {};
+	projectState.urlPathMap[url] = path;
+	saveProjectState();
+};
+var loadProject = function(type, path, autosave, watchFiles, urlPathMap){
+	checkResources(urlPathMap);
 	registerNavListener();
 	
 	toggleWatchingFiles(watchFiles, path);
-	projectState = {type: type, path: path, autosave:autosave, watchFiles:watchFiles};
+	projectState = {type: type, path: path, autosave:autosave, watchFiles:watchFiles, urlPathMap: urlPathMap};
 	saveProjectState();
 };
 
 		inspectedLocation = location;
 		// file protocol is a special case
 		var type,path,temp;
+		var maybeLoadProject = function(){
+			var urlPathMap = temp.urlPathMap || {}
+			if (type && path && projectState.path != path){
+				backgroundMsgSupport.loadProject(type, path, inspectedLocation.origin, urlPathMap, function(data){
+					if (data.error){
+						logError(data.error);
+					}
+					else{
+						loadProject(type, path, temp.autosave, temp.watchFiles, urlPathMap);
+					}
+				});
+			}
+		}
 		if (location.protocol == 'file:'){
 			var pathElements = location.pathname.split("/");
 			type = 'fileUrl';
 			if (path.charAt(0) == '/' && navigator.platform.indexOf('Win') == 0){
 				path = path.substring(1);
 			}
-			var projectStateStr = localStorage[location.origin];
-			if (projectStateStr){
-				temp = JSON.parse(projectStateStr);
-			}
-			else{
-				temp = {autosave: true, watchFiles: true};
-			}
+			chrome.storage.local.get(String(location.origin), function(data){
+				temp = data[location.origin] || {autosave: true, watchFiles: true};
+				maybeLoadProject();
+			});
 		}
 		else{
-			var projectStateStr = localStorage[location.origin];
-			if (projectStateStr){
-				temp = JSON.parse(projectStateStr);
-				if (temp.type != 'fileUrl'){
-					type = temp.type;
-					path = temp.path;
-				}
-			}
-		}
-		if (type && path && projectState.path != path){
-			backgroundMsgSupport.loadProject(type, path, inspectedLocation.origin, function(data){
-				if (data.error){
-					logError(data.error);
-				}
-				else{
-					loadProject(type, path, temp.autosave, temp.watchFiles);
+			chrome.storage.local.get(String(location.origin), function(data){
+				temp = data[location.origin];
+				if (temp){
+					if (temp.type != 'fileUrl'){
+						type = temp.type;
+						path = temp.path;
+					}
+					maybeLoadProject();
 				}
 			});
 		}
+		 
 	});
 }
+//window.setTimeout(function(){
 checkLocation();
-toggleLogging(localStorage['logging'] === 'true');
+//}, 10000);
+
+chrome.storage.local.get('logging', function(data){
+	toggleLogging(data['logging'] === 'true');
+});
+
+$(window).bind('resourceRefresh', function(){
+	if (!editorPanelWindow){
+		return;
+	}
+	editorPanelWindow.clearResources();
+});
+
+$(window).bind('resourceItem', function(e, resources){
+	if (!editorPanelWindow){
+		return;
+	}
+	editorPanelWindow.refreshResources(resources);
+});
+$(window).bind('resourceConfirm', function(e, resource, isUserMapped){
+	if (!editorPanelWindow){
+		return;
+	}
+	editorPanelWindow.updateResource(resource, isUserMapped);
+});
 
 chrome.devtools.panels.create(' Tincr ', 'icon.png', 'editorpanel.html', function(panel){
 	var windowInjector = function(panelWindow){

File editorpanel.css

 fieldset{
     border:none;
 }
-
-#project-select-form form.aui{
+body{
+    font-size:75%;
     font-family: "Lucida Grande", sans-serif;
+}
+h1, h2, h3 {
+    -webkit-user-select: none;
+    font-weight: normal;
+    line-height: 1;
+}
+h1 {
+    margin: 0;
+    padding: 21px 0 13px;
+    font-size: 1.5em;
+}
+h1::after {
+    -webkit-margin-end: 20px;
+    background-color: #eee;
+    content: ' ';
+    display: block;
+    height: 1px;
+    position: relative;
+    top: 13px;
+}
+#project-select-form form.aui{
     padding-left: 40px;
 }
 #native-browse{
 #support-block{
     margin-top: 40px;
     color: #555;
+}
+#resource-list-container{
+    position:relative;
+    bottom: 0;
+    overflow-y: auto;
+    padding-left: 40px;
+}
+#resource-list{
+    list-style: none;
+    margin: 0px;
+    padding: 0px;
+}
+.list-heading{
+    background: -webkit-gradient(linear, left top, left bottom, from(rgb(236, 236, 236)), to(rgb(217, 217, 217)));
+    border-right: 1px solid rgb(179, 179, 179);
+    border-bottom: 1px solid rgb(179, 179, 179);
+    
+    padding: 10px 5px;
+}
+.details > div{
+    margin: 5px 0px;
+}
+.resource{
+    padding-left: 40px;
+    margin-bottom: 15px;
+}
+
+.resource.script{
+    background: url('images/resourceJSIcon.png') no-repeat;
+}
+
+.resource.stylesheet{
+    background: url('images/resourceCSSIcon.png') no-repeat;
+}
+
+.resource.document{
+    background: url('images/resourceDocumentIcon.png') no-repeat;
+}
+.resource-name{
+    font-weight: 500;
+    font-size: 14px;
+}
+#resource-list-container .resource .resource-localpath button, #resource-list-container .resource .resource-reloadfrom button{
+    margin: 0px;
+    padding: 0px;
+    min-height: initial;
+    min-width: initial;
+    font-size: 85%;
+    margin-right: 7px;
+}
+.select label, .field-group label{
+
+    float: left;
+display: block;
+text-align: right;
+width: 100px;
+padding-top: 4px;
+margin-left: -110px;
+
+}
+
+.select, .field-group{
+    margin-bottom:10px;
+    padding-left: 110px;
+}
+
+#toggle-autosave, #toggle-watch{
+    margin-top: 7px;
+}
+
+
+.description{
+    font-size: 85%;
+}
+
+.resource-reloadfrom ul {
+     list-style-type: none;
+     background: url(vline.png) repeat-y;
+     margin: 0;
+     padding: 0;
+     margin-top: 5px;
+     margin-left: 10px;
+   }
+   
+
+.resource-reloadfrom ul li {
+     margin: 0;
+     padding: 0 12px;
+     
+     background: url(node.png) no-repeat;
+}
+
+.resource-reloadfrom ul li:last-child {
+ background: #fff url(lastnode.png) no-repeat;
 }

File editorpanel.html

 <html>
 <head>
-	<link rel="stylesheet" type="text/css" href="aui.css" />
+	<link rel="stylesheet" type="text/css" href="widgets.css" />
 	<link rel="stylesheet" type="text/css" href="editorpanel.css" />
 
 	<script src="zepto1.0rc1.js"></script>
 	<script src="BackgroundMsgSupport.js"></script>
 	<script src="Logger.js"></script>
 	<script src="editorpanel.js"></script>
+	<script src="bootstrap-list.js"></script>
 </head>
 <body>
 	<div id="project-select-form">
 		
 		<form class="aui">
 
-			 <h2>Configuration</h2>
+			 <h3>Configuration</h3>
 		 <fieldset>
-			<div class="field-group">
+			<div class="select">
 				<label>Project Type:</label>
 				<select id="project-type">
 					<option value="-1" selected="selected">Choose...</option>
 				<div class="description">Save changes from devtools to the file system</div>
 			</div>
 		</fieldset>
-		    <h2>Flags</h2>
-			<div class="field-group">
-				<label>Enable Logging</label>
-				<input type="checkbox" id="toggle-logging"/>
-				<div class="description">Logs Tincr errors and messages to the devtools console</div>
-			</div>
+		    <h3>Flags</h3>
+		    <fieldset>
+				<div class="checkbox">
+					<input type="checkbox" id="toggle-logging"/>
+					<label>Enable Logging</label>
+					<!-- <div class="description">Logs Tincr errors and messages to the devtools console</div> -->
+				</div>
+			</fieldset>
 			<h4 id="support-block">
 			Need help or support? Check out the <a target="_blank" href="https://groups.google.com/forum/#!forum/tincr-for-chrome-devtools">Tincr google group.</a>
 		</h4>
 		</form>
 	</div>
-	
+	<div id="resource-list-container">
+		
+	</div>
+	<!-- <div>
+		<div class="left-panel">
+			<div class="list-heading">URL Path</div>
+			<div id="resource-list-container">
+				<ul id="resource-list" tabindex="0">
+				</ul>
+			</div>
+		</div>
+		<div class="right-panel">
+			<form class="form-horizontal">
+			</form>
+		</div>
+	</div> -->
 </body>
 </html>

File editorpanel.js

         $('#load-success').show();
     }
     
-    var loggingEnabled = devtoolsWindow.localStorage['logging'] === 'true';
-    toggleLogging(loggingEnabled);
-    $('#toggle-logging')[0].checked = loggingEnabled;
+    chrome.storage.local.get('logging', function(data){
+        var loggingEnabled = data['logging'] === 'true';
+        toggleLogging(loggingEnabled);
+        $('#toggle-logging')[0].checked = loggingEnabled;
+    })
+    
     
 }
+var clearResources = function(){
+    $('#resource-list-container').empty();
+};
+var updateResource = function(resource, isUserMapped){
+    var paths = resource.paths || {};
+    var $item = $('.details[data-url="' + resource.url +'"]');
+    updateResourceData($item, paths, isUserMapped);
+};
+var updateResourceData = function($item, paths, isUserMapped){
+    if (isUserMapped){
+        $item.addClass('user-mapped');
+    }
+    else{
+        $item.removeClass('user-mapped');
+    }
+
+    $('.resource-localpath span', $item).text(paths.autoSave ? paths.autoSave : 'None');
+    $('.auto-refresh-item', $item).remove();
+    if (paths.autoReload){
+        for (var j = 0; j < paths.autoReload.length; j++){
+            $('.resource-reloadfrom li:last-child', $item).before('<li class="auto-refresh-item">' + paths.autoReload[j] + '&nbsp;<a data-index="' + j + '" href="#" class="remove-file">Remove</a></li>');
+        }
+    }
+};
+var refreshResources = function(resourceCache){
+    var urlParser = document.createElement('a');
+    var inspectedOrigin = devtoolsWindow.inspectedLocation.origin;
+    var $resourceList = $('#resource-list-container');
+    for (var i = 0; i < resourceCache.length; i++){
+        var resource = resourceCache[i];
+        var type = resource.type;
+        if ((type != 'document' && type != 'script' && type != 'stylesheet') || resource.url.indexOf(inspectedOrigin) != 0){
+            continue;
+        }
+        var template = '<div class="resource ' + type +'">\
+            <div class="details">\
+                <div class="resource-name">test.js</div>\
+                <div class="resource-url">Url: /something/something/test.js</div>\
+                <div class="resource-localpath">Auto-Save Path: <span>None</span>&nbsp;<a href="#" class="browse-autosave-file">Change</a></div>\
+                <div class="resource-reloadfrom">Auto-Refresh Paths \
+                    <ul>\
+                        <li><a href="#" class="browse-add-autorefresh-file">Add</a></li>\
+                    </ul>\
+                </div>\
+                <div class="resource-options">\
+                    <label>\
+                        <input type="checkbox" />\
+                        <span>Auto-Refresh performs a full refresh of the page</span>\
+                    </label>\
+                </div>\
+            </div>\
+        </div>';
+
+        urlParser.href = resource.url;
+        var filename = urlParser.pathname.substring(urlParser.pathname.lastIndexOf('/') + 1);
+        var url = urlParser.origin === inspectedOrigin ? resource.url.substring(inspectedOrigin.length) : resource.url;
+        var $item = $(template);
+        
+        $item.attr('data-url', url);
+        $('.resource-name', $item).text(filename.length ? filename : '/');
+        $('.resource-url', $item).text('URL: ' + url);
+
+        var paths = resource.paths || {};
+        updateResourceData($item, paths, false);
+        
+
+        // deal with a change to the 1-to-1 mapping
+        $('.browse-autosave-file', $item).click((function(url, $item){
+            return function(){
+                backgroundMsgSupport.launchFileSelect(url, 'autosave', function(data){
+                    //$('.resource-localpath span', $item).text(data.localPath);
+                    updateResourceData($item, data, true);
+                    window.devtoolsWindow.saveMapping(url, data);
+                });
+            }
+        }(resource.url, $item)));
+
+        // deal with adding a file for auto refresh
+        $('.browse-add-autorefresh-file', $item).click((function(url, $item){
+            return function(){
+                backgroundMsgSupport.launchFileSelect(url, 'add-autorefresh', function(data){
+                    updateResourceData($item, data, true);
+                    window.devtoolsWindow.saveMapping(url, data);
+                });
+            }
+        }(resource.url, $item)));
+
+        $item.appendTo($resourceList);
+    }
+};
 
 var initUI = function(){
 
     });
     $('#native-browse').on('click', function(e){
         var index = Number(typeSelect.value);
-        backgroundMsgSupport.launchFileSelect(index, window.devtoolsWindow.inspectedLocation.origin, function(result){
+        backgroundMsgSupport.launchFolderSelect(index, window.devtoolsWindow.inspectedLocation.origin, function(result){
             if (result.path && result.path.length){
                 $('#local-file-path').text(result.path).show();
                 if (result.error){
                 }
                 else{
                     var projectType = projectTypes[index];
-                    window.devtoolsWindow.loadProject(projectType.key, result.path, true, true);
+                    window.devtoolsWindow.loadProject(projectType.key, result.path, true, true, {});
                     $('#toggle-autosave')[0].checked = true;
                     $('#toggle-watch')[0].checked = true;
                     $('#auto-refresh').show();
         window.devtoolsWindow.saveProjectState();
     });
     $('#toggle-logging').on('change', function(e){
-        devtoolsWindow.localStorage['logging'] = '' + this.checked;
+        chrome.storage.local.set({'logging': this.checked});
         toggleLogging(this.checked);
         devtoolsWindow.toggleLogging(this.checked);
     });
+
     
+    refreshResources(window.devtoolsWindow.resourceCache)
+    //$('#resource-list-container').list()
+
 };

File manifest.json

   "background": {"page" :"background.html"},
   "icons": {"48": "tools_small.png",
             "128": "tools.png" },
-  "permissions": ["http://*/", "https://*/"],
+  "permissions": ["http://*/", "https://*/", "storage"],
   "plugins": [
     { "path": "NPAPIFileIOforChrome.plugin", "public": false },
     { "path": "npNPAPIFileIOforChrome.dll", "public": false },

File nativeFileSupportShim.js

-/*
-* Copyright 2012 Ryan Ackley (ryanackley@gmail.com)
-*
-* This file is part of Tincr.
-*
-* Tincr is free software: you can redistribute it and/or
-* modify it under the terms of the GNU General Public License
-* as published by the Free Software Foundation; either version 2
-* of the License, or (at your option) any later version.
-* 
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-* GNU General Public License for more details.
-* 
-* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-*/
-
-var backgroundMsgSupport = {
-	launchFileSelect : function(index, callback){
-		chrome.extension.sendRequest({key: 'launchFileSelect', index: index}, function(response){
-			callback(response);
-		});
-	},
-	getProjectTypes : function(callback){
-		chrome.extension.sendRequest({key : 'ProjectTypes'}, function(response){
-			callback(response);
-		});
-	},
-	checkResources : function(resources, callback){
-		chrome.extension.sendRequest({key : 'checkResources', resources: resources}, function(response){
-			callback(response);
-		});
-	},
-	checkResourceContent : function(url, content, callback){
-		chrome.extension.sendRequest({key : 'checkResourceContent', url: url, content:content}, function(response){
-			callback(response);
-		});
-	},
-	updateResource : function(url, content, callback){
-		chrome.extension.sendRequest({key : 'updateResource', url: url, content:content}, function(response){
-			callback(response);
-		});
-	},
-	pageChanged : function(callback){
-		chrome.extension.sendRequest({key: 'pageChanged'}, callback);
-	},
-	watchDirectory : function(fullPath, callback){
-		chrome.extension.sendRequest({key: 'watchDirectory', path : fullPath}, function(response){
-			callback(response);
-		});
-	},
-	loadProject : function(type, path, callback){
-		chrome.extension.sendRequest({key: 'loadProject', type: type, path: path}, function(response){
-			callback(response);
-		});
-	}
-}