Index: build_tools/deploy.bat
===================================================================
--- build_tools/deploy.bat	(revision b61796f6bb674a8ff8ec2f45dcb597770c836daf)
+++ build_tools/deploy.bat	(revision b61796f6bb674a8ff8ec2f45dcb597770c836daf)
@@ -0,0 +1,9 @@
+@echo off
+cd "c:\program files\putty"
+
+rem Copy new installer to the temp directory on the test server
+pscp -scp -v -i sasview.ppk C:\jenkins\workspace\SasView_Win7\sasview\sasview\Output\setupSasView.exe sasview@192.168.1.18:AppData\Local\Temp\
+rem Execute the test on the remote server
+plink -i sasview.ppk -ssh sasview@192.168.1.18 c:\\jenkins\\test_deployment.bat
+
+cd c:\jenkins
Index: build_tools/sasview_deploy_test.au3
===================================================================
--- build_tools/sasview_deploy_test.au3	(revision 55bbe0b27a20e96052e70b3b233bcf5dff44cea3)
+++ build_tools/sasview_deploy_test.au3	(revision 55bbe0b27a20e96052e70b3b233bcf5dff44cea3)
@@ -0,0 +1,160 @@
+; ****** REMEMBER TO SWITCH OFF UAC ON TARGET MACHINE!!! *********
+;
+; This script will install, run and uninstall SasView
+; given location of the installer. The exit code of the script
+; points at failure location:
+;
+; 0 - OK
+; 1 - Installer failure
+; 2 - Problems running SasView (simple fitting)
+; 3 - Uninstaller failure
+
+#include <Constants.au3>
+#include <FileConstants.au3>
+#include <MsgBoxConstants.au3>
+#include <WinAPIFiles.au3>
+
+; Modifiable globals
+Global $fInstallerLocation = @TempDir & "\setupSasView.exe"
+if $CmdLine[0] > 0 Then
+   ; If argument present - use it as local download path
+   $fInstallerLocation = $CmdLine[1]
+EndIf
+
+Global $fUninstallerLocation = "C:\Program Files (x86)\SasView\unins000.exe"
+Global $lTimeout = 10          ; 10 sec timeout for waiting on windows
+Global $lInstallTimeout = 120  ; 2 min timeout for the installation process
+
+; General globals
+Global $installerPID = 0
+
+;; MAIN SCRIPT
+Install()
+RunSasView()
+Uninstall()
+Exit(0)
+
+;==============================================================
+
+Func Install()
+   ;;;;; APPLICATION INSTALLED ;;;;;;;
+   Local $sSetupWindow = "Setup - SasView"
+   Local $iFailFlag = 1
+   ; Run setup
+   if FileExists($fInstallerLocation) Then
+	  $installerPID = Run($fInstallerLocation)
+	  Assert($installerPID, $iFailFlag)
+	  Sleep(1000)
+   Else
+	  ;$Error = ObjEvent(AutoIt.Error, "Setup file does not exist","123")
+	  Exit($iFailFlag)
+   EndIf
+
+   ; License click through
+   WinActivate($sSetupWindow)
+   Local $test = WinWaitActive($sSetupWindow, "License Agreement", $lTimeout)
+   ;ConsoleWrite("license agreement: " & $test)
+   Assert($test, $iFailFlag)
+   sleep(1000)
+
+   Send("{TAB}{up}{ENTER}")
+
+   ; Location
+   Sleep(1000)
+   $test = WinWaitActive($sSetupWindow, "Select Destination Location", $lTimeout)
+   Assert($test, $iFailFlag)
+   Send("{ENTER}")
+
+   ; Icons, Startup entry
+   Sleep(1000)
+   $test = WinWaitActive($sSetupWindow, "Select Additional Tasks", $lTimeout)
+   Assert($test, $iFailFlag)
+   Send("{ENTER}")
+
+   ; Ready to install...
+   Sleep(1000)
+   $test = WinWaitActive($sSetupWindow, "Ready to Install", $lTimeout)
+   Assert($test, $iFailFlag)
+   Send("{ENTER}")
+
+   ; Final OK on running
+   Sleep(5000)
+   $test = WinWaitActive($sSetupWindow, "Completing the SasView Setup Wizard", $lInstallTimeout)
+   Assert($test, $iFailFlag)
+   Send("{ENTER}")
+   ;ConsoleWrite("Installed" & @CRLF)
+
+EndFunc
+
+
+Func RunSasView()
+   ;;;;; APPLICATION STARTED ;;;;;;;
+   ; Start app - DEBUG ONLY
+   ;;Run("C:\Program Files (x86)\SasView\SasView.exe")
+   Local $iFailFlag = 2
+   ; Wait for the window
+   Sleep(1000)
+   Local $hWnd = WinWaitActive("SasView  - Fitting -", "", $lTimeout)
+   Assert($hWnd, $iFailFlag)
+
+   ;;;;; Load a File
+   ; Open File Load dialog
+   Send("!{f}{ENTER}")
+   WinWaitActive("Choose a file", "", $lTimeout)
+   Assert($hWnd, $iFailFlag)
+   Sleep(200)
+
+   ; Focus is in file chooser - enter filename
+   Send("C:\Program Files (x86)\SasView\test\1d_data\cyl_400_20.txt")
+   Sleep(1000)
+   Send("{ENTER}")
+
+   ;; Send file to fitting
+   ControlClick($hWnd, "Send To", 231)
+
+   ;; Choose a model
+   ControlCommand($hWnd, "", "ComboBox3", "SetCurrentSelection", 1)
+
+   ;; Calculate the model
+   ControlClick($hWnd, "Compute", 211)
+
+   ;; Close SasView
+   WinClose($hWnd)
+
+   Local $hEnd = WinWaitActive("Confirm Exit", "", $lTimeout)
+   Assert($hEnd, $iFailFlag)
+   ControlClick($hEnd, "", "[CLASS:Button; INSTANCE:1]")
+
+   Local $sv_closed = WinWaitClose($hWnd, "", $lTimeout)
+   Assert($sv_closed, $iFailFlag)
+
+EndFunc
+
+Func Uninstall()
+;;;;; UNINSTALL ;;;;;;;
+   Local $iFailFlag = 3
+   $installerPID = Run($fUninstallerLocation)
+   Assert($installerPID, $iFailFlag)
+
+   Local $sSetupWindow = "SasView Uninstall"
+
+   Local $test = WinWaitActive($sSetupWindow, "", $lTimeout)
+   Assert($test, $iFailFlag)
+   Send("{TAB}{ENTER}")
+
+   WinActivate("SasView Uninstal")
+   $test = WinWaitActive($sSetupWindow, "SasView was successfully removed", $lTimeout)
+   Assert($test, $iFailFlag)
+   Send("{ENTER}")
+
+EndFunc
+
+
+;;; HELPER FUNCTIONS ;;;;
+Func Assert($test, $lExitValue)
+   ;;; Asserts $test to be non-zero and exit with code $lExitValue ;;;
+   if $test == 0 Then
+	  ProcessClose($installerPID)
+	  Exit($lExitValue)
+   EndIf
+EndFunc
Index: build_tools/test_deployment.bat
===================================================================
--- build_tools/test_deployment.bat	(revision b61796f6bb674a8ff8ec2f45dcb597770c836daf)
+++ build_tools/test_deployment.bat	(revision b61796f6bb674a8ff8ec2f45dcb597770c836daf)
@@ -0,0 +1,8 @@
+@echo off
+cd c:\jenkins
+
+rem sasview.exe is an AutoIt3 generated binary based on sasview_deploy_test.au3
+start /wait c:\jenkins\sasview.exe %1 %2
+
+echo %errorlevel%
+
Index: docs/sphinx-docs/build_sphinx.py
===================================================================
--- docs/sphinx-docs/build_sphinx.py	(revision eff1a8fcae5ef75ec5471c7b351cd73b602ce8b2)
+++ docs/sphinx-docs/build_sphinx.py	(revision 8096b4468bcb6bd95abe40059a38d5bd17d057a8)
@@ -35,4 +35,6 @@
 #/sasview-local-trunk/docs/sphinx-docs/build_sphinx.py
 SASMODELS_SOURCE_PROLOG = os.path.join(CURRENT_SCRIPT_DIR, "..", "..", "..", "sasmodels", "doc")
+SASMODELS_SOURCE_MAGNETISM = os.path.join(CURRENT_SCRIPT_DIR, "..", "..", "..", "sasmodels", "doc", "ref", "magnetism")
+SASMODELS_SOURCE_MAGIMG = os.path.join(CURRENT_SCRIPT_DIR, "..", "..", "..", "sasmodels", "doc", "ref", "magnetism", "mag_img")
 SASMODELS_SOURCE_REF_MODELS = os.path.join(CURRENT_SCRIPT_DIR, "..", "..", "..", "sasmodels", "doc", "ref", "models")
 SASMODELS_SOURCE_MODELS = os.path.join(CURRENT_SCRIPT_DIR, "..", "..", "..", "sasmodels", "doc", "model")
@@ -43,4 +45,5 @@
 SASMODELS_DEST_MODELS = os.path.join(CURRENT_SCRIPT_DIR, "source", "user", "models")
 SASMODELS_DEST_IMG = os.path.join(CURRENT_SCRIPT_DIR,  "source", "user", "model-imgs", "new-models")
+SASMODELS_DEST_MAGIMG = os.path.join(CURRENT_SCRIPT_DIR,  "source", "user", "mag_img")
 SASMODELS_DEST_BUILDIMG = os.path.join(CURRENT_SCRIPT_DIR,  "source", "user", "models", "img")
 
@@ -136,5 +139,5 @@
 
             copy_tree(docs, dest_dir)
-            
+
     # Now pickup testdata_help.rst
 #    print os.path.abspath(SASVIEW_TEST)
@@ -143,7 +146,7 @@
     if os.path.exists(SASVIEW_TEST):
        print "Found docs folder at ", SASVIEW_TEST
-       shutil.copytree(SASVIEW_TEST, SPHINX_SOURCE_TEST)       
-
-    print "=== And the Sasmodels Docs ===" 
+       shutil.copytree(SASVIEW_TEST, SPHINX_SOURCE_TEST)
+
+    print "=== And the Sasmodels Docs ==="
     # Make sure we have the relevant images for the new sasmodels documentation
     # First(!) we'll make a local reference copy for SasView (/new-models will be cleaned each build)
@@ -174,5 +177,5 @@
             shutil.copy(fromhere,tohere)
         else: print "no source directorty",SASMODELS_SOURCE_AUTOIMG ,"was found"
-    
+
     # And the rst prolog with the unit substitutions
     if os.path.exists(SASMODELS_SOURCE_PROLOG):
@@ -186,4 +189,28 @@
                     tohere=os.path.join(SASMODELS_DEST_PROLOG,files)
                     shutil.copy(fromhere,tohere)
+
+    if os.path.exists(SASMODELS_SOURCE_MAGNETISM):
+        print "Found docs folder SASMODELS_SOURCE_MAGNETISM at ", SASMODELS_SOURCE_MAGNETISM
+        if os.path.exists(SASMODELS_DEST_REF_MODELS):
+            print "Found docs folder SASMODELS_DEST_REF_MODELS   at ", SASMODELS_DEST_REF_MODELS
+            print "Copying sasmodels model toctree files..."
+            for files in os.listdir(SASMODELS_SOURCE_MAGNETISM):
+                if files.endswith(".rst"):
+                    fromhere=os.path.join(SASMODELS_SOURCE_MAGNETISM,files)
+                    tohere=os.path.join(SASMODELS_DEST_REF_MODELS,files)
+                    shutil.copy(fromhere,tohere)
+
+    if os.path.exists(SASMODELS_SOURCE_MAGIMG):
+        print "Found img  folder SASMODELS_SOURCE_MAGIMG    at ", SASMODELS_SOURCE_MAGIMG
+        if not os.path.exists(SASMODELS_DEST_MAGIMG):
+            print "Missing docs folder SASMODELS_DEST_MAGIMG at ", SASMODELS_DEST_MAGIMG
+            os.makedirs(SASMODELS_DEST_MAGIMG)
+            print "created SASMODELS_DEST_MAGIMG at ", SASMODELS_DEST_MAGIMG
+        print "Copying sasmodels model auto-generated image files..."
+        for files in os.listdir(SASMODELS_SOURCE_MAGIMG):
+            fromhere=os.path.join(SASMODELS_SOURCE_MAGIMG,files)
+            tohere=os.path.join(SASMODELS_DEST_MAGIMG,files)
+            shutil.copy(fromhere,tohere)
+        else: print "no source directorty",SASMODELS_SOURCE_MAGIMG ,"was found"
 
     if os.path.exists(SASMODELS_SOURCE_REF_MODELS):
Index: sasview/installer_generator.py
===================================================================
--- sasview/installer_generator.py	(revision 6a698c0c5af344a6ab7262889158874408362b2f)
+++ sasview/installer_generator.py	(revision 09afe905cc9186fc6fe30cdfb86e1fd68ebe2949)
@@ -199,6 +199,4 @@
     msg += """Source: "dist\plugin_models\*";\tDestDir: "{userdesktop}\..\.sasview\plugin_models";\t"""
     msg += """Flags: recursesubdirs createallsubdirs\n""" 
-    msg += """Source: "dist\compiled_models\*";\tDestDir: "{userdesktop}\..\.sasmodels\compiled_models";\t"""
-    msg += """Flags: recursesubdirs createallsubdirs\n"""
     msg += """Source: "dist\config\custom_config.py";\tDestDir: "{userdesktop}\..\.sasview\config";\t""" 
     msg += """Flags: recursesubdirs createallsubdirs\n"""
Index: sasview/sasview.py
===================================================================
--- sasview/sasview.py	(revision c23f303448cfceb9e5a58e5d7da46258cdb3cbd8)
+++ sasview/sasview.py	(revision cde6e52c62a26bad1dd50f25b4fea01ed9ffce87)
@@ -99,4 +99,7 @@
 APP_NAME = 'SasView'
 
+from matplotlib import backend_bases
+backend_bases.FigureCanvasBase.filetypes.pop('pgf', None)
+
 class SasView():
     """
Index: sasview/setup_exe.py
===================================================================
--- sasview/setup_exe.py	(revision 98d89df2b4f3085f9a959747dbb596aabf5d50b8)
+++ sasview/setup_exe.py	(revision 09afe905cc9186fc6fe30cdfb86e1fd68ebe2949)
@@ -209,14 +209,4 @@
 data_files += guiframe.data_files()
 
-# precompile sas models into the sasview build path; doesn't matter too much
-# where it is so long as it is a place that will get cleaned up afterwards.
-import sasmodels.core
-dll_path = os.path.join(build_path, 'compiled_models')
-compiled_dlls = sasmodels.core.precompile_dlls(dll_path, dtype='double')
-
-# include the compiled models as data; coordinate the target path for the
-# data with installer_generator.py
-data_files.append(('compiled_models', compiled_dlls))
-
 import sasmodels
 data_files += sasmodels.data_files()
Index: src/sas/sascalc/dataloader/data_info.py
===================================================================
--- src/sas/sascalc/dataloader/data_info.py	(revision d72567eac270b5ba960e7bfa2dac6f9cbc461dbe)
+++ src/sas/sascalc/dataloader/data_info.py	(revision 45d766273b9962bed84bd4bb7cb2c0227b97686d)
@@ -445,5 +445,5 @@
         return len(self.name) == 0 and len(self.date) == 0 and len(self.description) == 0 \
             and len(self.term) == 0 and len(self.notes) == 0
-            
+
     def single_line_desc(self):
         """
@@ -451,5 +451,5 @@
         """
         return "%s %s %s" % (self.name, self.date, self.description)
-     
+
     def __str__(self):
         _str = "Process:\n"
@@ -1037,4 +1037,5 @@
         _str += "   Z-axis:       %s\t[%s]\n" % (self._zaxis, self._zunit)
         _str += "   Length:       %g \n" % (len(self.data))
+        _str += "   Shape:        (%d, %d)\n" % (len(self.y_bins), len(self.x_bins))
         return _str
 
@@ -1220,5 +1221,5 @@
         result.mask = numpy.append(self.mask, other.mask)
         if result.err_data is not None:
-            result.err_data = numpy.append(self.err_data, other.err_data) 
+            result.err_data = numpy.append(self.err_data, other.err_data)
         if self.dqx_data is not None:
             result.dqx_data = numpy.append(self.dqx_data, other.dqx_data)
@@ -1252,4 +1253,6 @@
         final_dataset.yaxis(data._yaxis, data._yunit)
         final_dataset.zaxis(data._zaxis, data._zunit)
+        final_dataset.x_bins = data.x_bins
+        final_dataset.y_bins = data.y_bins
     else:
         return_string = "Should Never Happen: _combine_data_info_with_plottable input is not a plottable1d or " + \
Index: src/sas/sascalc/dataloader/readers/cansas_constants.py
===================================================================
--- src/sas/sascalc/dataloader/readers/cansas_constants.py	(revision d398285d1a07fbcba8a3328a6db3de09fb8581df)
+++ src/sas/sascalc/dataloader/readers/cansas_constants.py	(revision 250fec92ea66b0770da33360a90f022e1d674072)
@@ -27,5 +27,4 @@
         return_me.current_level = self.CANSAS_FORMAT.get("SASentry")
         # Defaults for variable and datatype
-        return_me.ns_variable = "{0}.meta_data[\"{2}\"] = \"{1}\""
         return_me.ns_datatype = "content"
         return_me.ns_optional = True
@@ -38,13 +37,10 @@
                         return_me.current_level = \
                                 return_me.current_level.get("<any>", "")
-                    cl_variable = return_me.current_level.get("variable", "")
                     cl_datatype = return_me.current_level.get("storeas", "")
                     cl_units_optional = \
-                             return_me.current_level.get("units_required", "")
+                             return_me.current_level.get("units_optional", "")
                     # Where are how to store the variable for the given
                     # namespace CANSAS_CONSTANTS tree is hierarchical, so
                     # is no value, inherit
-                    return_me.ns_variable = cl_variable if cl_variable != "" \
-                        else return_me.ns_variable
                     return_me.ns_datatype = cl_datatype if cl_datatype != "" \
                         else return_me.ns_datatype
@@ -53,5 +49,4 @@
                                         else return_me.ns_optional
             except AttributeError:
-                return_me.ns_variable = "{0}.meta_data[\"{2}\"] = \"{1}\""
                 return_me.ns_datatype = "content"
                 return_me.ns_optional = True
@@ -75,23 +70,12 @@
     # The constants below hold information on where to store the CanSAS data
     # when loaded in using sasview
-    META_DATA = "{0}.meta_data[\"{2}\"] = \"{1}\""
-    ANY = {"variable" : "{0}.meta_data[\"{2}\"] = \'{1}\'",
-           "storeas" : "content",
-          }
-    TITLE = {"variable" : "{0}.title = \"{1}\""}
-    SASNOTE = {"variable" : "{0}.notes.append(\'{1}\')"}
-    SASPROCESS_TERM = {"variable" : None,
-                       "attributes" : {"unit" : {"variable" : None},
-                                       "name" : {"variable" : None}
-                                      }
-                      }
-    SASPROCESS_SASPROCESSNOTE = {"variable" : None,
-                                 "children" : {"<any>" : ANY}
-                                }
-    SASPROCESS = {"variable" : None,
-                  "children" : {"name" : {"variable" : "{0}.name = \'{1}\'"},
-                                "date" : {"variable" : "{0}.date = \'{1}\'"},
-                                "description" :
-                                {"variable" : "{0}.description = \'{1}\'"},
+    ANY = {"storeas" : "content"}
+    TITLE = {}
+    SASNOTE = {}
+    SASPROCESS_TERM = {"attributes" : {"unit" : {}, "name" : {}}}
+    SASPROCESS_SASPROCESSNOTE = {"children" : {"<any>" : ANY}}
+    SASPROCESS = {"children" : {"name" : {},
+                                "date" : {},
+                                "description" : {},
                                 "term" : SASPROCESS_TERM,
                                 "SASprocessnote" : SASPROCESS_SASPROCESSNOTE,
@@ -99,74 +83,40 @@
                                },
                  }
-    RUN = {"variable" : "{0}.run.append(\"{1}\")",
-           "attributes" : {"name" :
-                           {"variable" : "{0}.run_name[\"{3}\"] = \"{1}\""}}
-          }
-    SASDATA_IDATA_Q = {"variable" : "{0}.x = numpy.append({0}.x, {1})",
-                       "unit" : "x_unit",
-                       "attributes" : {"unit" :
-                                       {"variable" : "{0}.xaxis(\"Q\", \'{1}\')",
-                                        "storeas" : "content"
-                                       }
-                                      },
-                      }
-    SASDATA_IDATA_I = {"variable" : "{0}.y = numpy.append({0}.y, {1})",
-                       "unit" : "y_unit",
-                       "attributes" : {"unit" :
-                                       {"variable" : "{0}.yaxis(\"Intensity\", \'{1}\')",
-                                        "storeas" : "content"
-                                       }
-                                      },
-                      }
-    SASDATA_IDATA_IDEV = {"variable" : "{0}.dy = numpy.append({0}.dy, {1})",
+    RUN = {"attributes" : {"name" :{}}}
+    SASDATA_IDATA_Q = {"units_optional" : False,
+                       "storeas" : "float",
+                        "unit" : "x_unit",
+                       "attributes" : {"unit" : {"storeas" : "content"}},
+                      }
+    SASDATA_IDATA_I = {"units_optional" : False,
+                       "storeas" : "float",
+                        "unit" : "y_unit",
+                       "attributes" : {"unit" : {"storeas" : "content"}},
+                      }
+    SASDATA_IDATA_IDEV = {"units_optional" : False,
+                          "storeas" : "float",
                           "unit" : "y_unit",
-                          "attributes" : {"unit" :
-                                          {"variable" : META_DATA,
-                                           "storeas" : "content"
-                                          }
-                                         },
-                         }
-    SASDATA_IDATA_QDEV = {"variable" : "{0}.dx = numpy.append({0}.dx, {1})",
+                          "attributes" : {"unit" : {"storeas" : "content"}},
+                         }
+    SASDATA_IDATA_QDEV = {"units_optional" : False,
+                          "storeas" : "float",
                           "unit" : "x_unit",
-                          "attributes" : {"unit" :
-                                          {"variable" : META_DATA,
-                                           "storeas" : "content"
-                                          }
-                                         },
-                         }
-    SASDATA_IDATA_DQL = {"variable" : "{0}.dxl = numpy.append({0}.dxl, {1})",
+                          "attributes" : {"unit" : {"storeas" : "content"}},
+                         }
+    SASDATA_IDATA_DQL = {"units_optional" : False,
+                         "storeas" : "float",
                          "unit" : "x_unit",
-                         "attributes" : {"unit" :
-                                         {"variable" : META_DATA,
-                                          "storeas" : "content"
-                                         }
-                                        },
-                        }
-    SASDATA_IDATA_DQW = {"variable" : "{0}.dxw = numpy.append({0}.dxw, {1})",
+                         "attributes" : {"unit" : {"storeas" : "content"}},
+                        }
+    SASDATA_IDATA_DQW = {"units_optional" : False,
+                         "storeas" : "float",
                          "unit" : "x_unit",
-                         "attributes" : {"unit" :
-                                         {"variable" : META_DATA,
-                                          "storeas" : "content"
-                                         }
-                                        },
-                        }
-    SASDATA_IDATA_QMEAN = {"storeas" : "content",
-                           "unit" : "x_unit",
-                           "variable" : META_DATA,
-                           "attributes" : {"unit" : {"variable" : META_DATA}},
+                         "attributes" : {"unit" : {"storeas" : "content"}},
+                        }
+    SASDATA_IDATA_QMEAN = {"unit" : "x_unit",
+                           "attributes" : {"unit" : {}},
                           }
-    SASDATA_IDATA_SHADOWFACTOR = {"variable" : META_DATA,
-                                  "storeas" : "content",
-                                 }
-    SASDATA_IDATA = {"storeas" : "float",
-                     "units_optional" : False,
-                     "variable" : None,
-                     "attributes" : {"name" : {"variable" : META_DATA,
-                                               "storeas" : "content",
-                                              },
-                                     "timestamp" : {"variable" : META_DATA,
-                                                    "storeas" : "timestamp",
-                                                   }
-                                    },
+    SASDATA_IDATA_SHADOWFACTOR = {}
+    SASDATA_IDATA = {"attributes" : {"name" : {},"timestamp" : {"storeas" : "timestamp"}},
                      "children" : {"Q" : SASDATA_IDATA_Q,
                                    "I" : SASDATA_IDATA_I,
@@ -180,5 +130,5 @@
                                   }
                     }
-    SASDATA = {"attributes" : {"name" : {"variable" : META_DATA,}},
+    SASDATA = {"attributes" : {"name" : {}},
                "variable" : None,
                "children" : {"Idata" : SASDATA_IDATA,
@@ -186,36 +136,17 @@
                             }
               }
-    SASTRANSSPEC_TDATA_LAMDBA = {"variable" : "{0}.wavelength.append({1})",
+    SASTRANSSPEC_TDATA_LAMDBA = {"storeas" : "float",
                                  "unit" : "wavelength_unit",
-                                 "attributes" :
-                                 {"unit" :
-                                  {"variable" : "{0}.wavelength_unit = \"{1}\"",
-                                   "storeas" : "content"
-                                  }
-                                 }
+                                 "attributes" : {"unit" : {"storeas" : "content"}}
                                 }
-    SASTRANSSPEC_TDATA_T = {"variable" : "{0}.transmission.append({1})",
+    SASTRANSSPEC_TDATA_T = {"storeas" : "float",
                             "unit" : "transmission_unit",
-                            "attributes" :
-                            {"unit" :
-                             {"variable" : "{0}.transmission_unit = \"{1}\"",
-                              "storeas" : "content"
-                             }
-                            }
-                           }
-    SASTRANSSPEC_TDATA_TDEV = {"variable" :
-                               "{0}.transmission_deviation.append({1})",
+                            "attributes" : {"unit" : {"storeas" : "content"}}
+                           }
+    SASTRANSSPEC_TDATA_TDEV = {"storeas" : "float",
                                "unit" : "transmission_deviation_unit",
-                               "attributes" :
-                               {"unit" :
-                                {"variable" :
-                                 "{0}.transmission_deviation_unit = \"{1}\"",
-                                 "storeas" : "content"
-                                }
-                               }
-                              }
-    SASTRANSSPEC_TDATA = {"storeas" : "float",
-                          "variable" : None,
-                          "children" : {"Lambda" : SASTRANSSPEC_TDATA_LAMDBA,
+                               "attributes" : {"unit" :{"storeas" : "content"}}
+                              }
+    SASTRANSSPEC_TDATA = {"children" : {"Lambda" : SASTRANSSPEC_TDATA_LAMDBA,
                                         "T" : SASTRANSSPEC_TDATA_T,
                                         "Tdev" : SASTRANSSPEC_TDATA_TDEV,
@@ -223,87 +154,50 @@
                                        }
                          }
-    SASTRANSSPEC = {"variable" : None,
-                    "children" : {"Tdata" : SASTRANSSPEC_TDATA,
+    SASTRANSSPEC = {"children" : {"Tdata" : SASTRANSSPEC_TDATA,
                                   "<any>" : ANY,
                                  },
-                    "attributes" :
-                    {"name" :
-                     {"variable" : "{0}.name = \"{1}\""},
-                     "timestamp" :
-                     {"variable" : "{0}.timestamp = \"{1}\""},
-                    }
+                    "attributes" : {"name" :{}, "timestamp" : {},}
                    }
-    SASSAMPLE_THICK = {"variable" : "{0}.sample.thickness = {1}",
-                       "unit" : "sample.thickness_unit",
-                       "storeas" : "float",
-                       "attributes" :
-                       {"unit" :
-                        {"variable" : "{0}.sample.thickness_unit = \"{1}\"",
-                         "storeas" : "content"
-                        }
-                       },
-                      }
-    SASSAMPLE_TRANS = {"variable" : "{0}.sample.transmission = {1}",
-                       "storeas" : "float",
-                      }
-    SASSAMPLE_TEMP = {"variable" : "{0}.sample.temperature = {1}",
-                      "unit" : "sample.temperature_unit",
+    SASSAMPLE_THICK = {"unit" : "thickness_unit",
+                       "storeas" : "float",
+                       "attributes" : {"unit" :{}},
+                      }
+    SASSAMPLE_TRANS = {"storeas" : "float",}
+    SASSAMPLE_TEMP = {"unit" : "temperature_unit",
                       "storeas" : "float",
-                      "attributes" :
-                      {"unit" :
-                       {"variable" : "{0}.sample.temperature_unit = \"{1}\"",
-                        "storeas" : "content"
-                       }
-                      },
+                      "attributes" :{"unit" :{}},
                      }
-    SASSAMPLE_POS_ATTR = {"unit" : {"variable" :
-                                    "{0}.sample.position_unit = \"{1}\"",
-                                    "storeas" : "content"
-                                   }
-                         }
-    SASSAMPLE_POS_X = {"variable" : "{0}.sample.position.x = {1}",
-                       "unit" : "sample.position_unit",
+    SASSAMPLE_POS_ATTR = {"unit" : {}}
+    SASSAMPLE_POS_X = {"unit" : "position_unit",
                        "storeas" : "float",
                        "attributes" : SASSAMPLE_POS_ATTR
                       }
-    SASSAMPLE_POS_Y = {"variable" : "{0}.sample.position.y = {1}",
-                       "unit" : "sample.position_unit",
+    SASSAMPLE_POS_Y = {"unit" : "position_unit",
                        "storeas" : "float",
                        "attributes" : SASSAMPLE_POS_ATTR
                       }
-    SASSAMPLE_POS_Z = {"variable" : "{0}.sample.position.z = {1}",
-                       "unit" : "sample.position_unit",
+    SASSAMPLE_POS_Z = {"unit" : "position_unit",
                        "storeas" : "float",
                        "attributes" : SASSAMPLE_POS_ATTR
                       }
-    SASSAMPLE_POS = {"children" : {"variable" : None,
-                                   "x" : SASSAMPLE_POS_X,
+    SASSAMPLE_POS = {"children" : {"x" : SASSAMPLE_POS_X,
                                    "y" : SASSAMPLE_POS_Y,
                                    "z" : SASSAMPLE_POS_Z,
                                   },
                     }
-    SASSAMPLE_ORIENT_ATTR = {"unit" :
-                             {"variable" :
-                              "{0}.sample.orientation_unit = \"{1}\"",
-                              "storeas" : "content"
-                             }
-                            }
-    SASSAMPLE_ORIENT_ROLL = {"variable" : "{0}.sample.orientation.x = {1}",
-                             "unit" : "sample.orientation_unit",
+    SASSAMPLE_ORIENT_ATTR = {"unit" :{}}
+    SASSAMPLE_ORIENT_ROLL = {"unit" : "orientation_unit",
                              "storeas" : "float",
                              "attributes" : SASSAMPLE_ORIENT_ATTR
                             }
-    SASSAMPLE_ORIENT_PITCH = {"variable" : "{0}.sample.orientation.y = {1}",
-                              "unit" : "sample.orientation_unit",
+    SASSAMPLE_ORIENT_PITCH = {"unit" : "orientation_unit",
                               "storeas" : "float",
                               "attributes" : SASSAMPLE_ORIENT_ATTR
                              }
-    SASSAMPLE_ORIENT_YAW = {"variable" : "{0}.sample.orientation.z = {1}",
-                            "unit" : "sample.orientation_unit",
+    SASSAMPLE_ORIENT_YAW = {"unit" : "orientation_unit",
                             "storeas" : "float",
                             "attributes" : SASSAMPLE_ORIENT_ATTR
                            }
-    SASSAMPLE_ORIENT = {"variable" : None,
-                        "children" : {"roll" : SASSAMPLE_ORIENT_ROLL,
+    SASSAMPLE_ORIENT = {"children" : {"roll" : SASSAMPLE_ORIENT_ROLL,
                                       "pitch" : SASSAMPLE_ORIENT_PITCH,
                                       "yaw" : SASSAMPLE_ORIENT_YAW,
@@ -311,7 +205,6 @@
                        }
     SASSAMPLE = {"attributes" :
-                 {"name" : {"variable" : "{0}.sample.name = \"{1}\""},},
-                 "variable" : None,
-                 "children" : {"ID" : {"variable" : "{0}.sample.ID = \"{1}\""},
+                 {"name" : {},},
+                 "children" : {"ID" : {},
                                "thickness" : SASSAMPLE_THICK,
                                "transmission" : SASSAMPLE_TRANS,
@@ -319,33 +212,22 @@
                                "position" : SASSAMPLE_POS,
                                "orientation" : SASSAMPLE_ORIENT,
-                               "details" :
-                               {"variable" :
-                                "{0}.sample.details.append(\"{1}\")"},
+                               "details" : {},
                                "<any>" : ANY
                               },
                 }
-    SASINSTR_SRC_BEAMSIZE_ATTR = {"unit" :
-                                  "{0}.source.beam_size_unit = \"{1}\"",
-                                  "storeas" : "content"
-                                 }
-    SASINSTR_SRC_BEAMSIZE_X = {"variable" : "{0}.source.beam_size.x = {1}",
-                               "unit" : "source.beam_size_unit",
+    SASINSTR_SRC_BEAMSIZE_ATTR = {"unit" : ""}
+    SASINSTR_SRC_BEAMSIZE_X = {"unit" : "beam_size_unit",
                                "storeas" : "float",
                                "attributes" : SASINSTR_SRC_BEAMSIZE_ATTR
                               }
-    SASINSTR_SRC_BEAMSIZE_Y = {"variable" : "{0}.source.beam_size.y = {1}",
-                               "unit" : "source.beam_size_unit",
+    SASINSTR_SRC_BEAMSIZE_Y = {"unit" : "beam_size_unit",
                                "storeas" : "float",
                                "attributes" : SASINSTR_SRC_BEAMSIZE_ATTR
                               }
-    SASINSTR_SRC_BEAMSIZE_Z = {"variable" : "{0}.source.beam_size.z = {1}",
-                               "unit" : "source.beam_size_unit",
+    SASINSTR_SRC_BEAMSIZE_Z = {"unit" : "beam_size_unit",
                                "storeas" : "float",
                                "attributes" : SASINSTR_SRC_BEAMSIZE_ATTR
                               }
-    SASINSTR_SRC_BEAMSIZE = {"attributes" :
-                             {"name" : {"variable" :
-                                        "{0}.source.beam_size_name = \"{1}\""}},
-                             "variable" : None,
+    SASINSTR_SRC_BEAMSIZE = {"attributes" : {"name" : {}},
                              "children" : {"x" : SASINSTR_SRC_BEAMSIZE_X,
                                            "y" : SASINSTR_SRC_BEAMSIZE_Y,
@@ -353,57 +235,25 @@
                                           }
                             }
-    SASINSTR_SRC_WL = {"variable" : "{0}.source.wavelength = {1}",
-                       "unit" : "source.wavelength_unit",
-                       "storeas" : "float",
-                       "attributes" :
-                       {"unit" :
-                        {"variable" : "{0}.source.wavelength_unit = \"{1}\"",
-                         "storeas" : "content"
-                        },
+    SASINSTR_SRC_WL = {"unit" : "wavelength_unit",
+                       "storeas" : "float",
+                       "attributes" : {"unit" :{},
                        }
                       }
-    SASINSTR_SRC_WL_MIN = {"variable" : "{0}.source.wavelength_min = {1}",
-                           "unit" : "source.wavelength_min_unit",
+    SASINSTR_SRC_WL_MIN = {"unit" : "wavelength_min_unit",
                            "storeas" : "float",
-                           "attributes" :
-                           {"unit" :
-                            {"variable" :
-                             "{0}.source.wavelength_min_unit = \"{1}\"",
-                             "storeas" : "content"
-                            },
-                           }
+                           "attributes" : {"unit" :{"storeas" : "content"},}
                           }
-    SASINSTR_SRC_WL_MAX = {"variable" : "{0}.source.wavelength_max = {1}",
-                           "unit" : "source.wavelength_max_unit",
+    SASINSTR_SRC_WL_MAX = {"unit" : "wavelength_max_unit",
                            "storeas" : "float",
-                           "attributes" :
-                           {"unit" :
-                            {"variable" :
-                             "{0}.source.wavelength_max_unit = \"{1}\"",
-                             "storeas" : "content"
-                            },
-                           }
+                           "attributes" : {"unit" :{"storeas" : "content"},}
                           }
-    SASINSTR_SRC_WL_SPR = {"variable" : "{0}.source.wavelength_spread = {1}",
-                           "unit" : "source.wavelength_spread_unit",
+    SASINSTR_SRC_WL_SPR = {"unit" : "wavelength_spread_unit",
                            "storeas" : "float",
-                           "attributes" :
-                           {"unit" :
-                            {"variable" :
-                             "{0}.source.wavelength_spread_unit = \"{1}\"",
-                             "storeas" : "content"
-                            },
-                           }
+                           "attributes" : {"unit" : {"storeas" : "content"},}
                           }
-    SASINSTR_SRC = {"attributes" : {"name" : {"variable" :
-                                              "{0}.source.name = \"{1}\""}},
-                    "variable" : None,
-                    "children" : {"radiation" :
-                                  {"variable" :
-                                   "{0}.source.radiation = \"{1}\""},
+    SASINSTR_SRC = {"attributes" : {"name" : {}},
+                    "children" : {"radiation" : {},
                                   "beam_size" : SASINSTR_SRC_BEAMSIZE,
-                                  "beam_shape" :
-                                  {"variable" :
-                                   "{0}.source.beam_shape = \"{1}\""},
+                                  "beam_shape" : {},
                                   "wavelength" : SASINSTR_SRC_WL,
                                   "wavelength_min" : SASINSTR_SRC_WL_MIN,
@@ -412,26 +262,18 @@
                                  },
                    }
-    SASINSTR_COLL_APER_ATTR = {"unit" : {"variable" : "{0}.size_unit = \"{1}\"",
-                                         "storeas" : "content"
-                                        },
-                              }
-    SASINSTR_COLL_APER_X = {"variable" : "{0}.size.x = {1}",
-                            "unit" : "size_unit",
+    SASINSTR_COLL_APER_ATTR = {"unit" : {}}
+    SASINSTR_COLL_APER_X = {"unit" : "size_unit",
                             "storeas" : "float",
                             "attributes" : SASINSTR_COLL_APER_ATTR
                            }
-    SASINSTR_COLL_APER_Y = {"variable" : "{0}.size.y = {1}",
-                            "unit" : "size_unit",
+    SASINSTR_COLL_APER_Y = {"unit" : "size_unit",
                             "storeas" : "float",
                             "attributes" : SASINSTR_COLL_APER_ATTR
                            }
-    SASINSTR_COLL_APER_Z = {"variable" : "{0}.size.z = {1}",
-                            "unit" : "size_unit",
+    SASINSTR_COLL_APER_Z = {"unit" : "size_unit",
                             "storeas" : "float",
                             "attributes" : SASINSTR_COLL_APER_ATTR
                            }
-    SASINSTR_COLL_APER_SIZE = {"attributes" :
-                               {"unit" : {"variable" :
-                                          "{0}.size_unit = \"{1}\""}},
+    SASINSTR_COLL_APER_SIZE = {"attributes" : {"unit" : {}},
                                "children" : {"storeas" : "float",
                                              "x" : SASINSTR_COLL_APER_X,
@@ -441,163 +283,101 @@
                               }
     SASINSTR_COLL_APER_DIST = {"storeas" : "float",
-                               "attributes" :
-                               {"storeas" : "content",
-                                "unit" : {"variable" :
-                                          "{0}.distance_unit = \"{1}\""}
-                               },
-                               "variable" : "{0}.distance = {1}",
+                               "attributes" : {"unit" : {}},
                                "unit" : "distance_unit",
                               }
-    SASINSTR_COLL_APER = {"variable" : None,
-                          "attributes" : {"name" : {"variable" :
-                                                    "{0}.name = \"{1}\""},
-                                          "type" : {"variable" :
-                                                    "{0}.type = \"{1}\""},
-                                         },
+    SASINSTR_COLL_APER = {"attributes" : {"name" : {}, "type" : {}, },
                           "children" : {"size" : SASINSTR_COLL_APER_SIZE,
                                         "distance" : SASINSTR_COLL_APER_DIST
                                        }
                          }
-    SASINSTR_COLL = {"attributes" :
-                     {"name" : {"variable" : "{0}.name = \"{1}\""}},
-                     "variable" : None,
+    SASINSTR_COLL = {"attributes" : {"name" : {}},
                      "children" :
-                     {"length" :
-                      {"variable" : "{0}.length = {1}",
-                       "unit" : "length_unit",
-                       "storeas" : "float",
-                       "attributes" :
-                       {"storeas" : "content",
-                        "unit" : {"variable" : "{0}.length_unit = \"{1}\""}
-                       },
-                      },
-                      "aperture" : SASINSTR_COLL_APER,
-                     },
+                         {"length" :
+                          {"unit" : "length_unit",
+                           "storeas" : "float",
+                           "attributes" : {"storeas" : "content", "unit" : {}},
+                          },
+                          "aperture" : SASINSTR_COLL_APER,
+                         },
                     }
-    SASINSTR_DET_SDD = {"variable" : "{0}.distance = {1}",
+    SASINSTR_DET_SDD = {"storeas" : "float",
                         "unit" : "distance_unit",
-                        "attributes" :
-                        {"unit" :
-                         {"variable" : "{0}.distance_unit = \"{1}\"",
-                          "storeas" : "content"
-                         }
-                        },
+                        "attributes" : {"unit" :{}},
                        }
-    SASINSTR_DET_OFF_ATTR = {"unit" : {"variable" : "{0}.offset_unit = \"{1}\"",
-                                       "storeas" : "content"
-                                      },
-                            }
-    SASINSTR_DET_OFF_X = {"variable" : "{0}.offset.x = {1}",
+    SASINSTR_DET_OFF_ATTR = {"unit" : {"storeas" : "content" }}
+    SASINSTR_DET_OFF_X = {"storeas" : "float",
                           "unit" : "offset_unit",
                           "attributes" : SASINSTR_DET_OFF_ATTR
                          }
-    SASINSTR_DET_OFF_Y = {"variable" : "{0}.offset.y = {1}",
+    SASINSTR_DET_OFF_Y = {"storeas" : "float",
                           "unit" : "offset_unit",
                           "attributes" : SASINSTR_DET_OFF_ATTR
                          }
-    SASINSTR_DET_OFF_Z = {"variable" : "{0}.offset.z = {1}",
+    SASINSTR_DET_OFF_Z = {"storeas" : "float",
                           "unit" : "offset_unit",
                           "attributes" : SASINSTR_DET_OFF_ATTR
                          }
-    SASINSTR_DET_OFF = {"variable" : None,
-                        "children" : {"x" : SASINSTR_DET_OFF_X,
+    SASINSTR_DET_OFF = {"children" : {"x" : SASINSTR_DET_OFF_X,
                                       "y" : SASINSTR_DET_OFF_Y,
                                       "z" : SASINSTR_DET_OFF_Z,
                                      }
                        }
-    SASINSTR_DET_OR_ATTR = {"unit" : "{0}.orientation_unit = \"{1}\"",
-                            "storeas" : "content"
-                           }
-    SASINSTR_DET_OR_ROLL = {"variable" : "{0}.orientation.x = {1}",
+    SASINSTR_DET_OR_ATTR = {}
+    SASINSTR_DET_OR_ROLL = {"storeas" : "float",
                             "unit" : "orientation_unit",
                             "attributes" : SASINSTR_DET_OR_ATTR
                            }
-    SASINSTR_DET_OR_PITCH = {"variable" : "{0}.orientation.y = {1}",
+    SASINSTR_DET_OR_PITCH = {"storeas" : "float",
                              "unit" : "orientation_unit",
                              "attributes" : SASINSTR_DET_OR_ATTR
                             }
-    SASINSTR_DET_OR_YAW = {"variable" : "{0}.orientation.z = {1}",
+    SASINSTR_DET_OR_YAW = {"storeas" : "float",
                            "unit" : "orientation_unit",
                            "attributes" : SASINSTR_DET_OR_ATTR
                           }
-    SASINSTR_DET_OR = {"variable" : None,
-                       "children" : {"roll" : SASINSTR_DET_OR_ROLL,
+    SASINSTR_DET_OR = {"children" : {"roll" : SASINSTR_DET_OR_ROLL,
                                      "pitch" : SASINSTR_DET_OR_PITCH,
                                      "yaw" : SASINSTR_DET_OR_YAW,
                                     }
                       }
-    SASINSTR_DET_BC_X = {"variable" : "{0}.beam_center.x = {1}",
+    SASINSTR_DET_BC_X = {"storeas" : "float",
                          "unit" : "beam_center_unit",
-                         "attributes" :
-                         {"unit" : "{0}.beam_center_unit = \"{1}\"",
-                          "storeas" : "content"
-                         }
-                        }
-    SASINSTR_DET_BC_Y = {"variable" : "{0}.beam_center.y = {1}",
+                         "attributes" : {"storeas" : "content"}
+                        }
+    SASINSTR_DET_BC_Y = {"storeas" : "float",
                          "unit" : "beam_center_unit",
-                         "attributes" :
-                         {"unit" : "{0}.beam_center_unit = \"{1}\"",
-                          "storeas" : "content"
-                         }
-                        }
-    SASINSTR_DET_BC_Z = {"variable" : "{0}.beam_center.z = {1}",
+                         "attributes" : {"storeas" : "content"}
+                        }
+    SASINSTR_DET_BC_Z = {"storeas" : "float",
                          "unit" : "beam_center_unit",
-                         "attributes" :
-                         {"unit" : "{0}.beam_center_unit = \"{1}\"",
-                          "storeas" : "content"
-                         }
-                        }
-    SASINSTR_DET_BC = {"variable" : None,
-                       "children" : {"x" : SASINSTR_DET_BC_X,
+                         "attributes" : {"storeas" : "content"}
+                        }
+    SASINSTR_DET_BC = {"children" : {"x" : SASINSTR_DET_BC_X,
                                      "y" : SASINSTR_DET_BC_Y,
-                                     "z" : SASINSTR_DET_BC_Z,
-                                    }
-                      }
-    SASINSTR_DET_PIXEL_X = {"variable" : "{0}.pixel_size.x = {1}",
+                                     "z" : SASINSTR_DET_BC_Z,}
+                      }
+    SASINSTR_DET_PIXEL_X = {"storeas" : "float",
                             "unit" : "pixel_size_unit",
-                            "attributes" :
-                            {"unit" : "{0}.pixel_size_unit = \"{1}\"",
-                             "storeas" : "content"
-                            }
-                           }
-    SASINSTR_DET_PIXEL_Y = {"variable" : "{0}.pixel_size.y = {1}",
+                            "attributes" : {"storeas" : "content" }
+                           }
+    SASINSTR_DET_PIXEL_Y = {"storeas" : "float",
                             "unit" : "pixel_size_unit",
-                            "attributes" :
-                            {"unit" : "{0}.pixel_size_unit = \"{1}\"",
-                             "storeas" : "content"
-                            }
-                           }
-    SASINSTR_DET_PIXEL_Z = {"variable" : "{0}.pixel_size.z = {1}",
+                            "attributes" : {"storeas" : "content"}
+                           }
+    SASINSTR_DET_PIXEL_Z = {"storeas" : "float",
                             "unit" : "pixel_size_unit",
-                            "attributes" :
-                            {"unit" : "{0}.pixel_size_unit = \"{1}\"",
-                             "storeas" : "content"
-                            }
-                           }
-    SASINSTR_DET_PIXEL = {"variable" : None,
-                          "children" : {"x" : SASINSTR_DET_PIXEL_X,
+                            "attributes" : {"storeas" : "content"}
+                           }
+    SASINSTR_DET_PIXEL = {"children" : {"x" : SASINSTR_DET_PIXEL_X,
                                         "y" : SASINSTR_DET_PIXEL_Y,
                                         "z" : SASINSTR_DET_PIXEL_Z,
                                        }
                          }
-    SASINSTR_DET_SLIT = {"variable" : "{0}.slit_length = {1}",
+    SASINSTR_DET_SLIT = {"storeas" : "float",
                          "unit" : "slit_length_unit",
-                         "attributes" :
-                         {"unit" :
-                          {"variable" : "{0}.slit_length_unit = \"{1}\"",
-                           "storeas" : "content"
-                          }
-                         }
-                        }
-    SASINSTR_DET = {"storeas" : "float",
-                    "variable" : None,
-                    "attributes" : {"name" :
-                                    {"storeas" : "content",
-                                     "variable" : "{0}.name = \"{1}\"",
-                                    }
-                                   },
-                    "children" : {"name" : {"storeas" : "content",
-                                            "variable" : "{0}.name = \"{1}\"",
-                                           },
+                         "attributes" : {"unit" : {}}
+                        }
+    SASINSTR_DET = {"attributes" : {"name" : {"storeas" : "content"}},
+                    "children" : {"name" : {"storeas" : "content"},
                                   "SDD" : SASINSTR_DET_SDD,
                                   "offset" : SASINSTR_DET_OFF,
@@ -608,8 +388,6 @@
                                  }
                    }
-    SASINSTR = {"variable" : None,
-                "children" :
-                {"variable" : None,
-                 "name" : {"variable" : "{0}.instrument = \"{1}\""},
+    SASINSTR = {"children" :
+                {"name" : {},
                  "SASsource" : SASINSTR_SRC,
                  "SAScollimation" : SASINSTR_COLL,
@@ -619,9 +397,6 @@
     CANSAS_FORMAT = {"SASentry" :
                      {"units_optional" : True,
-                      "variable" : None,
                       "storeas" : "content",
-                      "attributes" : {"name" :
-                                      {"variable" :
-                                       "{0}.run_name[\"{3}\"] = \"{1}\""}},
+                      "attributes" : {"name" : {}},
                       "children" : {"Title" : TITLE,
                                     "Run" : RUN,
@@ -644,5 +419,4 @@
 
     current_level = ''
-    ns_variable = ''
     ns_datatype = ''
     ns_optional = True
@@ -650,5 +424,4 @@
     def __init__(self):
         self.current_level = {}
-        self.ns_variable = ''
         self.ns_datatype = "content"
         self.ns_optional = True
Index: src/sas/sascalc/dataloader/readers/cansas_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/cansas_reader.py	(revision 83b6408fbfaefa4819b955c697fef834d8f033f6)
+++ src/sas/sascalc/dataloader/readers/cansas_reader.py	(revision 5f26aa48a5e34765d73c20fa10024d5319567854)
@@ -14,5 +14,5 @@
 
 import logging
-import numpy
+import numpy as np
 import os
 import sys
@@ -20,13 +20,10 @@
 import inspect
 # For saving individual sections of data
-from sas.sascalc.dataloader.data_info import Data1D
-from sas.sascalc.dataloader.data_info import Collimation
-from sas.sascalc.dataloader.data_info import TransmissionSpectrum
-from sas.sascalc.dataloader.data_info import Detector
-from sas.sascalc.dataloader.data_info import Process
-from sas.sascalc.dataloader.data_info import Aperture
+from sas.sascalc.dataloader.data_info import Data1D, DataInfo, plottable_1D
+from sas.sascalc.dataloader.data_info import Collimation, TransmissionSpectrum, Detector, Process, Aperture
+from sas.sascalc.dataloader.data_info import combine_data_info_with_plottable as combine_data
 import sas.sascalc.dataloader.readers.xml_reader as xml_reader
 from sas.sascalc.dataloader.readers.xml_reader import XMLreader
-from sas.sascalc.dataloader.readers.cansas_constants import CansasConstants
+from sas.sascalc.dataloader.readers.cansas_constants import CansasConstants, CurrentLevel
 
 # The following 2 imports *ARE* used. Do not remove either.
@@ -34,11 +31,11 @@
 from xml.dom.minidom import parseString
 
-## TODO: Refactor to load multiple <SASData> as separate Data1D objects
-## TODO: Refactor to allow invalid XML, but give a useful warning when loaded
-
-_ZERO = 1e-16
 PREPROCESS = "xmlpreprocess"
 ENCODING = "encoding"
 RUN_NAME_DEFAULT = "None"
+INVALID_SCHEMA_PATH_1_1 = "{0}/sas/sascalc/dataloader/readers/schema/cansas1d_invalid_v1_1.xsd"
+INVALID_SCHEMA_PATH_1_0 = "{0}/sas/sascalc/dataloader/readers/schema/cansas1d_invalid_v1_0.xsd"
+INVALID_XML = "\n\nThe loaded xml file, {0} does not fully meet the CanSAS v1.x specification. SasView loaded " + \
+              "as much of the data as possible.\n\n"
 HAS_CONVERTER = True
 try:
@@ -52,46 +49,4 @@
 ALLOW_ALL = True
 
-# DO NOT REMOVE Called by outside packages:
-#    sas.sasgui.perspectives.invariant.invariant_state
-#    sas.sasgui.perspectives.fitting.pagestate
-def get_content(location, node):
-    """
-    Get the first instance of the content of a xpath location.
-
-    :param location: xpath location
-    :param node: node to start at
-
-    :return: Element, or None
-    """
-    nodes = node.xpath(location,
-                       namespaces={'ns': CANSAS_NS.get("1.0").get("ns")})
-    if len(nodes) > 0:
-        return nodes[0]
-    else:
-        return None
-
-# DO NOT REMOVE Called by outside packages:
-#    sas.sasgui.perspectives.fitting.pagestate
-def write_node(doc, parent, name, value, attr=None):
-    """
-    :param doc: document DOM
-    :param parent: parent node
-    :param name: tag of the element
-    :param value: value of the child text node
-    :param attr: attribute dictionary
-
-    :return: True if something was appended, otherwise False
-    """
-    if attr is None:
-        attr = {}
-    if value is not None:
-        node = doc.createElement(name)
-        node.appendChild(doc.createTextNode(str(value)))
-        for item in attr:
-            node.setAttribute(item, attr[item])
-        parent.appendChild(node)
-        return True
-    return False
-
 class Reader(XMLreader):
     """
@@ -101,10 +56,23 @@
         The CanSAS reader requires PyXML 0.8.4 or later.
     """
-    ##CanSAS version - defaults to version 1.0
+    ## CanSAS version - defaults to version 1.0
     cansas_version = "1.0"
     base_ns = "{cansas1d/1.0}"
+    cansas_defaults = None
+    type_name = "canSAS"
+    invalid = True
+    ## Log messages and errors
     logging = None
-    errors = None
-    type_name = "canSAS"
+    errors = set()
+    ## Namespace hierarchy for current xml_file object
+    names = None
+    ns_list = None
+    ## Temporary storage location for loading multiple data sets in a single file
+    current_datainfo = None
+    current_dataset = None
+    current_data1d = None
+    data = None
+    ## List of data1D objects to be sent back to SasView
+    output = None
     ## Wildcards
     type = ["XML files (*.xml)|*.xml", "SasView Save Files (*.svs)|*.svs"]
@@ -114,9 +82,384 @@
     allow_all = True
 
-    def __init__(self):
-        ## List of errors
-        self.errors = set()
+    def reset_state(self):
+        """
+        Resets the class state to a base case when loading a new data file so previous
+        data files do not appear a second time
+        """
+        self.current_datainfo = None
+        self.current_dataset = None
+        self.current_data1d = None
+        self.data = []
+        self.process = Process()
+        self.transspectrum = TransmissionSpectrum()
+        self.aperture = Aperture()
+        self.collimation = Collimation()
+        self.detector = Detector()
+        self.names = []
+        self.cansas_defaults = {}
+        self.output = []
+        self.ns_list = None
         self.logging = []
         self.encoding = None
+
+    def read(self, xml_file, schema_path="", invalid=True):
+        """
+        Validate and read in an xml_file file in the canSAS format.
+
+        :param xml_file: A canSAS file path in proper XML format
+        :param schema_path: A file path to an XML schema to validate the xml_file against
+        """
+        # For every file loaded, reset everything to a base state
+        self.reset_state()
+        self.invalid = invalid
+        # Check that the file exists
+        if os.path.isfile(xml_file):
+            basename, extension = os.path.splitext(os.path.basename(xml_file))
+            # If the file type is not allowed, return nothing
+            if extension in self.ext or self.allow_all:
+                # Get the file location of
+                self.load_file_and_schema(xml_file, schema_path)
+                self.add_data_set()
+                # Try to load the file, but raise an error if unable to.
+                # Check the file matches the XML schema
+                try:
+                    self.is_cansas(extension)
+                    self.invalid = False
+                    # Get each SASentry from XML file and add it to a list.
+                    entry_list = self.xmlroot.xpath(
+                            '/ns:SASroot/ns:SASentry',
+                            namespaces={'ns': self.cansas_defaults.get("ns")})
+                    self.names.append("SASentry")
+
+                    # Get all preprocessing events and encoding
+                    self.set_processing_instructions()
+
+                    # Parse each <SASentry> item
+                    for entry in entry_list:
+                        # Create a new DataInfo object for every <SASentry>
+
+
+                        # Set the file name and then parse the entry.
+                        self.current_datainfo.filename = basename + extension
+                        self.current_datainfo.meta_data["loader"] = "CanSAS XML 1D"
+                        self.current_datainfo.meta_data[PREPROCESS] = \
+                            self.processing_instructions
+
+                        # Parse the XML SASentry
+                        self._parse_entry(entry)
+                        # Combine datasets with datainfo
+                        self.add_data_set()
+                except RuntimeError:
+                    # If the file does not match the schema, raise this error
+                    invalid_xml = self.find_invalid_xml()
+                    invalid_xml = INVALID_XML.format(basename + extension) + invalid_xml
+                    self.errors.add(invalid_xml)
+                    # Try again with an invalid CanSAS schema, that requires only a data set in each
+                    base_name = xml_reader.__file__
+                    base_name = base_name.replace("\\", "/")
+                    base = base_name.split("/sas/")[0]
+                    if self.cansas_version == "1.1":
+                        invalid_schema = INVALID_SCHEMA_PATH_1_1.format(base, self.cansas_defaults.get("schema"))
+                    else:
+                        invalid_schema = INVALID_SCHEMA_PATH_1_0.format(base, self.cansas_defaults.get("schema"))
+                    self.set_schema(invalid_schema)
+                    try:
+                        if self.invalid:
+                            if self.is_cansas():
+                                self.output = self.read(xml_file, invalid_schema, False)
+                            else:
+                                raise RuntimeError
+                        else:
+                            raise RuntimeError
+                    except RuntimeError:
+                        x = np.zeros(1)
+                        y = np.zeros(1)
+                        self.current_data1d = Data1D(x,y)
+                        self.current_data1d.errors = self.errors
+                        return [self.current_data1d]
+        else:
+            self.output.append("Not a valid file path.")
+        # Return a list of parsed entries that dataloader can manage
+        return self.output
+
+    def _parse_entry(self, dom):
+        """
+        Parse a SASEntry - new recursive method for parsing the dom of
+            the CanSAS data format. This will allow multiple data files
+            and extra nodes to be read in simultaneously.
+
+        :param dom: dom object with a namespace base of names
+        """
+
+        frm = inspect.stack()[1]
+        if not self._is_call_local(frm):
+            self.reset_state()
+            self.add_data_set()
+            self.names.append("SASentry")
+            self.parent_class = "SASentry"
+        self._check_for_empty_data()
+        self.base_ns = "{0}{1}{2}".format("{", \
+                            CANSAS_NS.get(self.cansas_version).get("ns"), "}")
+        tagname = ''
+        tagname_original = ''
+
+        # Go through each child in the parent element
+        for node in dom:
+            attr = node.attrib
+            name = attr.get("name", "")
+            type = attr.get("type", "")
+            # Get the element name and set the current names level
+            tagname = node.tag.replace(self.base_ns, "")
+            tagname_original = tagname
+            # Skip this iteration when loading in save state information
+            if tagname == "fitting_plug_in" or tagname == "pr_inversion" or tagname == "invariant":
+                continue
+
+            # Get where to store content
+            self.names.append(tagname_original)
+            self.ns_list = CONSTANTS.iterate_namespace(self.names)
+            # If the element is a child element, recurse
+            if len(node.getchildren()) > 0:
+                self.parent_class = tagname_original
+                if tagname == 'SASdata':
+                    self._initialize_new_data_set()
+                ## Recursion step to access data within the group
+                self._parse_entry(node)
+                if tagname == "SASsample":
+                    self.current_datainfo.sample.name = name
+                elif tagname == "beam_size":
+                    self.current_datainfo.source.beam_size_name = name
+                elif tagname == "SAScollimation":
+                    self.collimation.name = name
+                elif tagname == "aperture":
+                    self.aperture.name = name
+                    self.aperture.type = type
+                self.add_intermediate()
+            else:
+                data_point, unit = self._get_node_value(node, tagname)
+
+                ## If this is a dataset, store the data appropriately
+                if tagname == 'Run':
+                    self.current_datainfo.run_name[data_point] = name
+                    self.current_datainfo.run.append(data_point)
+                elif tagname == 'Title':
+                    self.current_datainfo.title = data_point
+                elif tagname == 'SASnote':
+                    self.current_datainfo.notes.append(data_point)
+
+                ## I and Q Data
+                elif tagname == 'I':
+                    self.current_dataset.yaxis("Intensity", unit)
+                    self.current_dataset.y = np.append(self.current_dataset.y, data_point)
+                elif tagname == 'Idev':
+                    self.current_dataset.dy = np.append(self.current_dataset.dy, data_point)
+                elif tagname == 'Q':
+                    self.current_dataset.xaxis("Q", unit)
+                    self.current_dataset.x = np.append(self.current_dataset.x, data_point)
+                elif tagname == 'Qdev':
+                    self.current_dataset.dx = np.append(self.current_dataset.dx, data_point)
+                elif tagname == 'dQw':
+                    self.current_dataset.dxw = np.append(self.current_dataset.dxw, data_point)
+                elif tagname == 'dQl':
+                    self.current_dataset.dxl = np.append(self.current_dataset.dxl, data_point)
+                elif tagname == 'Qmean':
+                    pass
+                elif tagname == 'Shadowfactor':
+                    pass
+
+                ## Sample Information
+                elif tagname == 'ID' and self.parent_class == 'SASsample':
+                    self.current_datainfo.sample.ID = data_point
+                elif tagname == 'Title' and self.parent_class == 'SASsample':
+                    self.current_datainfo.sample.name = data_point
+                elif tagname == 'thickness' and self.parent_class == 'SASsample':
+                    self.current_datainfo.sample.thickness = data_point
+                    self.current_datainfo.sample.thickness_unit = unit
+                elif tagname == 'transmission' and self.parent_class == 'SASsample':
+                    self.current_datainfo.sample.transmission = data_point
+                elif tagname == 'temperature' and self.parent_class == 'SASsample':
+                    self.current_datainfo.sample.temperature = data_point
+                    self.current_datainfo.sample.temperature_unit = unit
+                elif tagname == 'details' and self.parent_class == 'SASsample':
+                    self.current_datainfo.sample.details.append(data_point)
+                elif tagname == 'x' and self.parent_class == 'position':
+                    self.current_datainfo.sample.position.x = data_point
+                    self.current_datainfo.sample.position_unit = unit
+                elif tagname == 'y' and self.parent_class == 'position':
+                    self.current_datainfo.sample.position.y = data_point
+                    self.current_datainfo.sample.position_unit = unit
+                elif tagname == 'z' and self.parent_class == 'position':
+                    self.current_datainfo.sample.position.z = data_point
+                    self.current_datainfo.sample.position_unit = unit
+                elif tagname == 'roll' and self.parent_class == 'orientation' and 'SASsample' in self.names:
+                    self.current_datainfo.sample.orientation.x = data_point
+                    self.current_datainfo.sample.orientation_unit = unit
+                elif tagname == 'pitch' and self.parent_class == 'orientation' and 'SASsample' in self.names:
+                    self.current_datainfo.sample.orientation.y = data_point
+                    self.current_datainfo.sample.orientation_unit = unit
+                elif tagname == 'yaw' and self.parent_class == 'orientation' and 'SASsample' in self.names:
+                    self.current_datainfo.sample.orientation.z = data_point
+                    self.current_datainfo.sample.orientation_unit = unit
+
+                ## Instrumental Information
+                elif tagname == 'name' and self.parent_class == 'SASinstrument':
+                    self.current_datainfo.instrument = data_point
+                ## Detector Information
+                elif tagname == 'name' and self.parent_class == 'SASdetector':
+                    self.detector.name = data_point
+                elif tagname == 'SDD' and self.parent_class == 'SASdetector':
+                    self.detector.distance = data_point
+                    self.detector.distance_unit = unit
+                elif tagname == 'slit_length' and self.parent_class == 'SASdetector':
+                    self.detector.slit_length = data_point
+                    self.detector.slit_length_unit = unit
+                elif tagname == 'x' and self.parent_class == 'offset':
+                    self.detector.offset.x = data_point
+                    self.detector.offset_unit = unit
+                elif tagname == 'y' and self.parent_class == 'offset':
+                    self.detector.offset.y = data_point
+                    self.detector.offset_unit = unit
+                elif tagname == 'z' and self.parent_class == 'offset':
+                    self.detector.offset.z = data_point
+                    self.detector.offset_unit = unit
+                elif tagname == 'x' and self.parent_class == 'beam_center':
+                    self.detector.beam_center.x = data_point
+                    self.detector.beam_center_unit = unit
+                elif tagname == 'y' and self.parent_class == 'beam_center':
+                    self.detector.beam_center.y = data_point
+                    self.detector.beam_center_unit = unit
+                elif tagname == 'z' and self.parent_class == 'beam_center':
+                    self.detector.beam_center.z = data_point
+                    self.detector.beam_center_unit = unit
+                elif tagname == 'x' and self.parent_class == 'pixel_size':
+                    self.detector.pixel_size.x = data_point
+                    self.detector.pixel_size_unit = unit
+                elif tagname == 'y' and self.parent_class == 'pixel_size':
+                    self.detector.pixel_size.y = data_point
+                    self.detector.pixel_size_unit = unit
+                elif tagname == 'z' and self.parent_class == 'pixel_size':
+                    self.detector.pixel_size.z = data_point
+                    self.detector.pixel_size_unit = unit
+                elif tagname == 'roll' and self.parent_class == 'orientation' and 'SASdetector' in self.names:
+                    self.detector.orientation.x = data_point
+                    self.detector.orientation_unit = unit
+                elif tagname == 'pitch' and self.parent_class == 'orientation' and 'SASdetector' in self.names:
+                    self.detector.orientation.y = data_point
+                    self.detector.orientation_unit = unit
+                elif tagname == 'yaw' and self.parent_class == 'orientation' and 'SASdetector' in self.names:
+                    self.detector.orientation.z = data_point
+                    self.detector.orientation_unit = unit
+                ## Collimation and Aperture
+                elif tagname == 'length' and self.parent_class == 'SAScollimation':
+                    self.collimation.length = data_point
+                    self.collimation.length_unit = unit
+                elif tagname == 'name' and self.parent_class == 'SAScollimation':
+                    self.collimation.name = data_point
+                elif tagname == 'distance' and self.parent_class == 'aperture':
+                    self.aperture.distance = data_point
+                    self.aperture.distance_unit = unit
+                elif tagname == 'x' and self.parent_class == 'size':
+                    self.aperture.size.x = data_point
+                    self.collimation.size_unit = unit
+                elif tagname == 'y' and self.parent_class == 'size':
+                    self.aperture.size.y = data_point
+                    self.collimation.size_unit = unit
+                elif tagname == 'z' and self.parent_class == 'size':
+                    self.aperture.size.z = data_point
+                    self.collimation.size_unit = unit
+
+                ## Process Information
+                elif tagname == 'name' and self.parent_class == 'SASprocess':
+                    self.process.name = data_point
+                elif tagname == 'description' and self.parent_class == 'SASprocess':
+                    self.process.description = data_point
+                elif tagname == 'date' and self.parent_class == 'SASprocess':
+                    try:
+                        self.process.date = datetime.datetime.fromtimestamp(data_point)
+                    except:
+                        self.process.date = data_point
+                elif tagname == 'SASprocessnote':
+                    self.process.notes.append(data_point)
+                elif tagname == 'term' and self.parent_class == 'SASprocess':
+                    unit = attr.get("unit", "")
+                    dic = {}
+                    dic["name"] = name
+                    dic["value"] = data_point
+                    dic["unit"] = unit
+                    self.process.term.append(dic)
+
+                ## Transmission Spectrum
+                elif tagname == 'T' and self.parent_class == 'Tdata':
+                    self.transspectrum.transmission = np.append(self.transspectrum.transmission, data_point)
+                    self.transspectrum.transmission_unit = unit
+                elif tagname == 'Tdev' and self.parent_class == 'Tdata':
+                    self.transspectrum.transmission_deviation = np.append(self.transspectrum.transmission_deviation, data_point)
+                    self.transspectrum.transmission_deviation_unit = unit
+                elif tagname == 'Lambda' and self.parent_class == 'Tdata':
+                    self.transspectrum.wavelength = np.append(self.transspectrum.wavelength, data_point)
+                    self.transspectrum.wavelength_unit = unit
+
+                ## Source Information
+                elif tagname == 'wavelength' and (self.parent_class == 'SASsource' or self.parent_class == 'SASData'):
+                    self.current_datainfo.source.wavelength = data_point
+                    self.current_datainfo.source.wavelength_unit = unit
+                elif tagname == 'wavelength_min' and self.parent_class == 'SASsource':
+                    self.current_datainfo.source.wavelength_min = data_point
+                    self.current_datainfo.source.wavelength_min_unit = unit
+                elif tagname == 'wavelength_max' and self.parent_class == 'SASsource':
+                    self.current_datainfo.source.wavelength_max = data_point
+                    self.current_datainfo.source.wavelength_max_unit = unit
+                elif tagname == 'wavelength_spread' and self.parent_class == 'SASsource':
+                    self.current_datainfo.source.wavelength_spread = data_point
+                    self.current_datainfo.source.wavelength_spread_unit = unit
+                elif tagname == 'x' and self.parent_class == 'beam_size':
+                    self.current_datainfo.source.beam_size.x = data_point
+                    self.current_datainfo.source.beam_size_unit = unit
+                elif tagname == 'y' and self.parent_class == 'beam_size':
+                    self.current_datainfo.source.beam_size.y = data_point
+                    self.current_datainfo.source.beam_size_unit = unit
+                elif tagname == 'z' and self.parent_class == 'pixel_size':
+                    self.current_datainfo.source.data_point.z = data_point
+                    self.current_datainfo.source.beam_size_unit = unit
+                elif tagname == 'radiation' and self.parent_class == 'SASsource':
+                    self.current_datainfo.source.radiation = data_point
+                elif tagname == 'beam_shape' and self.parent_class == 'SASsource':
+                    self.current_datainfo.source.beam_shape = data_point
+
+                ## Everything else goes in meta_data
+                else:
+                    new_key = self._create_unique_key(self.current_datainfo.meta_data, tagname)
+                    self.current_datainfo.meta_data[new_key] = data_point
+
+            self.names.remove(tagname_original)
+            length = 0
+            if len(self.names) > 1:
+                length = len(self.names) - 1
+            self.parent_class = self.names[length]
+        if not self._is_call_local(frm):
+            self.add_data_set()
+            empty = None
+            if self.output[0].dx is not None:
+                self.output[0].dxl = np.empty(0)
+                self.output[0].dxw = np.empty(0)
+            else:
+                self.output[0].dx = np.empty(0)
+            return self.output[0], empty
+
+
+    def _is_call_local(self, frm=""):
+        """
+
+        :return:
+        """
+        if frm == "":
+            frm = inspect.stack()[1]
+        mod_name = frm[1].replace("\\", "/").replace(".pyc", "")
+        mod_name = mod_name.replace(".py", "")
+        mod = mod_name.split("sas/")
+        mod_name = mod[1]
+        if mod_name != "sascalc/dataloader/readers/cansas_reader":
+            return False
+        return True
 
     def is_cansas(self, ext="xml"):
@@ -134,11 +477,12 @@
         if ext == "svs":
             return True
-        return False
+        raise RuntimeError
 
     def load_file_and_schema(self, xml_file, schema_path=""):
         """
-        Loads the file and associates a schema, if a known schema exists
+        Loads the file and associates a schema, if a schema is passed in or if one already exists
 
         :param xml_file: The xml file path sent to Reader.read
+        :param schema_path: The path to a schema associated with the xml_file, or find one based on the file
         """
         base_name = xml_reader.__file__
@@ -151,135 +495,112 @@
 
         # Generic values for the cansas file based on the version
-        cansas_defaults = CANSAS_NS.get(self.cansas_version, "1.0")
+        self.cansas_defaults = CANSAS_NS.get(self.cansas_version, "1.0")
         if schema_path == "":
-            schema_path = "{0}/sas/sascalc/dataloader/readers/schema/{1}".format\
-                (base, cansas_defaults.get("schema")).replace("\\", "/")
+            schema_path = "{0}/sas/sascalc/dataloader/readers/schema/{1}".format \
+                (base, self.cansas_defaults.get("schema")).replace("\\", "/")
 
         # Link a schema to the XML file.
         self.set_schema(schema_path)
-        return cansas_defaults
-
-    ## TODO: Test loading invalid CanSAS XML files and see if this works
-    ## TODO: Once works, try adding a warning that the data is invalid
-    def read(self, xml_file, schema_path=""):
-        """
-        Validate and read in an xml_file file in the canSAS format.
-
-        :param xml_file: A canSAS file path in proper XML format
-        """
-        # output - Final list of Data1D objects
-        output = []
-        # ns - Namespace hierarchy for current xml_file object
-        ns_list = []
-
-        # Check that the file exists
-        if os.path.isfile(xml_file):
-            basename = os.path.basename(xml_file)
-            _, extension = os.path.splitext(basename)
-            # If the file type is not allowed, return nothing
-            if extension in self.ext or self.allow_all:
-                # Get the file location of
-                cansas_defaults = self.load_file_and_schema(xml_file, schema_path)
-
-                # Try to load the file, but raise an error if unable to.
-                # Check the file matches the XML schema
-                try:
-                    if self.is_cansas(extension):
-                        # Get each SASentry from XML file and add it to a list.
-                        entry_list = self.xmlroot.xpath(
-                                '/ns:SASroot/ns:SASentry',
-                                namespaces={'ns': cansas_defaults.get("ns")})
-                        ns_list.append("SASentry")
-
-                        # If multiple files, modify the name for each is unique
-                        increment = 0
-                        # Parse each SASentry item
-                        for entry in entry_list:
-                            # Define a new Data1D object with zeroes for
-                            # x_vals and y_vals
-                            data1d = Data1D(numpy.empty(0), numpy.empty(0),
-                                            numpy.empty(0), numpy.empty(0))
-                            data1d.dxl = numpy.empty(0)
-                            data1d.dxw = numpy.empty(0)
-
-                            # If more than one SASentry, increment each in order
-                            name = basename
-                            if len(entry_list) - 1 > 0:
-                                name += "_{0}".format(increment)
-                                increment += 1
-
-                            # Set the Data1D name and then parse the entry.
-                            # The entry is appended to a list of entry values
-                            data1d.filename = name
-                            data1d.meta_data["loader"] = "CanSAS 1D"
-
-                            # Get all preprocessing events and encoding
-                            self.set_processing_instructions()
-                            data1d.meta_data[PREPROCESS] = \
-                                    self.processing_instructions
-
-                            # Parse the XML file
-                            return_value, extras = \
-                                self._parse_entry(entry, ns_list, data1d)
-                            del extras[:]
-
-                            return_value = self._final_cleanup(return_value)
-                            output.append(return_value)
-                    else:
-                        raise RuntimeError, "Invalid XML at: {0}".format(\
-                                                    self.find_invalid_xml())
-                except:
-                    # If the file does not match the schema, raise this error
-                    schema_path = "{0}/sas/sascalc/dataloader/readers/schema/cansas1d_invalid.xsd"
-                    invalid_xml = self.find_invalid_xml()
-                    invalid_xml = "\n\nThe loaded xml file does not fully meet the CanSAS v1.x specification. SasView " + \
-                                  "loaded as much of the data as possible.\n\n" + invalid_xml
-                    self.errors.add(invalid_xml)
-                    self.set_schema(schema_path)
-                    if self.is_cansas():
-                        output = self.read(xml_file, schema_path)
-                    else:
-                        raise RuntimeError, "%s cannot be read" % xml_file
-                return output
-        # Return a list of parsed entries that dataloader can manage
-        return None
-
-    def _final_cleanup(self, data1d):
+
+    def add_data_set(self):
+        """
+        Adds the current_dataset to the list of outputs after preforming final processing on the data and then calls a
+        private method to generate a new data set.
+
+        :param key: NeXus group name for current tree level
+        """
+
+        if self.current_datainfo and self.current_dataset:
+            self._final_cleanup()
+        self.data = []
+        self.current_datainfo = DataInfo()
+
+    def _initialize_new_data_set(self, parent_list=None):
+        """
+        A private class method to generate a new 1D data object.
+        Outside methods should call add_data_set() to be sure any existing data is stored properly.
+
+        :param parent_list: List of names of parent elements
+        """
+
+        if parent_list is None:
+            parent_list = []
+        x = np.array(0)
+        y = np.array(0)
+        self.current_dataset = plottable_1D(x, y)
+
+    def add_intermediate(self):
+        """
+        This method stores any intermediate objects within the final data set after fully reading the set.
+
+        :param parent: The NXclass name for the h5py Group object that just finished being processed
+        """
+
+        if self.parent_class == 'SASprocess':
+            self.current_datainfo.process.append(self.process)
+            self.process = Process()
+        elif self.parent_class == 'SASdetector':
+            self.current_datainfo.detector.append(self.detector)
+            self.detector = Detector()
+        elif self.parent_class == 'SAStransmission_spectrum':
+            self.current_datainfo.trans_spectrum.append(self.transspectrum)
+            self.transspectrum = TransmissionSpectrum()
+        elif self.parent_class == 'SAScollimation':
+            self.current_datainfo.collimation.append(self.collimation)
+            self.collimation = Collimation()
+        elif self.parent_class == 'aperture':
+            self.collimation.aperture.append(self.aperture)
+            self.aperture = Aperture()
+        elif self.parent_class == 'SASdata':
+            self._check_for_empty_resolution()
+            self.data.append(self.current_dataset)
+
+    def _final_cleanup(self):
         """
         Final cleanup of the Data1D object to be sure it has all the
         appropriate information needed for perspectives
-
-        :param data1d: Data1D object that has been populated
-        """
-        # Final cleanup
-        # Remove empty nodes, verify array sizes are correct
+        """
+
+        ## Append errors to dataset and reset class errors
+        self.current_datainfo.errors = set()
         for error in self.errors:
-            data1d.errors.append(error)
+            self.current_datainfo.errors.add(error)
         self.errors.clear()
-        numpy.trim_zeros(data1d.x)
-        numpy.trim_zeros(data1d.y)
-        numpy.trim_zeros(data1d.dy)
-        size_dx = data1d.dx.size
-        size_dxl = data1d.dxl.size
-        size_dxw = data1d.dxw.size
-        if data1d._xunit != data1d.x_unit:
-            data1d.x_unit = data1d._xunit
-        if data1d._yunit != data1d.y_unit:
-            data1d.y_unit = data1d._yunit
-        if size_dxl == 0 and size_dxw == 0:
-            data1d.dxl = None
-            data1d.dxw = None
-            numpy.trim_zeros(data1d.dx)
-        elif size_dx == 0:
-            data1d.dx = None
-            size_dx = size_dxl
-            numpy.trim_zeros(data1d.dxl)
-            numpy.trim_zeros(data1d.dxw)
-        return data1d
+
+        ## Combine all plottables with datainfo and append each to output
+        ## Type cast data arrays to float64 and find min/max as appropriate
+        for dataset in self.data:
+            if dataset.x is not None:
+                dataset.x = np.delete(dataset.x, [0])
+                dataset.x = dataset.x.astype(np.float64)
+                dataset.xmin = np.min(dataset.x)
+                dataset.xmax = np.max(dataset.x)
+            if dataset.y is not None:
+                dataset.y = np.delete(dataset.y, [0])
+                dataset.y = dataset.y.astype(np.float64)
+                dataset.ymin = np.min(dataset.y)
+                dataset.ymax = np.max(dataset.y)
+            if dataset.dx is not None:
+                dataset.dx = np.delete(dataset.dx, [0])
+                dataset.dx = dataset.dx.astype(np.float64)
+            if dataset.dxl is not None:
+                dataset.dxl = np.delete(dataset.dxl, [0])
+                dataset.dxl = dataset.dxl.astype(np.float64)
+            if dataset.dxw is not None:
+                dataset.dxw = np.delete(dataset.dxw, [0])
+                dataset.dxw = dataset.dxw.astype(np.float64)
+            if dataset.dy is not None:
+                dataset.dy = np.delete(dataset.dy, [0])
+                dataset.dy = dataset.dy.astype(np.float64)
+            np.trim_zeros(dataset.x)
+            np.trim_zeros(dataset.y)
+            np.trim_zeros(dataset.dy)
+            final_dataset = combine_data(dataset, self.current_datainfo)
+            self.output.append(final_dataset)
 
     def _create_unique_key(self, dictionary, name, numb=0):
         """
         Create a unique key value for any dictionary to prevent overwriting
-        Recurses until a unique key value is found.
+        Recurse until a unique key value is found.
 
         :param dictionary: A dictionary with any number of entries
@@ -294,14 +615,47 @@
         return name
 
-    def _unit_conversion(self, node, new_current_level, data1d, \
-                                                tagname, node_value):
+    def _get_node_value(self, node, tagname):
+        """
+        Get the value of a node and any applicable units
+
+        :param node: The XML node to get the value of
+        :param tagname: The tagname of the node
+        """
+        #Get the text from the node and convert all whitespace to spaces
+        units = ''
+        node_value = node.text
+        if node_value is not None:
+            node_value = ' '.join(node_value.split())
+        else:
+            node_value = ""
+
+        # If the value is a float, compile with units.
+        if self.ns_list.ns_datatype == "float":
+            # If an empty value is given, set as zero.
+            if node_value is None or node_value.isspace() \
+                                    or node_value.lower() == "nan":
+                node_value = "0.0"
+            #Convert the value to the base units
+            node_value, units = self._unit_conversion(node, tagname, node_value)
+
+        # If the value is a timestamp, convert to a datetime object
+        elif self.ns_list.ns_datatype == "timestamp":
+            if node_value is None or node_value.isspace():
+                pass
+            else:
+                try:
+                    node_value = \
+                        datetime.datetime.fromtimestamp(node_value)
+                except ValueError:
+                    node_value = None
+        return node_value, units
+
+    def _unit_conversion(self, node, tagname, node_value):
         """
         A unit converter method used to convert the data included in the file
         to the default units listed in data_info
 
-        :param new_current_level: cansas_constants level as returned by
-            iterate_namespace
-        :param attr: The attributes of the node
-        :param data1d: Where the values will be saved
+        :param node: XML node
+        :param tagname: name of the node
         :param node_value: The value of the current dom node
         """
@@ -310,13 +664,33 @@
         err_msg = None
         default_unit = None
-        if 'unit' in attr and new_current_level.get('unit') is not None:
+        if not isinstance(node_value, float):
+            node_value = float(node_value)
+        if 'unit' in attr and attr.get('unit') is not None:
             try:
                 local_unit = attr['unit']
-                if isinstance(node_value, float) is False:
-                    exec("node_value = float({0})".format(node_value))
-                unitname = new_current_level.get("unit")
-                exec "default_unit = data1d.{0}".format(unitname)
-                if local_unit is not None and default_unit is not None and \
-                        local_unit.lower() != default_unit.lower() \
+                unitname = self.ns_list.current_level.get("unit", "")
+                if "SASdetector" in self.names:
+                    save_in = "detector"
+                elif "aperture" in self.names:
+                    save_in = "aperture"
+                elif "SAScollimation" in self.names:
+                    save_in = "collimation"
+                elif "SAStransmission_spectrum" in self.names:
+                    save_in = "transspectrum"
+                elif "SASdata" in self.names:
+                    x = np.zeros(1)
+                    y = np.zeros(1)
+                    self.current_data1d = Data1D(x, y)
+                    save_in = "current_data1d"
+                elif "SASsource" in self.names:
+                    save_in = "current_datainfo.source"
+                elif "SASsample" in self.names:
+                    save_in = "current_datainfo.sample"
+                elif "SASprocess" in self.names:
+                    save_in = "process"
+                else:
+                    save_in = "current_datainfo"
+                exec "default_unit = self.{0}.{1}".format(save_in, unitname)
+                if local_unit and default_unit and local_unit.lower() != default_unit.lower() \
                         and local_unit.lower() != "none":
                     if HAS_CONVERTER == True:
@@ -345,8 +719,7 @@
         if err_msg:
             self.errors.add(err_msg)
-        node_value = "float({0})".format(node_value)
         return node_value, value_unit
 
-    def _check_for_empty_data(self, data1d):
+    def _check_for_empty_data(self):
         """
         Creates an empty data set if no data is passed to the reader
@@ -354,51 +727,16 @@
         :param data1d: presumably a Data1D object
         """
-        if data1d == None:
-            self.errors = set()
-            x_vals = numpy.empty(0)
-            y_vals = numpy.empty(0)
-            dx_vals = numpy.empty(0)
-            dy_vals = numpy.empty(0)
-            dxl = numpy.empty(0)
-            dxw = numpy.empty(0)
-            data1d = Data1D(x_vals, y_vals, dx_vals, dy_vals)
-            data1d.dxl = dxl
-            data1d.dxw = dxw
-        return data1d
-
-    def _handle_special_cases(self, tagname, data1d, children):
-        """
-        Handle cases where the data type in Data1D is a dictionary or list
-
-        :param tagname: XML tagname in use
-        :param data1d: The original Data1D object
-        :param children: Child nodes of node
-        :param node: existing node with tag name 'tagname'
-        """
-        if tagname == "SASdetector":
-            data1d = Detector()
-        elif tagname == "SAScollimation":
-            data1d = Collimation()
-        elif tagname == "SAStransmission_spectrum":
-            data1d = TransmissionSpectrum()
-        elif tagname == "SASprocess":
-            data1d = Process()
-            for child in children:
-                if child.tag.replace(self.base_ns, "") == "term":
-                    term_attr = {}
-                    for attr in child.keys():
-                        term_attr[attr] = \
-                            ' '.join(child.get(attr).split())
-                    if child.text is not None:
-                        term_attr['value'] = \
-                            ' '.join(child.text.split())
-                    data1d.term.append(term_attr)
-        elif tagname == "aperture":
-            data1d = Aperture()
-        if tagname == "Idata" and children is not None:
-            data1d = self._check_for_empty_resolution(data1d, children)
-        return data1d
-
-    def _check_for_empty_resolution(self, data1d, children):
+        if self.current_dataset == None:
+            x_vals = np.empty(0)
+            y_vals = np.empty(0)
+            dx_vals = np.empty(0)
+            dy_vals = np.empty(0)
+            dxl = np.empty(0)
+            dxw = np.empty(0)
+            self.current_dataset = plottable_1D(x_vals, y_vals, dx_vals, dy_vals)
+            self.current_dataset.dxl = dxl
+            self.current_dataset.dxw = dxw
+
+    def _check_for_empty_resolution(self):
         """
         A method to check all resolution data sets are the same size as I and Q
@@ -408,623 +746,45 @@
         dq_exists = False
         di_exists = False
-        for child in children:
-            tag = child.tag.replace(self.base_ns, "")
-            if tag == "dQl":
-                dql_exists = True
-            if tag == "dQw":
-                dqw_exists = True
-            if tag == "Qdev":
-                dq_exists = True
-            if tag == "Idev":
-                di_exists = True
-        if dqw_exists and dql_exists == False:
-            data1d.dxl = numpy.append(data1d.dxl, 0.0)
-        elif dql_exists and dqw_exists == False:
-            data1d.dxw = numpy.append(data1d.dxw, 0.0)
-        elif dql_exists == False and dqw_exists == False \
-                                            and dq_exists == False:
-            data1d.dx = numpy.append(data1d.dx, 0.0)
-        if di_exists == False:
-            data1d.dy = numpy.append(data1d.dy, 0.0)
-        return data1d
-
-    def _restore_original_case(self,
-                               tagname_original,
-                               tagname,
-                               save_data1d,
-                               data1d):
-        """
-        Save the special case data to the appropriate location and restore
-        the original Data1D object
-
-        :param tagname_original: Unmodified tagname for the node
-        :param tagname: modified tagname for the node
-        :param save_data1d: The original Data1D object
-        :param data1d: If a special case was handled, an object of that type
-        """
-        if tagname_original == "SASdetector":
-            save_data1d.detector.append(data1d)
-        elif tagname_original == "SAScollimation":
-            save_data1d.collimation.append(data1d)
-        elif tagname == "SAStransmission_spectrum":
-            save_data1d.trans_spectrum.append(data1d)
-        elif tagname_original == "SASprocess":
-            save_data1d.process.append(data1d)
-        elif tagname_original == "aperture":
-            save_data1d.aperture.append(data1d)
-        else:
-            save_data1d = data1d
-        return save_data1d
-
-    def _handle_attributes(self, node, data1d, cs_values, tagname):
-        """
-        Process all of the attributes for a node
-        """
-        attr = node.attrib
-        if attr is not None:
-            for key in node.keys():
-                try:
-                    node_value, unit = self._get_node_value(node, cs_values, \
-                                                   data1d, tagname)
-                    cansas_attrib = \
-                        cs_values.current_level.get("attributes").get(key)
-                    attrib_variable = cansas_attrib.get("variable")
-                    if key == 'unit' and unit != '':
-                        attrib_value = unit
-                    else:
-                        attrib_value = node.attrib[key]
-                    store_attr = attrib_variable.format("data1d",
-                                                        attrib_value,
-                                                        key,
-                                                        node_value)
-                    exec store_attr
-                except AttributeError:
-                    pass
-        return data1d
-
-    def _get_node_value(self, node, cs_values, data1d, tagname):
-        """
-        Get the value of a node and any applicable units
-
-        :param node: The XML node to get the value of
-        :param cs_values: A CansasConstants.CurrentLevel object
-        :param attr: The node attributes
-        :param dataid: The working object to be modified
-        :param tagname: The tagname of the node
-        """
-        #Get the text from the node and convert all whitespace to spaces
-        units = ''
-        node_value = node.text
-        if node_value == "":
-            node_value = None
-        if node_value is not None:
-            node_value = ' '.join(node_value.split())
-
-        # If the value is a float, compile with units.
-        if cs_values.ns_datatype == "float":
-            # If an empty value is given, set as zero.
-            if node_value is None or node_value.isspace() \
-                                    or node_value.lower() == "nan":
-                node_value = "0.0"
-            #Convert the value to the base units
-            node_value, units = self._unit_conversion(node, \
-                        cs_values.current_level, data1d, tagname, node_value)
-
-        # If the value is a timestamp, convert to a datetime object
-        elif cs_values.ns_datatype == "timestamp":
-            if node_value is None or node_value.isspace():
-                pass
-            else:
-                try:
-                    node_value = \
-                        datetime.datetime.fromtimestamp(node_value)
-                except ValueError:
-                    node_value = None
-        return node_value, units
-
-    def _parse_entry(self, dom, names=None, data1d=None, extras=None):
-        """
-        Parse a SASEntry - new recursive method for parsing the dom of
-            the CanSAS data format. This will allow multiple data files
-            and extra nodes to be read in simultaneously.
-
-        :param dom: dom object with a namespace base of names
-        :param names: A list of element names that lead up to the dom object
-        :param data1d: The data1d object that will be modified
-        :param extras: Any values that should go into meta_data when data1d
-            is not a Data1D object
-        """
-
-        if extras is None:
-            extras = []
-        if names is None or names == []:
-            names = ["SASentry"]
-
-        data1d = self._check_for_empty_data(data1d)
-
-        self.base_ns = "{0}{1}{2}".format("{", \
-                            CANSAS_NS.get(self.cansas_version).get("ns"), "}")
-        tagname = ''
-        tagname_original = ''
-
-        # Go through each child in the parent element
-        for node in dom:
-            try:
-                # Get the element name and set the current names level
-                tagname = node.tag.replace(self.base_ns, "")
-                tagname_original = tagname
-                if tagname == "fitting_plug_in" or tagname == "pr_inversion" or\
-                    tagname == "invariant":
-                    continue
-                names.append(tagname)
-                children = node.getchildren()
-                if len(children) == 0:
-                    children = None
-                save_data1d = data1d
-
-                # Look for special cases
-                data1d = self._handle_special_cases(tagname, data1d, children)
-
-                # Get where to store content
-                cs_values = CONSTANTS.iterate_namespace(names)
-                # If the element is a child element, recurse
-                if children is not None:
-                    # Returned value is new Data1D object with all previous and
-                    # new values in it.
-                    data1d, extras = self._parse_entry(node,
-                                                       names, data1d, extras)
-
-                #Get the information from the node
-                node_value, _ = self._get_node_value(node, cs_values, \
-                                                            data1d, tagname)
-
-                # If appending to a dictionary (meta_data | run_name)
-                # make sure the key is unique
-                if cs_values.ns_variable == "{0}.meta_data[\"{2}\"] = \"{1}\"":
-                    # If we are within a Process, Detector, Collimation or
-                    # Aperture instance, pull out old data1d
-                    tagname = self._create_unique_key(data1d.meta_data, \
-                                                      tagname, 0)
-                    if isinstance(data1d, Data1D) == False:
-                        store_me = cs_values.ns_variable.format("data1d", \
-                                                            node_value, tagname)
-                        extras.append(store_me)
-                        cs_values.ns_variable = None
-                if cs_values.ns_variable == "{0}.run_name[\"{2}\"] = \"{1}\"":
-                    tagname = self._create_unique_key(data1d.run_name, \
-                                                      tagname, 0)
-
-                # Check for Data1D object and any extra commands to save
-                if isinstance(data1d, Data1D):
-                    for item in extras:
-                        exec item
-                # Don't bother saving empty information unless it is a float
-                if cs_values.ns_variable is not None and \
-                            node_value is not None and \
-                            node_value.isspace() == False:
-                    # Format a string and then execute it.
-                    store_me = cs_values.ns_variable.format("data1d", \
-                                                            node_value, tagname)
-                    exec store_me
-                # Get attributes and process them
-                data1d = self._handle_attributes(node, data1d, cs_values, \
-                                                 tagname)
-
-            except TypeError:
-                pass
-            except Exception as excep:
-                exc_type, exc_obj, exc_tb = sys.exc_info()
-                fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
-                print(excep, exc_type, fname, exc_tb.tb_lineno, \
-                      tagname, exc_obj)
-            finally:
-                # Save special cases in original data1d object
-                # then restore the data1d
-                save_data1d = self._restore_original_case(tagname_original, \
-                                                tagname, save_data1d, data1d)
-                if tagname_original == "fitting_plug_in" or \
-                    tagname_original == "invariant" or \
-                    tagname_original == "pr_inversion":
-                    pass
-                else:
-                    data1d = save_data1d
-                    # Remove tagname from names to restore original base
-                    names.remove(tagname_original)
-        return data1d, extras
-
-    def _get_pi_string(self):
-        """
-        Creates the processing instructions header for writing to file
-        """
-        pis = self.return_processing_instructions()
-        if len(pis) > 0:
-            pi_tree = self.create_tree(pis[0])
-            i = 1
-            for i in range(1, len(pis) - 1):
-                pi_tree = self.append(pis[i], pi_tree)
-            pi_string = self.to_string(pi_tree)
-        else:
-            pi_string = ""
-        return pi_string
-
-    def _create_main_node(self):
-        """
-        Creates the primary xml header used when writing to file
-        """
-        xsi = "http://www.w3.org/2001/XMLSchema-instance"
-        version = self.cansas_version
-        n_s = CANSAS_NS.get(version).get("ns")
-        if version == "1.1":
-            url = "http://www.cansas.org/formats/1.1/"
-        else:
-            url = "http://svn.smallangles.net/svn/canSAS/1dwg/trunk/"
-        schema_location = "{0} {1}cansas1d.xsd".format(n_s, url)
-        attrib = {"{" + xsi + "}schemaLocation" : schema_location,
-                  "version" : version}
-        nsmap = {'xsi' : xsi, None: n_s}
-
-        main_node = self.create_element("{" + n_s + "}SASroot",
-                                        attrib=attrib, nsmap=nsmap)
-        return main_node
-
-    def _write_run_names(self, datainfo, entry_node):
-        """
-        Writes the run names to the XML file
-
-        :param datainfo: The Data1D object the information is coming from
-        :param entry_node: lxml node ElementTree object to be appended to
-        """
-        if datainfo.run == None or datainfo.run == []:
-            datainfo.run.append(RUN_NAME_DEFAULT)
-            datainfo.run_name[RUN_NAME_DEFAULT] = RUN_NAME_DEFAULT
-        for item in datainfo.run:
-            runname = {}
-            if item in datainfo.run_name and \
-            len(str(datainfo.run_name[item])) > 1:
-                runname = {'name': datainfo.run_name[item]}
-            self.write_node(entry_node, "Run", item, runname)
-
-    def _write_data(self, datainfo, entry_node):
-        """
-        Writes the I and Q data to the XML file
-
-        :param datainfo: The Data1D object the information is coming from
-        :param entry_node: lxml node ElementTree object to be appended to
-        """
-        node = self.create_element("SASdata")
-        self.append(node, entry_node)
-
-        for i in range(len(datainfo.x)):
-            point = self.create_element("Idata")
-            node.append(point)
-            self.write_node(point, "Q", datainfo.x[i],
-                            {'unit': datainfo.x_unit})
-            if len(datainfo.y) >= i:
-                self.write_node(point, "I", datainfo.y[i],
-                                {'unit': datainfo.y_unit})
-            if datainfo.dy != None and len(datainfo.dy) > i:
-                self.write_node(point, "Idev", datainfo.dy[i],
-                                {'unit': datainfo.y_unit})
-            if datainfo.dx != None and len(datainfo.dx) > i:
-                self.write_node(point, "Qdev", datainfo.dx[i],
-                                {'unit': datainfo.x_unit})
-            if datainfo.dxw != None and len(datainfo.dxw) > i:
-                self.write_node(point, "dQw", datainfo.dxw[i],
-                                {'unit': datainfo.x_unit})
-            if datainfo.dxl != None and len(datainfo.dxl) > i:
-                self.write_node(point, "dQl", datainfo.dxl[i],
-                                {'unit': datainfo.x_unit})
-
-    def _write_trans_spectrum(self, datainfo, entry_node):
-        """
-        Writes the transmission spectrum data to the XML file
-
-        :param datainfo: The Data1D object the information is coming from
-        :param entry_node: lxml node ElementTree object to be appended to
-        """
-        for i in range(len(datainfo.trans_spectrum)):
-            spectrum = datainfo.trans_spectrum[i]
-            node = self.create_element("SAStransmission_spectrum",
-                                       {"name" : spectrum.name})
-            self.append(node, entry_node)
-            if isinstance(spectrum.timestamp, datetime.datetime):
-                node.setAttribute("timestamp", spectrum.timestamp)
-            for i in range(len(spectrum.wavelength)):
-                point = self.create_element("Tdata")
-                node.append(point)
-                self.write_node(point, "Lambda", spectrum.wavelength[i],
-                                {'unit': spectrum.wavelength_unit})
-                self.write_node(point, "T", spectrum.transmission[i],
-                                {'unit': spectrum.transmission_unit})
-                if spectrum.transmission_deviation != None \
-                and len(spectrum.transmission_deviation) >= i:
-                    self.write_node(point, "Tdev",
-                                    spectrum.transmission_deviation[i],
-                                    {'unit':
-                                     spectrum.transmission_deviation_unit})
-
-    def _write_sample_info(self, datainfo, entry_node):
-        """
-        Writes the sample information to the XML file
-
-        :param datainfo: The Data1D object the information is coming from
-        :param entry_node: lxml node ElementTree object to be appended to
-        """
-        sample = self.create_element("SASsample")
-        if datainfo.sample.name is not None:
-            self.write_attribute(sample, "name",
-                                 str(datainfo.sample.name))
-        self.append(sample, entry_node)
-        self.write_node(sample, "ID", str(datainfo.sample.ID))
-        self.write_node(sample, "thickness", datainfo.sample.thickness,
-                        {"unit": datainfo.sample.thickness_unit})
-        self.write_node(sample, "transmission", datainfo.sample.transmission)
-        self.write_node(sample, "temperature", datainfo.sample.temperature,
-                        {"unit": datainfo.sample.temperature_unit})
-
-        pos = self.create_element("position")
-        written = self.write_node(pos,
-                                  "x",
-                                  datainfo.sample.position.x,
-                                  {"unit": datainfo.sample.position_unit})
-        written = written | self.write_node( \
-            pos, "y", datainfo.sample.position.y,
-            {"unit": datainfo.sample.position_unit})
-        written = written | self.write_node( \
-            pos, "z", datainfo.sample.position.z,
-            {"unit": datainfo.sample.position_unit})
-        if written == True:
-            self.append(pos, sample)
-
-        ori = self.create_element("orientation")
-        written = self.write_node(ori, "roll",
-                                  datainfo.sample.orientation.x,
-                                  {"unit": datainfo.sample.orientation_unit})
-        written = written | self.write_node( \
-            ori, "pitch", datainfo.sample.orientation.y,
-            {"unit": datainfo.sample.orientation_unit})
-        written = written | self.write_node( \
-            ori, "yaw", datainfo.sample.orientation.z,
-            {"unit": datainfo.sample.orientation_unit})
-        if written == True:
-            self.append(ori, sample)
-
-        for item in datainfo.sample.details:
-            self.write_node(sample, "details", item)
-
-    def _write_instrument(self, datainfo, entry_node):
-        """
-        Writes the instrumental information to the XML file
-
-        :param datainfo: The Data1D object the information is coming from
-        :param entry_node: lxml node ElementTree object to be appended to
-        """
-        instr = self.create_element("SASinstrument")
-        self.append(instr, entry_node)
-        self.write_node(instr, "name", datainfo.instrument)
-        return instr
-
-    def _write_source(self, datainfo, instr):
-        """
-        Writes the source information to the XML file
-
-        :param datainfo: The Data1D object the information is coming from
-        :param instr: instrument node  to be appended to
-        """
-        source = self.create_element("SASsource")
-        if datainfo.source.name is not None:
-            self.write_attribute(source, "name",
-                                 str(datainfo.source.name))
-        self.append(source, instr)
-        if datainfo.source.radiation == None or datainfo.source.radiation == '':
-            datainfo.source.radiation = "neutron"
-        self.write_node(source, "radiation", datainfo.source.radiation)
-
-        size = self.create_element("beam_size")
-        if datainfo.source.beam_size_name is not None:
-            self.write_attribute(size, "name",
-                                 str(datainfo.source.beam_size_name))
-        written = self.write_node( \
-            size, "x", datainfo.source.beam_size.x,
-            {"unit": datainfo.source.beam_size_unit})
-        written = written | self.write_node( \
-            size, "y", datainfo.source.beam_size.y,
-            {"unit": datainfo.source.beam_size_unit})
-        written = written | self.write_node( \
-            size, "z", datainfo.source.beam_size.z,
-            {"unit": datainfo.source.beam_size_unit})
-        if written == True:
-            self.append(size, source)
-
-        self.write_node(source, "beam_shape", datainfo.source.beam_shape)
-        self.write_node(source, "wavelength",
-                        datainfo.source.wavelength,
-                        {"unit": datainfo.source.wavelength_unit})
-        self.write_node(source, "wavelength_min",
-                        datainfo.source.wavelength_min,
-                        {"unit": datainfo.source.wavelength_min_unit})
-        self.write_node(source, "wavelength_max",
-                        datainfo.source.wavelength_max,
-                        {"unit": datainfo.source.wavelength_max_unit})
-        self.write_node(source, "wavelength_spread",
-                        datainfo.source.wavelength_spread,
-                        {"unit": datainfo.source.wavelength_spread_unit})
-
-    def _write_collimation(self, datainfo, instr):
-        """
-        Writes the collimation information to the XML file
-
-        :param datainfo: The Data1D object the information is coming from
-        :param instr: lxml node ElementTree object to be appended to
-        """
-        if datainfo.collimation == [] or datainfo.collimation == None:
-            coll = Collimation()
-            datainfo.collimation.append(coll)
-        for item in datainfo.collimation:
-            coll = self.create_element("SAScollimation")
-            if item.name is not None:
-                self.write_attribute(coll, "name", str(item.name))
-            self.append(coll, instr)
-
-            self.write_node(coll, "length", item.length,
-                            {"unit": item.length_unit})
-
-            for aperture in item.aperture:
-                apert = self.create_element("aperture")
-                if aperture.name is not None:
-                    self.write_attribute(apert, "name", str(aperture.name))
-                if aperture.type is not None:
-                    self.write_attribute(apert, "type", str(aperture.type))
-                self.append(apert, coll)
-
-                size = self.create_element("size")
-                if aperture.size_name is not None:
-                    self.write_attribute(size, "name",
-                                         str(aperture.size_name))
-                written = self.write_node(size, "x", aperture.size.x,
-                                          {"unit": aperture.size_unit})
-                written = written | self.write_node( \
-                    size, "y", aperture.size.y,
-                    {"unit": aperture.size_unit})
-                written = written | self.write_node( \
-                    size, "z", aperture.size.z,
-                    {"unit": aperture.size_unit})
-                if written == True:
-                    self.append(size, apert)
-
-                self.write_node(apert, "distance", aperture.distance,
-                                {"unit": aperture.distance_unit})
-
-    def _write_detectors(self, datainfo, instr):
-        """
-        Writes the detector information to the XML file
-
-        :param datainfo: The Data1D object the information is coming from
-        :param inst: lxml instrument node to be appended to
-        """
-        if datainfo.detector == None or datainfo.detector == []:
-            det = Detector()
-            det.name = ""
-            datainfo.detector.append(det)
-
-        for item in datainfo.detector:
-            det = self.create_element("SASdetector")
-            written = self.write_node(det, "name", item.name)
-            written = written | self.write_node(det, "SDD", item.distance,
-                                                {"unit": item.distance_unit})
-            if written == True:
-                self.append(det, instr)
-
-            off = self.create_element("offset")
-            written = self.write_node(off, "x", item.offset.x,
-                                      {"unit": item.offset_unit})
-            written = written | self.write_node(off, "y", item.offset.y,
-                                                {"unit": item.offset_unit})
-            written = written | self.write_node(off, "z", item.offset.z,
-                                                {"unit": item.offset_unit})
-            if written == True:
-                self.append(off, det)
-
-            ori = self.create_element("orientation")
-            written = self.write_node(ori, "roll", item.orientation.x,
-                                      {"unit": item.orientation_unit})
-            written = written | self.write_node(ori, "pitch",
-                                                item.orientation.y,
-                                                {"unit": item.orientation_unit})
-            written = written | self.write_node(ori, "yaw",
-                                                item.orientation.z,
-                                                {"unit": item.orientation_unit})
-            if written == True:
-                self.append(ori, det)
-
-            center = self.create_element("beam_center")
-            written = self.write_node(center, "x", item.beam_center.x,
-                                      {"unit": item.beam_center_unit})
-            written = written | self.write_node(center, "y",
-                                                item.beam_center.y,
-                                                {"unit": item.beam_center_unit})
-            written = written | self.write_node(center, "z",
-                                                item.beam_center.z,
-                                                {"unit": item.beam_center_unit})
-            if written == True:
-                self.append(center, det)
-
-            pix = self.create_element("pixel_size")
-            written = self.write_node(pix, "x", item.pixel_size.x,
-                                      {"unit": item.pixel_size_unit})
-            written = written | self.write_node(pix, "y", item.pixel_size.y,
-                                                {"unit": item.pixel_size_unit})
-            written = written | self.write_node(pix, "z", item.pixel_size.z,
-                                                {"unit": item.pixel_size_unit})
-            written = written | self.write_node(det, "slit_length",
-                                                item.slit_length,
-                                                {"unit": item.slit_length_unit})
-            if written == True:
-                self.append(pix, det)
-
-    def _write_process_notes(self, datainfo, entry_node):
-        """
-        Writes the process notes to the XML file
-
-        :param datainfo: The Data1D object the information is coming from
-        :param entry_node: lxml node ElementTree object to be appended to
-
-        """
-        for item in datainfo.process:
-            node = self.create_element("SASprocess")
-            self.append(node, entry_node)
-            self.write_node(node, "name", item.name)
-            self.write_node(node, "date", item.date)
-            self.write_node(node, "description", item.description)
-            for term in item.term:
-                value = term['value']
-                del term['value']
-                self.write_node(node, "term", value, term)
-            for note in item.notes:
-                self.write_node(node, "SASprocessnote", note)
-            if len(item.notes) == 0:
-                self.write_node(node, "SASprocessnote", "")
-
-    def _write_notes(self, datainfo, entry_node):
-        """
-        Writes the notes to the XML file and creates an empty note if none
-        exist
-
-        :param datainfo: The Data1D object the information is coming from
-        :param entry_node: lxml node ElementTree object to be appended to
-
-        """
-        if len(datainfo.notes) == 0:
-            node = self.create_element("SASnote")
-            self.append(node, entry_node)
-        else:
-            for item in datainfo.notes:
-                node = self.create_element("SASnote")
-                self.write_text(node, item)
-                self.append(node, entry_node)
-
-    def _check_origin(self, entry_node, doc, frm):
-        """
-        Return the document, and the SASentry node associated with
-        the data we just wrote.
-        If the calling function was not the cansas reader, return a minidom
-        object rather than an lxml object.
-
-        :param entry_node: lxml node ElementTree object to be appended to
-        :param doc: entire xml tree
-        """
-        if not frm:
-            frm = inspect.stack()[1]
-        mod_name = frm[1].replace("\\", "/").replace(".pyc", "")
-        mod_name = mod_name.replace(".py", "")
-        mod = mod_name.split("sas/")
-        mod_name = mod[1]
-        if mod_name != "sascalc/dataloader/readers/cansas_reader":
-            string = self.to_string(doc, pretty_print=False)
-            doc = parseString(string)
-            node_name = entry_node.tag
-            node_list = doc.getElementsByTagName(node_name)
-            entry_node = node_list.item(0)
-        return doc, entry_node
+        if self.current_dataset.dxl is not None:
+            dql_exists = True
+        if self.current_dataset.dxw is not None:
+            dqw_exists = True
+        if self.current_dataset.dx is not None:
+            dq_exists = True
+        if self.current_dataset.dy is not None:
+            di_exists = True
+        if dqw_exists and not dql_exists:
+            array_size = self.current_dataset.dxw.size - 1
+            self.current_dataset.dxl = np.append(self.current_dataset.dxl, np.zeros([array_size]))
+        elif dql_exists and not dqw_exists:
+            array_size = self.current_dataset.dxl.size - 1
+            self.current_dataset.dxw = np.append(self.current_dataset.dxw, np.zeros([array_size]))
+        elif not dql_exists and not dqw_exists and not dq_exists:
+            array_size = self.current_dataset.x.size - 1
+            self.current_dataset.dx = np.append(self.current_dataset.dx, np.zeros([array_size]))
+        if not di_exists:
+            array_size = self.current_dataset.y.size - 1
+            self.current_dataset.dy = np.append(self.current_dataset.dy, np.zeros([array_size]))
+
+
+    ####### All methods below are for writing CanSAS XML files #######
+
+
+    def write(self, filename, datainfo):
+        """
+        Write the content of a Data1D as a CanSAS XML file
+
+        :param filename: name of the file to write
+        :param datainfo: Data1D object
+        """
+        # Create XML document
+        doc, _ = self._to_xml_doc(datainfo)
+        # Write the file
+        file_ref = open(filename, 'w')
+        if self.encoding == None:
+            self.encoding = "UTF-8"
+        doc.write(file_ref, encoding=self.encoding,
+                  pretty_print=True, xml_declaration=True)
+        file_ref.close()
 
     def _to_xml_doc(self, datainfo):
@@ -1095,20 +855,404 @@
         return False
 
-    def write(self, filename, datainfo):
-        """
-        Write the content of a Data1D as a CanSAS XML file
-
-        :param filename: name of the file to write
-        :param datainfo: Data1D object
-        """
-        # Create XML document
-        doc, _ = self._to_xml_doc(datainfo)
-        # Write the file
-        file_ref = open(filename, 'w')
-        if self.encoding == None:
-            self.encoding = "UTF-8"
-        doc.write(file_ref, encoding=self.encoding,
-                  pretty_print=True, xml_declaration=True)
-        file_ref.close()
+    def _get_pi_string(self):
+        """
+        Creates the processing instructions header for writing to file
+        """
+        pis = self.return_processing_instructions()
+        if len(pis) > 0:
+            pi_tree = self.create_tree(pis[0])
+            i = 1
+            for i in range(1, len(pis) - 1):
+                pi_tree = self.append(pis[i], pi_tree)
+            pi_string = self.to_string(pi_tree)
+        else:
+            pi_string = ""
+        return pi_string
+
+    def _create_main_node(self):
+        """
+        Creates the primary xml header used when writing to file
+        """
+        xsi = "http://www.w3.org/2001/XMLSchema-instance"
+        version = self.cansas_version
+        n_s = CANSAS_NS.get(version).get("ns")
+        if version == "1.1":
+            url = "http://www.cansas.org/formats/1.1/"
+        else:
+            url = "http://svn.smallangles.net/svn/canSAS/1dwg/trunk/"
+        schema_location = "{0} {1}cansas1d.xsd".format(n_s, url)
+        attrib = {"{" + xsi + "}schemaLocation" : schema_location,
+                  "version" : version}
+        nsmap = {'xsi' : xsi, None: n_s}
+
+        main_node = self.create_element("{" + n_s + "}SASroot",
+                                        attrib=attrib, nsmap=nsmap)
+        return main_node
+
+    def _write_run_names(self, datainfo, entry_node):
+        """
+        Writes the run names to the XML file
+
+        :param datainfo: The Data1D object the information is coming from
+        :param entry_node: lxml node ElementTree object to be appended to
+        """
+        if datainfo.run == None or datainfo.run == []:
+            datainfo.run.append(RUN_NAME_DEFAULT)
+            datainfo.run_name[RUN_NAME_DEFAULT] = RUN_NAME_DEFAULT
+        for item in datainfo.run:
+            runname = {}
+            if item in datainfo.run_name and \
+            len(str(datainfo.run_name[item])) > 1:
+                runname = {'name': datainfo.run_name[item]}
+            self.write_node(entry_node, "Run", item, runname)
+
+    def _write_data(self, datainfo, entry_node):
+        """
+        Writes the I and Q data to the XML file
+
+        :param datainfo: The Data1D object the information is coming from
+        :param entry_node: lxml node ElementTree object to be appended to
+        """
+        node = self.create_element("SASdata")
+        self.append(node, entry_node)
+
+        for i in range(len(datainfo.x)):
+            point = self.create_element("Idata")
+            node.append(point)
+            self.write_node(point, "Q", datainfo.x[i],
+                            {'unit': datainfo.x_unit})
+            if len(datainfo.y) >= i:
+                self.write_node(point, "I", datainfo.y[i],
+                                {'unit': datainfo.y_unit})
+            if datainfo.dy is not None and len(datainfo.dy) > i:
+                self.write_node(point, "Idev", datainfo.dy[i],
+                                {'unit': datainfo.y_unit})
+            if datainfo.dx is not None and len(datainfo.dx) > i:
+                self.write_node(point, "Qdev", datainfo.dx[i],
+                                {'unit': datainfo.x_unit})
+            if datainfo.dxw is not None and len(datainfo.dxw) > i:
+                self.write_node(point, "dQw", datainfo.dxw[i],
+                                {'unit': datainfo.x_unit})
+            if datainfo.dxl is not None and len(datainfo.dxl) > i:
+                self.write_node(point, "dQl", datainfo.dxl[i],
+                                {'unit': datainfo.x_unit})
+
+    def _write_trans_spectrum(self, datainfo, entry_node):
+        """
+        Writes the transmission spectrum data to the XML file
+
+        :param datainfo: The Data1D object the information is coming from
+        :param entry_node: lxml node ElementTree object to be appended to
+        """
+        for i in range(len(datainfo.trans_spectrum)):
+            spectrum = datainfo.trans_spectrum[i]
+            node = self.create_element("SAStransmission_spectrum",
+                                       {"name" : spectrum.name})
+            self.append(node, entry_node)
+            if isinstance(spectrum.timestamp, datetime.datetime):
+                node.setAttribute("timestamp", spectrum.timestamp)
+            for i in range(len(spectrum.wavelength)):
+                point = self.create_element("Tdata")
+                node.append(point)
+                self.write_node(point, "Lambda", spectrum.wavelength[i],
+                                {'unit': spectrum.wavelength_unit})
+                self.write_node(point, "T", spectrum.transmission[i],
+                                {'unit': spectrum.transmission_unit})
+                if spectrum.transmission_deviation != None \
+                and len(spectrum.transmission_deviation) >= i:
+                    self.write_node(point, "Tdev",
+                                    spectrum.transmission_deviation[i],
+                                    {'unit':
+                                     spectrum.transmission_deviation_unit})
+
+    def _write_sample_info(self, datainfo, entry_node):
+        """
+        Writes the sample information to the XML file
+
+        :param datainfo: The Data1D object the information is coming from
+        :param entry_node: lxml node ElementTree object to be appended to
+        """
+        sample = self.create_element("SASsample")
+        if datainfo.sample.name is not None:
+            self.write_attribute(sample, "name",
+                                 str(datainfo.sample.name))
+        self.append(sample, entry_node)
+        self.write_node(sample, "ID", str(datainfo.sample.ID))
+        self.write_node(sample, "thickness", datainfo.sample.thickness,
+                        {"unit": datainfo.sample.thickness_unit})
+        self.write_node(sample, "transmission", datainfo.sample.transmission)
+        self.write_node(sample, "temperature", datainfo.sample.temperature,
+                        {"unit": datainfo.sample.temperature_unit})
+
+        pos = self.create_element("position")
+        written = self.write_node(pos,
+                                  "x",
+                                  datainfo.sample.position.x,
+                                  {"unit": datainfo.sample.position_unit})
+        written = written | self.write_node( \
+            pos, "y", datainfo.sample.position.y,
+            {"unit": datainfo.sample.position_unit})
+        written = written | self.write_node( \
+            pos, "z", datainfo.sample.position.z,
+            {"unit": datainfo.sample.position_unit})
+        if written == True:
+            self.append(pos, sample)
+
+        ori = self.create_element("orientation")
+        written = self.write_node(ori, "roll",
+                                  datainfo.sample.orientation.x,
+                                  {"unit": datainfo.sample.orientation_unit})
+        written = written | self.write_node( \
+            ori, "pitch", datainfo.sample.orientation.y,
+            {"unit": datainfo.sample.orientation_unit})
+        written = written | self.write_node( \
+            ori, "yaw", datainfo.sample.orientation.z,
+            {"unit": datainfo.sample.orientation_unit})
+        if written == True:
+            self.append(ori, sample)
+
+        for item in datainfo.sample.details:
+            self.write_node(sample, "details", item)
+
+    def _write_instrument(self, datainfo, entry_node):
+        """
+        Writes the instrumental information to the XML file
+
+        :param datainfo: The Data1D object the information is coming from
+        :param entry_node: lxml node ElementTree object to be appended to
+        """
+        instr = self.create_element("SASinstrument")
+        self.append(instr, entry_node)
+        self.write_node(instr, "name", datainfo.instrument)
+        return instr
+
+    def _write_source(self, datainfo, instr):
+        """
+        Writes the source information to the XML file
+
+        :param datainfo: The Data1D object the information is coming from
+        :param instr: instrument node  to be appended to
+        """
+        source = self.create_element("SASsource")
+        if datainfo.source.name is not None:
+            self.write_attribute(source, "name",
+                                 str(datainfo.source.name))
+        self.append(source, instr)
+        if datainfo.source.radiation == None or datainfo.source.radiation == '':
+            datainfo.source.radiation = "neutron"
+        self.write_node(source, "radiation", datainfo.source.radiation)
+
+        size = self.create_element("beam_size")
+        if datainfo.source.beam_size_name is not None:
+            self.write_attribute(size, "name",
+                                 str(datainfo.source.beam_size_name))
+        written = self.write_node( \
+            size, "x", datainfo.source.beam_size.x,
+            {"unit": datainfo.source.beam_size_unit})
+        written = written | self.write_node( \
+            size, "y", datainfo.source.beam_size.y,
+            {"unit": datainfo.source.beam_size_unit})
+        written = written | self.write_node( \
+            size, "z", datainfo.source.beam_size.z,
+            {"unit": datainfo.source.beam_size_unit})
+        if written == True:
+            self.append(size, source)
+
+        self.write_node(source, "beam_shape", datainfo.source.beam_shape)
+        self.write_node(source, "wavelength",
+                        datainfo.source.wavelength,
+                        {"unit": datainfo.source.wavelength_unit})
+        self.write_node(source, "wavelength_min",
+                        datainfo.source.wavelength_min,
+                        {"unit": datainfo.source.wavelength_min_unit})
+        self.write_node(source, "wavelength_max",
+                        datainfo.source.wavelength_max,
+                        {"unit": datainfo.source.wavelength_max_unit})
+        self.write_node(source, "wavelength_spread",
+                        datainfo.source.wavelength_spread,
+                        {"unit": datainfo.source.wavelength_spread_unit})
+
+    def _write_collimation(self, datainfo, instr):
+        """
+        Writes the collimation information to the XML file
+
+        :param datainfo: The Data1D object the information is coming from
+        :param instr: lxml node ElementTree object to be appended to
+        """
+        if datainfo.collimation == [] or datainfo.collimation == None:
+            coll = Collimation()
+            datainfo.collimation.append(coll)
+        for item in datainfo.collimation:
+            coll = self.create_element("SAScollimation")
+            if item.name is not None:
+                self.write_attribute(coll, "name", str(item.name))
+            self.append(coll, instr)
+
+            self.write_node(coll, "length", item.length,
+                            {"unit": item.length_unit})
+
+            for aperture in item.aperture:
+                apert = self.create_element("aperture")
+                if aperture.name is not None:
+                    self.write_attribute(apert, "name", str(aperture.name))
+                if aperture.type is not None:
+                    self.write_attribute(apert, "type", str(aperture.type))
+                self.append(apert, coll)
+
+                size = self.create_element("size")
+                if aperture.size_name is not None:
+                    self.write_attribute(size, "name",
+                                         str(aperture.size_name))
+                written = self.write_node(size, "x", aperture.size.x,
+                                          {"unit": aperture.size_unit})
+                written = written | self.write_node( \
+                    size, "y", aperture.size.y,
+                    {"unit": aperture.size_unit})
+                written = written | self.write_node( \
+                    size, "z", aperture.size.z,
+                    {"unit": aperture.size_unit})
+                if written == True:
+                    self.append(size, apert)
+
+                self.write_node(apert, "distance", aperture.distance,
+                                {"unit": aperture.distance_unit})
+
+    def _write_detectors(self, datainfo, instr):
+        """
+        Writes the detector information to the XML file
+
+        :param datainfo: The Data1D object the information is coming from
+        :param inst: lxml instrument node to be appended to
+        """
+        if datainfo.detector == None or datainfo.detector == []:
+            det = Detector()
+            det.name = ""
+            datainfo.detector.append(det)
+
+        for item in datainfo.detector:
+            det = self.create_element("SASdetector")
+            written = self.write_node(det, "name", item.name)
+            written = written | self.write_node(det, "SDD", item.distance,
+                                                {"unit": item.distance_unit})
+            if written == True:
+                self.append(det, instr)
+
+            off = self.create_element("offset")
+            written = self.write_node(off, "x", item.offset.x,
+                                      {"unit": item.offset_unit})
+            written = written | self.write_node(off, "y", item.offset.y,
+                                                {"unit": item.offset_unit})
+            written = written | self.write_node(off, "z", item.offset.z,
+                                                {"unit": item.offset_unit})
+            if written == True:
+                self.append(off, det)
+
+            ori = self.create_element("orientation")
+            written = self.write_node(ori, "roll", item.orientation.x,
+                                      {"unit": item.orientation_unit})
+            written = written | self.write_node(ori, "pitch",
+                                                item.orientation.y,
+                                                {"unit": item.orientation_unit})
+            written = written | self.write_node(ori, "yaw",
+                                                item.orientation.z,
+                                                {"unit": item.orientation_unit})
+            if written == True:
+                self.append(ori, det)
+
+            center = self.create_element("beam_center")
+            written = self.write_node(center, "x", item.beam_center.x,
+                                      {"unit": item.beam_center_unit})
+            written = written | self.write_node(center, "y",
+                                                item.beam_center.y,
+                                                {"unit": item.beam_center_unit})
+            written = written | self.write_node(center, "z",
+                                                item.beam_center.z,
+                                                {"unit": item.beam_center_unit})
+            if written == True:
+                self.append(center, det)
+
+            pix = self.create_element("pixel_size")
+            written = self.write_node(pix, "x", item.pixel_size.x,
+                                      {"unit": item.pixel_size_unit})
+            written = written | self.write_node(pix, "y", item.pixel_size.y,
+                                                {"unit": item.pixel_size_unit})
+            written = written | self.write_node(pix, "z", item.pixel_size.z,
+                                                {"unit": item.pixel_size_unit})
+            written = written | self.write_node(det, "slit_length",
+                                                item.slit_length,
+                                                {"unit": item.slit_length_unit})
+            if written == True:
+                self.append(pix, det)
+
+    def _write_process_notes(self, datainfo, entry_node):
+        """
+        Writes the process notes to the XML file
+
+        :param datainfo: The Data1D object the information is coming from
+        :param entry_node: lxml node ElementTree object to be appended to
+
+        """
+        for item in datainfo.process:
+            node = self.create_element("SASprocess")
+            self.append(node, entry_node)
+            self.write_node(node, "name", item.name)
+            self.write_node(node, "date", item.date)
+            self.write_node(node, "description", item.description)
+            for term in item.term:
+                if isinstance(term, list):
+                    value = term['value']
+                    del term['value']
+                elif isinstance(term, dict):
+                    value = term.get("value")
+                    del term['value']
+                else:
+                    value = term
+                self.write_node(node, "term", value, term)
+            for note in item.notes:
+                self.write_node(node, "SASprocessnote", note)
+            if len(item.notes) == 0:
+                self.write_node(node, "SASprocessnote", "")
+
+    def _write_notes(self, datainfo, entry_node):
+        """
+        Writes the notes to the XML file and creates an empty note if none
+        exist
+
+        :param datainfo: The Data1D object the information is coming from
+        :param entry_node: lxml node ElementTree object to be appended to
+
+        """
+        if len(datainfo.notes) == 0:
+            node = self.create_element("SASnote")
+            self.append(node, entry_node)
+        else:
+            for item in datainfo.notes:
+                node = self.create_element("SASnote")
+                self.write_text(node, item)
+                self.append(node, entry_node)
+
+    def _check_origin(self, entry_node, doc, frm):
+        """
+        Return the document, and the SASentry node associated with
+        the data we just wrote.
+        If the calling function was not the cansas reader, return a minidom
+        object rather than an lxml object.
+
+        :param entry_node: lxml node ElementTree object to be appended to
+        :param doc: entire xml tree
+        """
+        if not frm:
+            frm = inspect.stack()[1]
+        mod_name = frm[1].replace("\\", "/").replace(".pyc", "")
+        mod_name = mod_name.replace(".py", "")
+        mod = mod_name.split("sas/")
+        mod_name = mod[1]
+        if mod_name != "sascalc/dataloader/readers/cansas_reader":
+            string = self.to_string(doc, pretty_print=False)
+            doc = parseString(string)
+            node_name = entry_node.tag
+            node_list = doc.getElementsByTagName(node_name)
+            entry_node = node_list.item(0)
+        return doc, entry_node
 
     # DO NOT REMOVE - used in saving and loading panel states.
@@ -1195,2 +1339,45 @@
         if entry is not None and entry.text is not None:
             exec "storage.%s = entry.text.strip()" % variable
+
+
+# DO NOT REMOVE Called by outside packages:
+#    sas.sasgui.perspectives.invariant.invariant_state
+#    sas.sasgui.perspectives.fitting.pagestate
+def get_content(location, node):
+    """
+    Get the first instance of the content of a xpath location.
+
+    :param location: xpath location
+    :param node: node to start at
+
+    :return: Element, or None
+    """
+    nodes = node.xpath(location,
+                       namespaces={'ns': CANSAS_NS.get("1.0").get("ns")})
+    if len(nodes) > 0:
+        return nodes[0]
+    else:
+        return None
+
+# DO NOT REMOVE Called by outside packages:
+#    sas.sasgui.perspectives.fitting.pagestate
+def write_node(doc, parent, name, value, attr=None):
+    """
+    :param doc: document DOM
+    :param parent: parent node
+    :param name: tag of the element
+    :param value: value of the child text node
+    :param attr: attribute dictionary
+
+    :return: True if something was appended, otherwise False
+    """
+    if attr is None:
+        attr = {}
+    if value is not None:
+        node = doc.createElement(name)
+        node.appendChild(doc.createTextNode(str(value)))
+        for item in attr:
+            node.setAttribute(item, attr[item])
+        parent.appendChild(node)
+        return True
+    return False
Index: src/sas/sascalc/dataloader/readers/cansas_reader_HDF5.py
===================================================================
--- src/sas/sascalc/dataloader/readers/cansas_reader_HDF5.py	(revision d72567eac270b5ba960e7bfa2dac6f9cbc461dbe)
+++ src/sas/sascalc/dataloader/readers/cansas_reader_HDF5.py	(revision 479799ca62cb68babc5dffcb75789060111eddeb)
@@ -62,5 +62,4 @@
         :return: List of Data1D/2D objects and/or a list of errors.
         """
-
         ## Reinitialize the class when loading a new data file to reset all class variables
         self.reset_class_variables()
@@ -136,8 +135,48 @@
                 ## If this is a dataset, store the data appropriately
                 data_set = data[key][:]
+                unit = self._get_unit(value)
+
+                ## I and Q Data
+                if key == u'I':
+                    if type(self.current_dataset) is plottable_2D:
+                        self.current_dataset.data = data_set
+                        self.current_dataset.zaxis("Intensity", unit)
+                    else:
+                        self.current_dataset.y = data_set.flatten()
+                        self.current_dataset.yaxis("Intensity", unit)
+                    continue
+                elif key == u'Idev':
+                    if type(self.current_dataset) is plottable_2D:
+                        self.current_dataset.err_data = data_set.flatten()
+                    else:
+                        self.current_dataset.dy = data_set.flatten()
+                    continue
+                elif key == u'Q':
+                    self.current_dataset.xaxis("Q", unit)
+                    if type(self.current_dataset) is plottable_2D:
+                        self.current_dataset.q = data_set.flatten()
+                    else:
+                        self.current_dataset.x = data_set.flatten()
+                    continue
+                elif key == u'Qy':
+                    self.current_dataset.yaxis("Q_y", unit)
+                    self.current_dataset.qy_data = data_set.flatten()
+                    continue
+                elif key == u'Qydev':
+                    self.current_dataset.dqy_data = data_set.flatten()
+                    continue
+                elif key == u'Qx':
+                    self.current_dataset.xaxis("Q_x", unit)
+                    self.current_dataset.qx_data = data_set.flatten()
+                    continue
+                elif key == u'Qxdev':
+                    self.current_dataset.dqx_data = data_set.flatten()
+                    continue
+                elif key == u'Mask':
+                    self.current_dataset.mask = data_set.flatten()
+                    continue
 
                 for data_point in data_set:
                     ## Top Level Meta Data
-                    unit = self._get_unit(value)
                     if key == u'definition':
                         self.current_datainfo.meta_data['reader'] = data_point
@@ -148,36 +187,4 @@
                     elif key == u'SASnote':
                         self.current_datainfo.notes.append(data_point)
-
-                    ## I and Q Data
-                    elif key == u'I':
-                        if type(self.current_dataset) is plottable_2D:
-                            self.current_dataset.data = np.append(self.current_dataset.data, data_point)
-                            self.current_dataset.zaxis("Intensity", unit)
-                        else:
-                            self.current_dataset.y = np.append(self.current_dataset.y, data_point)
-                            self.current_dataset.yaxis("Intensity", unit)
-                    elif key == u'Idev':
-                        if type(self.current_dataset) is plottable_2D:
-                            self.current_dataset.err_data = np.append(self.current_dataset.err_data, data_point)
-                        else:
-                            self.current_dataset.dy = np.append(self.current_dataset.dy, data_point)
-                    elif key == u'Q':
-                        self.current_dataset.xaxis("Q", unit)
-                        if type(self.current_dataset) is plottable_2D:
-                            self.current_dataset.q = np.append(self.current_dataset.q, data_point)
-                        else:
-                            self.current_dataset.x = np.append(self.current_dataset.x, data_point)
-                    elif key == u'Qy':
-                        self.current_dataset.yaxis("Q_y", unit)
-                        self.current_dataset.qy_data = np.append(self.current_dataset.qy_data, data_point)
-                    elif key == u'Qydev':
-                        self.current_dataset.dqy_data = np.append(self.current_dataset.dqy_data, data_point)
-                    elif key == u'Qx':
-                        self.current_dataset.xaxis("Q_x", unit)
-                        self.current_dataset.qx_data = np.append(self.current_dataset.qx_data, data_point)
-                    elif key == u'Qxdev':
-                        self.current_dataset.dqx_data = np.append(self.current_dataset.dqx_data, data_point)
-                    elif key == u'Mask':
-                        self.current_dataset.mask = np.append(self.current_dataset.mask, data_point)
 
                     ## Sample Information
@@ -296,27 +303,19 @@
         ## Type cast data arrays to float64 and find min/max as appropriate
         for dataset in self.data2d:
-            dataset.data = np.delete(dataset.data, [0])
             dataset.data = dataset.data.astype(np.float64)
-            dataset.err_data = np.delete(dataset.err_data, [0])
             dataset.err_data = dataset.err_data.astype(np.float64)
-            dataset.mask = np.delete(dataset.mask, [0])
             if dataset.qx_data is not None:
-                dataset.qx_data = np.delete(dataset.qx_data, [0])
                 dataset.xmin = np.min(dataset.qx_data)
                 dataset.xmax = np.max(dataset.qx_data)
                 dataset.qx_data = dataset.qx_data.astype(np.float64)
             if dataset.dqx_data is not None:
-                dataset.dqx_data = np.delete(dataset.dqx_data, [0])
                 dataset.dqx_data = dataset.dqx_data.astype(np.float64)
             if dataset.qy_data is not None:
-                dataset.qy_data = np.delete(dataset.qy_data, [0])
                 dataset.ymin = np.min(dataset.qy_data)
                 dataset.ymax = np.max(dataset.qy_data)
                 dataset.qy_data = dataset.qy_data.astype(np.float64)
             if dataset.dqy_data is not None:
-                dataset.dqy_data = np.delete(dataset.dqy_data, [0])
                 dataset.dqy_data = dataset.dqy_data.astype(np.float64)
             if dataset.q_data is not None:
-                dataset.q_data = np.delete(dataset.q_data, [0])
                 dataset.q_data = dataset.q_data.astype(np.float64)
             zeros = np.ones(dataset.data.size, dtype=bool)
@@ -333,4 +332,11 @@
             except:
                 dataset.q_data = None
+
+            if dataset.data.ndim == 2:
+                (n_rows, n_cols) = dataset.data.shape
+                dataset.y_bins = dataset.qy_data[0::n_cols]
+                dataset.x_bins = dataset.qx_data[:n_cols]
+                dataset.data = dataset.data.flatten()
+
             final_dataset = combine_data_info_with_plottable(dataset, self.current_datainfo)
             self.output.append(final_dataset)
@@ -338,24 +344,18 @@
         for dataset in self.data1d:
             if dataset.x is not None:
-                dataset.x = np.delete(dataset.x, [0])
                 dataset.x = dataset.x.astype(np.float64)
                 dataset.xmin = np.min(dataset.x)
                 dataset.xmax = np.max(dataset.x)
             if dataset.y is not None:
-                dataset.y = np.delete(dataset.y, [0])
                 dataset.y = dataset.y.astype(np.float64)
                 dataset.ymin = np.min(dataset.y)
                 dataset.ymax = np.max(dataset.y)
             if dataset.dx is not None:
-                dataset.dx = np.delete(dataset.dx, [0])
                 dataset.dx = dataset.dx.astype(np.float64)
             if dataset.dxl is not None:
-                dataset.dxl = np.delete(dataset.dxl, [0])
                 dataset.dxl = dataset.dxl.astype(np.float64)
             if dataset.dxw is not None:
-                dataset.dxw = np.delete(dataset.dxw, [0])
                 dataset.dxw = dataset.dxw.astype(np.float64)
             if dataset.dy is not None:
-                dataset.dy = np.delete(dataset.dy, [0])
                 dataset.dy = dataset.dy.astype(np.float64)
             final_dataset = combine_data_info_with_plottable(dataset, self.current_datainfo)
Index: c/sas/sascalc/dataloader/readers/schema/cansas1d_invalid.xsd
===================================================================
--- src/sas/sascalc/dataloader/readers/schema/cansas1d_invalid.xsd	(revision 83b6408fbfaefa4819b955c697fef834d8f033f6)
+++ 	(revision )
@@ -1,269 +1,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<?xml-stylesheet type="text/xsl" href="xsd-documentation.xsl" ?>
-<schema xmlns="http://www.w3.org/2001/XMLSchema"
-	xmlns:xsd="http://www.w3.org/2001/XMLSchema"
-	xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"  jaxb:version="2.0"
-	targetNamespace="urn:cansas1d:1.1" xmlns:tns="urn:cansas1d:1.1"
-	elementFormDefault="qualified">
-
-<!-- 
-Copyright (c) 2013, UChicago Argonne, LLC
-This file is distributed subject to a Software License Agreement found
-in the file LICENSE that is included with this distribution. 
- -->
- 
-<!-- JAXB: http://java.sun.com/developer/technicalArticles/WebServices/jaxb/ -->
-
-	<complexType name="floatUnitType">
-		<simpleContent>
-			<extension base="float">
-				<attribute name="unit" type="string" use="required" />
-			</extension>
-		</simpleContent>
-	</complexType>
-
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-
-	<group name="positionGroup">
-		<sequence>
-			<element name="x" type="tns:floatUnitType" minOccurs="0" maxOccurs="1" />
-			<element name="y" type="tns:floatUnitType" minOccurs="0" maxOccurs="1" />
-			<element name="z" type="tns:floatUnitType" minOccurs="0" maxOccurs="1" />
-		</sequence>
-	</group>
-
-	<complexType name="positionType">
-		<group ref="tns:positionGroup" />
-		<attribute name="name" type="string" use="optional" default="" />
-	</complexType>
-
-	<group name="orientationGroup">
-		<sequence>
-			<element name="roll" type="tns:floatUnitType" minOccurs="0"  maxOccurs="1" />
-			<element name="pitch" type="tns:floatUnitType" minOccurs="0" maxOccurs="1" />
-			<element name="yaw" type="tns:floatUnitType" minOccurs="0"   maxOccurs="1" />
-		</sequence>
-	</group>
-
-	<complexType name="orientationType">
-		<group ref="tns:orientationGroup" />
-		<attribute name="name" type="string" use="optional" default="" />
-	</complexType>
-
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-	
-	<complexType name="IdataType">
-		<sequence>
-			<element name="Q" minOccurs="1" maxOccurs="1"    type="tns:floatUnitType" />
-			<element name="I" minOccurs="1" maxOccurs="1"    type="tns:floatUnitType" />
-			<element name="Idev" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
-			<xsd:choice>
-				<!-- [ [Qdev] | [[dQw] | [dQl]] ] -->
-				<element name="Qdev" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
-				<xsd:sequence>
-					<element name="dQw" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
-					<element name="dQl" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
-				</xsd:sequence>
-			</xsd:choice>
-			<element name="Qmean" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
-			<element name="Shadowfactor" minOccurs="0" maxOccurs="1" type="float" default="1.0" />
-			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
-		</sequence>
-	</complexType>
-	
-	<complexType name="SASdataType">
-		<sequence minOccurs="1" maxOccurs="1">  <!-- http://svn.cansas.org/trac/ticket/21 -->
-			<sequence>
-				<element name="Idata" minOccurs="1" maxOccurs="unbounded" type="tns:IdataType" />
-			</sequence>
-			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
-		</sequence>
-		<attribute name="name" type="string" use="optional" default="" />
-		<attribute name="timestamp" type="dateTime" use="optional" />
-	</complexType>
-	
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-	
-	<complexType name="TdataType">
-			<sequence>
-				<element name="Lambda" minOccurs="0" maxOccurs="1"    type="tns:floatUnitType" />
-				<element name="T" minOccurs="0" maxOccurs="1"    type="tns:floatUnitType" />
-				<element name="Tdev" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
-				<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
-		</sequence>
-	</complexType>
-	
-	<complexType name="SAStransmission_spectrumType">
-		<sequence minOccurs="0" maxOccurs="1">
-			<sequence minOccurs="0" maxOccurs="1">
-				<element name="Tdata" minOccurs="0" maxOccurs="unbounded" type="tns:TdataType" />
-			</sequence>
-			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
-		</sequence>
-		<attribute name="name" type="string" use="optional" default="" />
-		<attribute name="timestamp" type="dateTime" use="optional" />
-	</complexType>
-	
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-
-	<complexType name="SASsampleType">
-		<sequence>
-			<element name="ID" minOccurs="0" maxOccurs="1" type="string" />
-			<element name="thickness" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-			<element name="transmission" minOccurs="0" maxOccurs="1" type="float" />
-			<element name="temperature" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-			<element name="position" minOccurs="0" maxOccurs="1" type="tns:positionType" />
-			<element name="orientation" minOccurs="0" maxOccurs="1" type="tns:orientationType" />
-			<element name="details" minOccurs="0" maxOccurs="unbounded" />
-			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
-		</sequence>
-		<attribute name="name" type="string" use="optional" default="" />
-	</complexType>
-
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-
-	<complexType name="termType">
-		<simpleContent>
-			<extension base="string">
-				<attribute name="name" type="string" use="optional" default="" />
-				<attribute name="unit" type="string" use="optional"/>
-			</extension>
-		</simpleContent>
-	</complexType>
-
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-
-	<complexType name="SASprocessType">
-		<annotation>
-			<appinfo>
-				<!-- JAXB: simplify class name -->
-				<jaxb:property name="SASprocess" />
-			</appinfo>
-		</annotation>
-		<sequence>
-			<element name="name" minOccurs="0" maxOccurs="1" type="string" default="">
-				<annotation>
-					<appinfo>
-						<!-- Remap collision of Name when using JAXB -->
-						<jaxb:property name="SASprocessName" />
-					</appinfo>
-				</annotation>
-			</element>
-			<element name="date" minOccurs="0" maxOccurs="1" type="string" />
-			<element name="description" minOccurs="0" maxOccurs="1" />
-			<element name="term" minOccurs="0" maxOccurs="unbounded" type="tns:termType" />
-			<element name="SASprocessnote" minOccurs="0" maxOccurs="unbounded" />
-			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
-		</sequence>
-		<attribute name="name" type="string" use="optional" default="">
-			<annotation>
-				<appinfo>
-					<!-- Remap collision of Name when using JAXB -->
-					<jaxb:property name="SASprocessNameAttr" />
-				</appinfo>
-			</annotation>
-		</attribute>
-	</complexType>
-
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-
-	<complexType name="SASsourceType">
-		<sequence>
-			<element name="radiation" minOccurs="0" maxOccurs="1" type="string" />
-			<element name="beam_size" minOccurs="0" maxOccurs="1" type="tns:positionType" />
-			<element name="beam_shape" minOccurs="0" maxOccurs="1" type="string" />
-			<element name="wavelength" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-			<element name="wavelength_min" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-			<element name="wavelength_max" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-			<element name="wavelength_spread" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-		</sequence>
-		<attribute name="name" type="string" use="optional" default="" />
-	</complexType>
-
-	<complexType name="SAScollimationType">
-		<sequence>
-			<element name="length" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-			<element name="aperture" minOccurs="0" maxOccurs="unbounded">
-				<complexType>
-					<sequence>
-						<element name="size" minOccurs="0" maxOccurs="1" type="tns:positionType" />
-						<element name="distance" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-					</sequence>
-					<attribute name="name" type="string" use="optional" default="" />
-					<attribute name="type" type="string" use="optional" default="" />
-				</complexType>
-			</element>
-		</sequence>
-		<attribute name="name" type="string" use="optional" default="" />
-	</complexType>
-
-	<complexType name="SASdetectorType">
-		<sequence>
-			<element name="name" minOccurs="0" maxOccurs="1" type="string" default=""/>
-			<element name="SDD" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-			<element name="offset" minOccurs="0" maxOccurs="1" type="tns:positionType" />
-			<element name="orientation" minOccurs="0" maxOccurs="1" type="tns:orientationType" />
-			<element name="beam_center" minOccurs="0" maxOccurs="1" type="tns:positionType" />
-			<element name="pixel_size" minOccurs="0" maxOccurs="1" type="tns:positionType" />
-			<element name="slit_length" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-		</sequence>
-	</complexType>
-
-	<complexType name="SASinstrumentType">
-		<sequence>
-			<element name="name" type="string" minOccurs="0" maxOccurs="1" default="" />
-			<element name="SASsource" type="tns:SASsourceType" minOccurs="0" maxOccurs="1" />
-			<element name="SAScollimation" type="tns:SAScollimationType" minOccurs="0" maxOccurs="unbounded" />
-			<element name="SASdetector" type="tns:SASdetectorType" minOccurs="0" maxOccurs="unbounded" />
-		</sequence>
-	</complexType>
-
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-
-	<complexType name="SASentryType">
-		<sequence>
-			<element name="Title" minOccurs="0" maxOccurs="1" type="string" />
-			<element name="Run" minOccurs="0" maxOccurs="unbounded">
-				<xsd:complexType>
-					<simpleContent>
-						<extension base="string">
-							<attribute name="name" type="string" use="optional" default="" />
-						</extension>
-					</simpleContent>
-				</xsd:complexType>
-			</element>
-			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
-			<element name="SASdata" minOccurs="1" maxOccurs="unbounded" type="tns:SASdataType" />
-			<element name="SAStransmission_spectrum" minOccurs="0" maxOccurs="unbounded" type="tns:SAStransmission_spectrumType"/>
-			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other">
-				<!-- Is a second *any* element needed here? -->
-				<annotation>
-					<appinfo>
-						<!-- Remap collision of Any when using JAXB --> 
-						<jaxb:property name="SASentryAny2" />
-					</appinfo>
-				</annotation>
-			</xsd:any>
-			<element name="SASsample" type="tns:SASsampleType" />
-			<element name="SASinstrument" type="tns:SASinstrumentType" minOccurs="0" maxOccurs="1" />
-			<element name="SASprocess" type="tns:SASprocessType" minOccurs="0" maxOccurs="unbounded" />
-			<element name="SASnote" minOccurs="0" maxOccurs="unbounded" />
-				<!-- SASnote is a free-form element.
-					Do not define any content for it or it will become restrictive.
-				-->
-		</sequence>
-		<attribute name="name" type="string" use="optional" default="" />
-	</complexType>
-
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-
-	<complexType name="SASrootType">
-		<sequence>
-			<element name="SASentry" minOccurs="1" maxOccurs="unbounded" type="tns:SASentryType" />
-		</sequence>
-		<attribute name="version" type="string" fixed="1.1" use="optional" />
-	</complexType>
-
-	<element name="SASroot" type="tns:SASrootType" />
-
-</schema>
Index: src/sas/sascalc/dataloader/readers/schema/cansas1d_invalid_v1_0.xsd
===================================================================
--- src/sas/sascalc/dataloader/readers/schema/cansas1d_invalid_v1_0.xsd	(revision 250fec92ea66b0770da33360a90f022e1d674072)
+++ src/sas/sascalc/dataloader/readers/schema/cansas1d_invalid_v1_0.xsd	(revision 250fec92ea66b0770da33360a90f022e1d674072)
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet type="text/xsl" href="xsd-documentation.xsl" ?>
+<schema xmlns="http://www.w3.org/2001/XMLSchema"
+	xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+	xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"  jaxb:version="2.0"
+	targetNamespace="cansas1d/1.0" xmlns:tns="cansas1d/1.0"
+	elementFormDefault="qualified">
+
+<!-- 
+Copyright (c) 2013, UChicago Argonne, LLC
+This file is distributed subject to a Software License Agreement found
+in the file LICENSE that is included with this distribution. 
+ -->
+ 
+<!-- JAXB: http://java.sun.com/developer/technicalArticles/WebServices/jaxb/ -->
+
+	<complexType name="floatUnitType">
+		<simpleContent>
+			<extension base="string">
+				<attribute name="unit" type="string" use="required" />
+			</extension>
+		</simpleContent>
+	</complexType>
+
+	<complexType name="IdataType">
+		<sequence>
+			<element name="Q" minOccurs="1" maxOccurs="1"    type="tns:floatUnitType" />
+			<element name="I" minOccurs="1" maxOccurs="1"    type="tns:floatUnitType" />
+			<element name="Idev" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
+			<xsd:choice>
+				<!-- [ [Qdev] | [[dQw] | [dQl]] ] -->
+				<element name="Qdev" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
+				<xsd:sequence>
+					<element name="dQw" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
+					<element name="dQl" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
+				</xsd:sequence>
+			</xsd:choice>
+			<element name="Qmean" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
+			<element name="Shadowfactor" minOccurs="0" maxOccurs="1" type="string" default="1.0" />
+			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="lax" namespace="##other" />
+		</sequence>
+	</complexType>
+	
+	<complexType name="SASdataType">
+		<sequence minOccurs="1" maxOccurs="1">  <!-- http://svn.cansas.org/trac/ticket/21 -->
+			<sequence>
+				<element name="Idata" minOccurs="1" maxOccurs="unbounded" type="tns:IdataType" />
+			</sequence>
+			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="lax" namespace="##other" />
+		</sequence>
+		<attribute name="name" type="string" use="optional" default="" />
+		<attribute name="timestamp" type="dateTime" use="optional" />
+	</complexType>
+
+	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
+
+	<complexType name="SASentryType">
+		<sequence>
+			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="lax" />
+			<element name="SASdata" minOccurs="1" maxOccurs="unbounded" type="tns:SASdataType" />
+			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="lax">
+				<!-- Is a second *any* element needed here? -->
+				<annotation>
+					<appinfo>
+						<!-- Remap collision of Any when using JAXB --> 
+						<jaxb:property name="SASentryAny2" />
+					</appinfo>
+				</annotation>
+			</xsd:any>
+		</sequence>
+		<attribute name="name" type="string" use="optional" default="" />
+	</complexType>
+
+	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
+
+	<complexType name="SASrootType">
+		<sequence>
+			<element name="SASentry" minOccurs="1" maxOccurs="unbounded" type="tns:SASentryType" />
+		</sequence>
+		<attribute name="version" type="string" fixed="1.0" use="optional" />
+	</complexType>
+
+	<element name="SASroot" type="tns:SASrootType" />
+
+</schema>
Index: src/sas/sascalc/dataloader/readers/schema/cansas1d_invalid_v1_1.xsd
===================================================================
--- src/sas/sascalc/dataloader/readers/schema/cansas1d_invalid_v1_1.xsd	(revision 250fec92ea66b0770da33360a90f022e1d674072)
+++ src/sas/sascalc/dataloader/readers/schema/cansas1d_invalid_v1_1.xsd	(revision 250fec92ea66b0770da33360a90f022e1d674072)
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet type="text/xsl" href="xsd-documentation.xsl" ?>
+<schema xmlns="http://www.w3.org/2001/XMLSchema"
+	xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+	xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"  jaxb:version="2.0"
+	targetNamespace="urn:cansas1d:1.1" xmlns:tns="urn:cansas1d:1.1"
+	elementFormDefault="qualified">
+
+<!-- 
+Copyright (c) 2013, UChicago Argonne, LLC
+This file is distributed subject to a Software License Agreement found
+in the file LICENSE that is included with this distribution. 
+ -->
+ 
+<!-- JAXB: http://java.sun.com/developer/technicalArticles/WebServices/jaxb/ -->
+
+	<complexType name="floatUnitType">
+		<simpleContent>
+			<extension base="string">
+				<attribute name="unit" type="string" use="required" />
+			</extension>
+		</simpleContent>
+	</complexType>
+
+	<complexType name="IdataType">
+		<sequence>
+			<element name="Q" minOccurs="1" maxOccurs="1"    type="tns:floatUnitType" />
+			<element name="I" minOccurs="1" maxOccurs="1"    type="tns:floatUnitType" />
+			<element name="Idev" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
+			<xsd:choice>
+				<!-- [ [Qdev] | [[dQw] | [dQl]] ] -->
+				<element name="Qdev" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
+				<xsd:sequence>
+					<element name="dQw" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
+					<element name="dQl" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
+				</xsd:sequence>
+			</xsd:choice>
+			<element name="Qmean" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
+			<element name="Shadowfactor" minOccurs="0" maxOccurs="1" type="string" default="1.0" />
+			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="lax" namespace="##other" />
+		</sequence>
+	</complexType>
+	
+	<complexType name="SASdataType">
+		<sequence minOccurs="1" maxOccurs="1">  <!-- http://svn.cansas.org/trac/ticket/21 -->
+			<sequence>
+				<element name="Idata" minOccurs="1" maxOccurs="unbounded" type="tns:IdataType" />
+			</sequence>
+			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="lax" namespace="##other" />
+		</sequence>
+		<attribute name="name" type="string" use="optional" default="" />
+		<attribute name="timestamp" type="dateTime" use="optional" />
+	</complexType>
+
+	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
+
+	<complexType name="SASentryType">
+		<sequence>
+			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="lax" />
+			<element name="SASdata" minOccurs="1" maxOccurs="unbounded" type="tns:SASdataType" />
+			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="lax">
+				<!-- Is a second *any* element needed here? -->
+				<annotation>
+					<appinfo>
+						<!-- Remap collision of Any when using JAXB --> 
+						<jaxb:property name="SASentryAny2" />
+					</appinfo>
+				</annotation>
+			</xsd:any>
+		</sequence>
+		<attribute name="name" type="string" use="optional" default="" />
+	</complexType>
+
+	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
+
+	<complexType name="SASrootType">
+		<sequence>
+			<element name="SASentry" minOccurs="1" maxOccurs="unbounded" type="tns:SASentryType" />
+		</sequence>
+		<attribute name="version" type="string" fixed="1.1" use="optional" />
+	</complexType>
+
+	<element name="SASroot" type="tns:SASrootType" />
+
+</schema>
Index: src/sas/sasgui/guiframe/data_panel.py
===================================================================
--- src/sas/sasgui/guiframe/data_panel.py	(revision b5c44f0493a2482bb7ea5c39743c7eb7514dd935)
+++ src/sas/sasgui/guiframe/data_panel.py	(revision e767897d6a10f25bfe2bccbef3e3fe6ab0385f7c)
@@ -2,5 +2,5 @@
 #This software was developed by the University of Tennessee as part of the
 #Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
-#project funded by the US National Science Foundation. 
+#project funded by the US National Science Foundation.
 #
 #See the license text in license.txt
@@ -41,13 +41,13 @@
         as QucikPlotDialog
 import sas.sasgui.guiframe.config as config
- 
+
 extension_list = []
 if config.APPLICATION_STATE_EXTENSION is not None:
     extension_list.append(config.APPLICATION_STATE_EXTENSION)
-EXTENSIONS = config.PLUGIN_STATE_EXTENSIONS + extension_list   
+EXTENSIONS = config.PLUGIN_STATE_EXTENSIONS + extension_list
 PLUGINS_WLIST = config.PLUGINS_WLIST
 APPLICATION_WLIST = config.APPLICATION_WLIST
 
-#Control panel width 
+#Control panel width
 if sys.platform.count("win32") > 0:
     PANEL_WIDTH = 235
@@ -67,6 +67,6 @@
 STYLE_FLAG = wx.RAISED_BORDER|CT.TR_HAS_BUTTONS| CT.TR_HIDE_ROOT|\
                     wx.WANTS_CHARS|CT.TR_HAS_VARIABLE_ROW_HEIGHT
-                    
-                    
+
+
 class DataTreeCtrl(CT.CustomTreeCtrl):
     """
@@ -97,9 +97,9 @@
                     CT.CustomTreeCtrl.__init__(self, parent, *args, **kwds)
         self.root = self.AddRoot("Available Data")
-                   
+
     def OnCompareItems(self, item1, item2):
-        """ 
-        Overrides OnCompareItems in wx.TreeCtrl. 
-        Used by the SortChildren method. 
+        """
+        Overrides OnCompareItems in wx.TreeCtrl.
+        Used by the SortChildren method.
         """
         # Get the item data
@@ -113,8 +113,8 @@
         else:
             return 0
-        
+
 class DataPanel(ScrolledPanel, PanelBase):
     """
-    This panel displays data available in the application and widgets to 
+    This panel displays data available in the application and widgets to
     interact with data.
     """
@@ -123,10 +123,10 @@
     ## Title to appear on top of the window
     window_caption = "Data Explorer"
-    #type of window 
+    #type of window
     window_type = "Data Panel"
     ## Flag to tell the GUI manager that this panel is not
     #  tied to any perspective
     #ALWAYS_ON = True
-    def __init__(self, parent, 
+    def __init__(self, parent,
                  list=None,
                  size=(PANEL_WIDTH, PANEL_HEIGHT),
@@ -138,9 +138,9 @@
         PanelBase.__init__(self, parent)
         self.SetupScrolling()
-        #Set window's font size 
+        #Set window's font size
         self.SetWindowVariant(variant=FONT_VARIANT)
-        self.loader = Loader()  
+        self.loader = Loader()
         #Default location
-        self._default_save_location = None  
+        self._default_save_location = None
         self.all_data1d = True
         self.parent = parent.parent
@@ -188,5 +188,5 @@
         if self.parent is not None:
             self.parent.Bind(EVT_DELETE_PLOTPANEL, self._on_delete_plot_panel)
-     
+
     def do_layout(self):
         """
@@ -198,5 +198,5 @@
         self.layout_batch()
         self.layout_button()
-        
+
     def disable_app_combo(self, enable):
         """
@@ -204,5 +204,5 @@
         """
         self.perspective_cbox.Enable(enable)
-        
+
     def define_panel_structure(self):
         """
@@ -213,10 +213,10 @@
         self.sizer1 = wx.BoxSizer(wx.VERTICAL)
         self.sizer1.SetMinSize(wx.Size(w/13, h*2/5))
-      
+
         self.sizer2 = wx.BoxSizer(wx.VERTICAL)
         self.sizer3 = wx.FlexGridSizer(9, 2, 4, 1)
         self.sizer4 = wx.BoxSizer(wx.VERTICAL)
         self.sizer5 = wx.BoxSizer(wx.VERTICAL)
-       
+
         self.vbox.Add(self.sizer5, 0, wx.EXPAND|wx.ALL, 1)
         self.vbox.Add(self.sizer1, 1, wx.EXPAND|wx.ALL, 0)
@@ -224,7 +224,7 @@
         self.vbox.Add(self.sizer3, 0, wx.EXPAND|wx.ALL, 10)
         #self.vbox.Add(self.sizer4, 0, wx.EXPAND|wx.ALL,5)
-        
+
         self.SetSizer(self.vbox)
-        
+
     def layout_selection(self):
         """
@@ -244,9 +244,9 @@
         self.selection_cbox.SetValue('Select all Data')
         wx.EVT_COMBOBOX(self.selection_cbox, -1, self._on_selection_type)
-        self.sizer5.AddMany([(select_txt, 0, wx.ALL, 5), 
+        self.sizer5.AddMany([(select_txt, 0, wx.ALL, 5),
                             (self.selection_cbox, 0, wx.ALL,5)])
         self.enable_selection()
-        
-    
+
+
     def _on_selection_type(self, event):
         """
@@ -254,32 +254,39 @@
             :param event: UI event
         """
+        def check_item_and_children(control, check_value=True):
+            self.tree_ctrl.CheckItem(data_ctrl, check_value)
+            if data_ctrl.HasChildren():
+                if check_value == True and not control.IsExpanded():
+                    # Only select children if control is expanded
+                    # Always deselect children, regardless (see ticket #259)
+                    return
+                for child_ctrl in data_ctrl.GetChildren():
+                    self.tree_ctrl.CheckItem(child_ctrl, check_value)
+
         option = self.selection_cbox.GetValue()
-        
+
         pos = self.selection_cbox.GetSelection()
         if pos == wx.NOT_FOUND:
-            return 
+            return
         option = self.selection_cbox.GetString(pos)
         for item in self.list_cb_data.values():
             data_ctrl, _, _, _, _, _, _, _ = item
-            _, data_class, _ = self.tree_ctrl.GetItemPyData(data_ctrl) 
+            _, data_class, _ = self.tree_ctrl.GetItemPyData(data_ctrl)
             if option == 'Select all Data':
-                self.tree_ctrl.CheckItem(data_ctrl, True) 
+                check_item_and_children(data_ctrl, check_value=True)
             elif option == 'Unselect all Data':
-                self.tree_ctrl.CheckItem(data_ctrl, False)
+                check_item_and_children(data_ctrl, check_value=False)
             elif option == 'Select all Data 1D':
                 if data_class == 'Data1D':
-                    self.tree_ctrl.CheckItem(data_ctrl, True) 
+                    check_item_and_children(data_ctrl, check_value=True)
             elif option == 'Unselect all Data 1D':
                 if data_class == 'Data1D':
-                    self.tree_ctrl.CheckItem(data_ctrl, False) 
-            elif option == 'Select all Data 1D':
-                if data_class == 'Data1D':
-                    self.tree_ctrl.CheckItem(data_ctrl, True) 
+                    check_item_and_children(data_ctrl, check_value=False)
             elif option == 'Select all Data 2D':
                 if data_class == 'Data2D':
-                    self.tree_ctrl.CheckItem(data_ctrl, True) 
+                    check_item_and_children(data_ctrl, check_value=True)
             elif option == 'Unselect all Data 2D':
                 if data_class == 'Data2D':
-                    self.tree_ctrl.CheckItem(data_ctrl, False) 
+                    check_item_and_children(data_ctrl, check_value=False)
         self.enable_append()
         self.enable_freeze()
@@ -287,5 +294,5 @@
         self.enable_import()
         self.enable_remove()
-               
+
     def layout_button(self):
         """
@@ -293,9 +300,9 @@
         """
         #Load Data Button
-        self.bt_add = wx.Button(self, wx.NewId(), "Load Data", 
+        self.bt_add = wx.Button(self, wx.NewId(), "Load Data",
                                 size=(BUTTON_WIDTH, -1))
         self.bt_add.SetToolTipString("Load data files")
         wx.EVT_BUTTON(self, self.bt_add.GetId(), self._load_data)
-        
+
         #Delete Data Button
         self.bt_remove = wx.Button(self, wx.NewId(), "Delete Data",
@@ -303,5 +310,5 @@
         self.bt_remove.SetToolTipString("Delete data from the application")
         wx.EVT_BUTTON(self, self.bt_remove.GetId(), self.on_remove)
-        
+
         #Send data to perspective button
         self.bt_import = wx.Button(self, wx.NewId(), "Send To",
@@ -309,5 +316,5 @@
         self.bt_import.SetToolTipString("Send Data set to active perspective")
         wx.EVT_BUTTON(self, self.bt_import.GetId(), self.on_import)
-        
+
         #Choose perspective to be send data to combo box
         self.perspective_cbox = wx.ComboBox(self, -1,
@@ -315,7 +322,7 @@
         if not IS_MAC:
             self.perspective_cbox.SetMinSize((BUTTON_WIDTH*1.6, -1))
-        wx.EVT_COMBOBOX(self.perspective_cbox, -1, 
+        wx.EVT_COMBOBOX(self.perspective_cbox, -1,
                         self._on_perspective_selection)
-    
+
         #Append data to current Graph Button
         self.bt_append_plot = wx.Button(self, wx.NewId(), "Append Plot To",
@@ -324,13 +331,13 @@
                                 "Plot the selected data in the active panel")
         wx.EVT_BUTTON(self, self.bt_append_plot.GetId(), self.on_append_plot)
-        
+
         #Create a new graph and send data to that new graph button
-        self.bt_plot = wx.Button(self, wx.NewId(), "New Plot", 
+        self.bt_plot = wx.Button(self, wx.NewId(), "New Plot",
                                  size=(BUTTON_WIDTH, -1))
         self.bt_plot.SetToolTipString("To trigger plotting")
         wx.EVT_BUTTON(self, self.bt_plot.GetId(), self.on_plot)
-        
+
         #Freeze current theory button - becomes a data set and stays on graph
-        self.bt_freeze = wx.Button(self, wx.NewId(), "Freeze Theory", 
+        self.bt_freeze = wx.Button(self, wx.NewId(), "Freeze Theory",
                                    size=(BUTTON_WIDTH, -1))
         freeze_tip = "To trigger freeze a theory: making a copy\n"
@@ -339,15 +346,15 @@
         self.bt_freeze.SetToolTipString(freeze_tip)
         wx.EVT_BUTTON(self, self.bt_freeze.GetId(), self.on_freeze)
-       
+
         #select plot to send to combo box (blank if no data)
         if sys.platform == 'darwin':
-            self.cb_plotpanel = wx.ComboBox(self, -1, 
+            self.cb_plotpanel = wx.ComboBox(self, -1,
                                             style=wx.CB_READONLY)
         else:
-            self.cb_plotpanel = wx.ComboBox(self, -1, 
+            self.cb_plotpanel = wx.ComboBox(self, -1,
                                             style=wx.CB_READONLY|wx.CB_SORT)
         wx.EVT_COMBOBOX(self.cb_plotpanel, -1, self._on_plot_selection)
         self.cb_plotpanel.Disable()
-        
+
         #Help button
         self.bt_help = wx.Button(self, wx.NewId(), "HELP",
@@ -365,10 +372,10 @@
                              ((10, 10)),
                              (self.bt_append_plot),
-                             (self.cb_plotpanel, 
+                             (self.cb_plotpanel,
                               wx.EXPAND|wx.ADJUST_MINSIZE, 5),
                              ((5, 5)),
                              ((5, 5)),
                              (self.bt_import, 0, wx.EXPAND|wx.RIGHT, 5),
-                             (self.perspective_cbox, 
+                             (self.perspective_cbox,
                               wx.EXPAND|wx.ADJUST_MINSIZE, 5),
                              ((10, 10)),
@@ -385,5 +392,5 @@
         self.enable_freeze()
         self.enable_remove_plot()
-        
+
     def layout_batch(self):
         """
@@ -397,10 +404,10 @@
         self.Bind(wx.EVT_RADIOBUTTON, self.on_batch_mode,
                    id=self.rb_batch_mode.GetId())
-        
+
         self.rb_single_mode.SetValue(not self.parent.batch_on)
         self.rb_batch_mode.SetValue(self.parent.batch_on)
         self.sizer4.AddMany([(self.rb_single_mode, 0, wx.ALL, 4),
                              (self.rb_batch_mode, 0, wx.ALL, 4)])
-        
+
     def on_single_mode(self, event):
         """
@@ -410,5 +417,5 @@
         if self.parent is not None:
             wx.PostEvent(self.parent, NewBatchEvent(enable=False))
-       
+
     def on_batch_mode(self, event):
         """
@@ -417,8 +424,8 @@
         """
         if self.parent is not None:
-            wx.PostEvent(self.parent, 
+            wx.PostEvent(self.parent,
                          NewBatchEvent(enable=True))
-    
-    def _get_data_selection(self, event):  
+
+    def _get_data_selection(self, event):
         """
             Get data selection from the right click
@@ -435,5 +442,5 @@
             data = theory_list.values()[0][0]
         return data
-    
+
     def on_edit_data(self, event):
         """
@@ -443,9 +450,9 @@
         from sas.sasgui.guiframe.local_perspectives.plotting.masking \
             import MaskPanel as MaskDialog
-        
-        panel = MaskDialog(parent=self.parent, base=self, 
+
+        panel = MaskDialog(parent=self.parent, base=self,
                            data=data, id=wx.NewId())
         panel.ShowModal()
-    
+
     def on_plot_3d(self, event):
         """
@@ -455,9 +462,9 @@
         from sas.sasgui.guiframe.local_perspectives.plotting.masking \
         import FloatPanel as Float3dDialog
-        
-        panel = Float3dDialog(base=self, data=data, 
+
+        panel = Float3dDialog(base=self, data=data,
                               dimension=3, id=wx.NewId())
-        panel.ShowModal()   
-    
+        panel.ShowModal()
+
     def on_quick_plot(self, event):
         """
@@ -468,6 +475,6 @@
             dimension = 2
         else:
-            dimension = 1 
-        #panel = QucikPlotDialog(base=self, data=data, 
+            dimension = 1
+        #panel = QucikPlotDialog(base=self, data=data,
         #                        dimension=dimension, id=wx.NewId())
         frame = QucikPlotDialog(self, -1, "Plot " + data.name, 'log_{10}')
@@ -477,6 +484,6 @@
         frame.Show(True)
         frame.SetFocus()
-        #panel.ShowModal()    
-    
+        #panel.ShowModal()
+
     def on_data_info(self, event):
         """
@@ -488,5 +495,5 @@
         else:
             self.parent.show_data1d(data, data.name)
-        
+
     def on_save_as(self, event):
         """
@@ -506,5 +513,5 @@
             else:
                 print "unable to save this type of data"
-        
+
     def layout_data_list(self):
         """
@@ -523,5 +530,5 @@
         self.data_menu.Append(id, name, msg)
         wx.EVT_MENU(self, id, self.on_data_info)
-        
+
         id = wx.NewId()
         name = "Save As"
@@ -529,5 +536,5 @@
         self.data_menu.Append(id, name, msg)
         wx.EVT_MENU(self, id, self.on_save_as)
-    
+
         quickplot_id = wx.NewId()
         name = "Quick Plot"
@@ -535,5 +542,5 @@
         self.data_menu.Append(quickplot_id, name, msg)
         wx.EVT_MENU(self, quickplot_id, self.on_quick_plot)
-        
+
         self.plot3d_id = wx.NewId()
         name = "Quick 3DPlot (Slow)"
@@ -541,5 +548,5 @@
         self.data_menu.Append(self.plot3d_id, name, msg)
         wx.EVT_MENU(self, self.plot3d_id, self.on_plot_3d)
-            
+
         self.editmask_id = wx.NewId()
         name = "Edit Mask"
@@ -547,12 +554,12 @@
         self.data_menu.Append(self.editmask_id, name, msg)
         wx.EVT_MENU(self, self.editmask_id, self.on_edit_data)
-        
+
         tree_ctrl_theory_label = wx.StaticText(self, -1, "Theory")
         tree_ctrl_theory_label.SetForegroundColour('blue')
-        self.tree_ctrl_theory = DataTreeCtrl(parent=self, 
+        self.tree_ctrl_theory = DataTreeCtrl(parent=self,
                                                     style=wx.SUNKEN_BORDER)
-        self.tree_ctrl_theory.Bind(CT.EVT_TREE_ITEM_CHECKING, 
+        self.tree_ctrl_theory.Bind(CT.EVT_TREE_ITEM_CHECKING,
                                                     self.on_check_item)
-        self.tree_ctrl_theory.Bind(CT.EVT_TREE_ITEM_MENU, 
+        self.tree_ctrl_theory.Bind(CT.EVT_TREE_ITEM_MENU,
                                    self.on_right_click_theory)
         self.sizer1.Add(tree_ctrl_label, 0, wx.LEFT, 10)
@@ -560,5 +567,5 @@
         self.sizer1.Add(tree_ctrl_theory_label, 0,  wx.LEFT, 10)
         self.sizer1.Add(self.tree_ctrl_theory, 1, wx.EXPAND|wx.ALL, 10)
-           
+
     def on_right_click_theory(self, event):
         """
@@ -575,6 +582,6 @@
             self.data_menu.Enable(self.editmask_id, False)
             self.data_menu.Enable(self.plot3d_id, menu_enable)
-            self.PopupMenu(self.data_menu) 
-                   
+            self.PopupMenu(self.data_menu)
+
     def on_right_click_data(self, event):
         """
@@ -596,7 +603,7 @@
             self.data_menu.Enable(self.editmask_id, maskmenu_enable)
             self.data_menu.Enable(self.plot3d_id, menu_enable)
-            self.PopupMenu(self.data_menu) 
-        
-    def onContextMenu(self, event): 
+            self.PopupMenu(self.data_menu)
+
+    def onContextMenu(self, event):
         """
         Retrieve the state selected state
@@ -606,7 +613,7 @@
         pos = event.GetPosition()
         pos = self.ScreenToClient(pos)
-        self.PopupMenu(self.popUpMenu, pos) 
-      
-  
+        self.PopupMenu(self.popUpMenu, pos)
+
+
     def on_check_item(self, event):
         """
@@ -614,5 +621,5 @@
         """
         item = event.GetItem()
-        item.Check(not item.IsChecked()) 
+        item.Check(not item.IsChecked())
         self.enable_append()
         self.enable_freeze()
@@ -621,5 +628,5 @@
         self.enable_remove()
         event.Skip()
-        
+
     def fill_cbox_analysis(self, plugin):
         """
@@ -635,10 +642,10 @@
                 if plug.get_perspective():
                     self.perspective_cbox.Append(plug.sub_menu, plug)
-            
+
             curr_pers = self.parent.get_current_perspective()
             if curr_pers:
                 self.perspective_cbox.SetStringSelection(curr_pers.sub_menu)
                 self.enable_import()
-                        
+
     def load_data_list(self, list):
         """
@@ -654,5 +661,5 @@
                     data_run = str(data.run)
                     data_class = data.__class__.__name__
-                    path = dstate.get_path() 
+                    path = dstate.get_path()
                     process_list = data.process
                     data_id = data.id
@@ -661,18 +668,18 @@
                         #new state
                         data_c = self.tree_ctrl.InsertItem(self.tree_ctrl.root,
-                                        0, data_name, ct_type=1, 
+                                        0, data_name, ct_type=1,
                                         data=(data_id, data_class, state_id))
                         data_c.Check(True)
                         d_i_c = self.tree_ctrl.AppendItem(data_c, 'Info')
-                        d_t_c = self.tree_ctrl.AppendItem(d_i_c, 
+                        d_t_c = self.tree_ctrl.AppendItem(d_i_c,
                                                       'Title: %s' % data_title)
-                        r_n_c = self.tree_ctrl.AppendItem(d_i_c, 
+                        r_n_c = self.tree_ctrl.AppendItem(d_i_c,
                                                       'Run: %s' % data_run)
-                        i_c_c = self.tree_ctrl.AppendItem(d_i_c, 
+                        i_c_c = self.tree_ctrl.AppendItem(d_i_c,
                                                       'Type: %s' % data_class)
                         p_c_c = self.tree_ctrl.AppendItem(d_i_c,
                                                       "Path: '%s'" % s_path)
                         d_p_c = self.tree_ctrl.AppendItem(d_i_c, 'Process')
-                        
+
                         for process in process_list:
                             process_str = str(process).replace('\n',' ')
@@ -680,7 +687,7 @@
                                 process_str = process_str[:20]+' [...]'
                             self.tree_ctrl.AppendItem(d_p_c, process_str)
-                        theory_child = self.tree_ctrl.AppendItem(data_c, 
+                        theory_child = self.tree_ctrl.AppendItem(data_c,
                                                                  "THEORIES")
-                        self.list_cb_data[state_id] = [data_c, 
+                        self.list_cb_data[state_id] = [data_c,
                                                        d_i_c,
                                                        d_t_c,
@@ -695,12 +702,12 @@
                         data_c, d_i_c, d_t_c, r_n_c,  i_c_c, p_c_c, d_p_c, _ \
                                 = data_ctrl_list
-                        self.tree_ctrl.SetItemText(data_c, data_name) 
+                        self.tree_ctrl.SetItemText(data_c, data_name)
                         temp = (data_id, data_class, state_id)
-                        self.tree_ctrl.SetItemPyData(data_c, temp) 
-                        self.tree_ctrl.SetItemText(i_c_c, 
+                        self.tree_ctrl.SetItemPyData(data_c, temp)
+                        self.tree_ctrl.SetItemText(i_c_c,
                                                    'Type: %s' % data_class)
-                        self.tree_ctrl.SetItemText(p_c_c, 
-                                                   'Path: %s' % s_path) 
-                        self.tree_ctrl.DeleteChildren(d_p_c) 
+                        self.tree_ctrl.SetItemText(p_c_c,
+                                                   'Path: %s' % s_path)
+                        self.tree_ctrl.DeleteChildren(d_p_c)
                         for process in process_list:
                             if not process.is_empty():
@@ -710,5 +717,5 @@
             # Sort by data name
             if self.tree_ctrl.root:
-                self.tree_ctrl.SortChildren(self.tree_ctrl.root)    
+                self.tree_ctrl.SortChildren(self.tree_ctrl.root)
         self.enable_remove()
         self.enable_import()
@@ -716,5 +723,5 @@
         self.enable_freeze()
         self.enable_selection()
-        
+
     def _uncheck_all(self):
         """
@@ -723,5 +730,5 @@
         for item in self.list_cb_data.values():
             data_ctrl, _, _, _, _, _, _, _ = item
-            self.tree_ctrl.CheckItem(data_ctrl, False) 
+            self.tree_ctrl.CheckItem(data_ctrl, False)
         self.enable_append()
         self.enable_freeze()
@@ -729,5 +736,5 @@
         self.enable_import()
         self.enable_remove()
-   
+
     def append_theory(self, state_id, theory_list):
         """
@@ -736,5 +743,5 @@
         """
         if not theory_list:
-            return 
+            return
         if state_id not in self.list_cb_data.keys():
             root = self.tree_ctrl_theory.root
@@ -746,9 +753,9 @@
             tree = self.tree_ctrl
         if root is not None:
-            wx.CallAfter(self.append_theory_helper, tree=tree, root=root, 
-                                       state_id=state_id, 
+            wx.CallAfter(self.append_theory_helper, tree=tree, root=root,
+                                       state_id=state_id,
                                        theory_list=theory_list)
-      
-      
+
+
     def append_theory_helper(self, tree, root, state_id, theory_list):
         """
@@ -778,23 +785,23 @@
                                                     name, ct_type=1, data=temp)
                     t_i_c = tree.AppendItem(t_child, 'Info')
-                    i_c_c = tree.AppendItem(t_i_c, 
+                    i_c_c = tree.AppendItem(t_i_c,
                                                   'Type: %s' % theory_class)
                     t_p_c = tree.AppendItem(t_i_c, 'Process')
-                    
+
                     for process in theory_data.process:
                         tree.AppendItem(t_p_c, process.__str__())
-                    theory_list_ctrl[theory_id] = [t_child, 
-                                                   i_c_c, 
+                    theory_list_ctrl[theory_id] = [t_child,
+                                                   i_c_c,
                                                    t_p_c]
                 else:
                     #replace theory
                     t_child, i_c_c, t_p_c = theory_list_ctrl[theory_id]
-                    tree.SetItemText(t_child, name) 
-                    tree.SetItemPyData(t_child, temp) 
-                    tree.SetItemText(i_c_c, 'Type: %s' % theory_class) 
-                    tree.DeleteChildren(t_p_c) 
+                    tree.SetItemText(t_child, name)
+                    tree.SetItemPyData(t_child, temp)
+                    tree.SetItemText(i_c_c, 'Type: %s' % theory_class)
+                    tree.DeleteChildren(t_p_c)
                     for process in theory_data.process:
                         tree.AppendItem(t_p_c, process.__str__())
-              
+
         else:
             #data didn't have a theory associated it before
@@ -807,23 +814,23 @@
                     theory_id = theory_data.id
                     #if theory_state is not None:
-                    #    name = theory_state.model.name 
+                    #    name = theory_state.model.name
                     temp = (theory_id, theory_class, state_id)
                     t_child = tree.AppendItem(root,
-                            name, ct_type=1, 
+                            name, ct_type=1,
                             data=(theory_data.id, theory_class, state_id))
                     t_i_c = tree.AppendItem(t_child, 'Info')
-                    i_c_c = tree.AppendItem(t_i_c, 
+                    i_c_c = tree.AppendItem(t_i_c,
                                                   'Type: %s' % theory_class)
                     t_p_c = tree.AppendItem(t_i_c, 'Process')
-                    
+
                     for process in theory_data.process:
                         tree.AppendItem(t_p_c, process.__str__())
-            
+
                     theory_list_ctrl[theory_id] = [t_child, i_c_c, t_p_c]
                 #self.list_cb_theory[data_id] = theory_list_ctrl
                 self.list_cb_theory[state_id] = theory_list_ctrl
-        
-            
-   
+
+
+
     def set_data_helper(self):
         """
@@ -840,5 +847,5 @@
                 if state_id not in state_to_plot:
                     state_to_plot.append(state_id)
-           
+
         for theory_dict in self.list_cb_theory.values():
             for _, value in theory_dict.iteritems():
@@ -850,5 +857,5 @@
                         state_to_plot.append(state_id)
         return data_to_plot, theory_to_plot, state_to_plot
-    
+
     def remove_by_id(self, id):
         """
@@ -857,22 +864,22 @@
         for item in self.list_cb_data.values():
             data_c, _, _, _, _, _,  _, _ = item
-            data_id, _, state_id = self.tree_ctrl.GetItemPyData(data_c) 
+            data_id, _, state_id = self.tree_ctrl.GetItemPyData(data_c)
             if id == data_id:
                 self.tree_ctrl.Delete(data_c)
                 del self.list_cb_data[state_id]
                 del self.list_cb_theory[data_id]
-              
+
     def load_error(self, error=None):
         """
         Pop up an error message.
-        
+
         :param error: details error message to be displayed
         """
         if error is not None or str(error).strip() != "":
-            dial = wx.MessageDialog(self.parent, str(error), 
+            dial = wx.MessageDialog(self.parent, str(error),
                                     'Error Loading File',
                                     wx.OK | wx.ICON_EXCLAMATION)
-            dial.ShowModal()  
-        
+            dial.ShowModal()
+
     def _load_data(self, event):
         """
@@ -881,10 +888,10 @@
         if self.parent is not None:
             wx.PostEvent(self.parent, NewLoadDataEvent())
-            
+
 
     def on_remove(self, event):
         """
         Get a list of item checked and remove them from the treectrl
-        Ask the parent to remove reference to this item 
+        Ask the parent to remove reference to this item
         """
         msg = "This operation will delete the data sets checked "
@@ -893,5 +900,5 @@
         if msg_box.ShowModal() != wx.ID_OK:
             return
-        
+
         data_to_remove, theory_to_remove, _ = self.set_data_helper()
         data_key = []
@@ -906,5 +913,5 @@
                     theory_list_ctrl = self.list_cb_theory[d_key]
                     theory_to_remove += theory_list_ctrl.keys()
-        # Remove theory from treectrl       
+        # Remove theory from treectrl
         for _, theory_dict in self.list_cb_theory.iteritems():
             for  key, value in theory_dict.iteritems():
@@ -916,5 +923,5 @@
                         pass
                     theory_key.append(key)
-                    
+
         #Remove data and related theory references
         for key in data_key:
@@ -934,6 +941,6 @@
                                 pass
                     del theory_dict[key]
-                    
-            
+
+
         self.parent.remove_data(data_id=data_to_remove,
                                   theory_id=theory_to_remove)
@@ -941,5 +948,5 @@
         self.enable_freeze()
         self.enable_remove_plot()
-        
+
     def on_import(self, event=None):
         """
@@ -951,5 +958,5 @@
         temp = data_id + state_id
         self.parent.set_data(data_id=temp, theory_id=theory_id)
-        
+
     def on_append_plot(self, event=None):
         """
@@ -958,9 +965,9 @@
         self._on_plot_selection()
         data_id, theory_id, state_id = self.set_data_helper()
-        self.parent.plot_data(data_id=data_id,  
+        self.parent.plot_data(data_id=data_id,
                               state_id=state_id,
                               theory_id=theory_id,
                               append=True)
-   
+
     def on_plot(self, event=None):
         """
@@ -968,10 +975,10 @@
         """
         data_id, theory_id, state_id = self.set_data_helper()
-        self.parent.plot_data(data_id=data_id,  
+        self.parent.plot_data(data_id=data_id,
                               state_id=state_id,
                               theory_id=theory_id,
                               append=False)
         self.enable_remove_plot()
-         
+
     def on_close_page(self, event=None):
         """
@@ -982,5 +989,5 @@
         # send parent to update menu with no show nor hide action
         self.parent.show_data_panel(action=False)
-    
+
     def on_freeze(self, event):
         """
@@ -995,5 +1002,5 @@
             msg = "Freeze Theory: Requires at least one theory checked."
         wx.PostEvent(self.parent, StatusEvent(status=msg))
-            
+
     def set_active_perspective(self, name):
         """
@@ -1002,8 +1009,8 @@
         self.perspective_cbox.SetStringSelection(name)
         self.enable_import()
-        
+
     def _on_delete_plot_panel(self, event):
         """
-        get an event with attribute name and caption to delete existing name 
+        get an event with attribute name and caption to delete existing name
         from the combobox of the current panel
         """
@@ -1011,9 +1018,9 @@
         caption = event.caption
         if self.cb_plotpanel is not None:
-            pos = self.cb_plotpanel.FindString(str(caption)) 
+            pos = self.cb_plotpanel.FindString(str(caption))
             if pos != wx.NOT_FOUND:
                 self.cb_plotpanel.Delete(pos)
         self.enable_append()
-        
+
     def set_panel_on_focus(self, name=None):
         """
@@ -1031,5 +1038,5 @@
         self.enable_append()
         self.enable_remove_plot()
-    
+
     def set_plot_unfocus(self):
         """
@@ -1037,5 +1044,5 @@
         """
         return
-    
+
     def _on_perspective_selection(self, event=None):
         """
@@ -1047,5 +1054,5 @@
             perspective.on_perspective(event=None)
             self.parent.check_multimode(perspective=perspective)
-                
+
     def _on_plot_selection(self, event=None):
         """
@@ -1061,10 +1068,10 @@
         if combo.GetValue() != 'None':
             panel = combo.GetClientData(selection)
-            self.parent.on_set_plot_focus(panel)   
-            
+            self.parent.on_set_plot_focus(panel)
+
     def on_close_plot(self, event):
         """
         clseo the panel on focus
-        """ 
+        """
         self.enable_append()
         selection = self.cb_plotpanel.GetSelection()
@@ -1072,19 +1079,19 @@
             panel = self.cb_plotpanel.GetClientData(selection)
             if self.parent is not None and panel is not None:
-                wx.PostEvent(self.parent, 
+                wx.PostEvent(self.parent,
                              NewPlotEvent(group_id=panel.group_id,
                                           action="delete"))
         self.enable_remove_plot()
-    
+
     def set_frame(self, frame):
         """
         """
         self.frame = frame
-    
+
     def get_frame(self):
         """
         """
-        return self.frame 
-    
+        return self.frame
+
     def on_help(self, event):
         """
@@ -1115,5 +1122,5 @@
         """
         self.parent.show_data_panel(event)
-                        
+
     def set_schedule_full_draw(self, panel=None, func='del'):
         """
@@ -1121,5 +1128,5 @@
         """
         self.parent.set_schedule_full_draw(panel, func)
-        
+
     def enable_remove_plot(self):
         """
@@ -1131,5 +1138,5 @@
         #else:
         #    self.bt_close_plot.Enable()
-            
+
     def enable_remove(self):
         """
@@ -1142,5 +1149,5 @@
         else:
             self.bt_remove.Enable()
-            
+
     def enable_import(self):
         """
@@ -1160,10 +1167,10 @@
         else:
             self.perspective_cbox.Enable()
-            
+
     def enable_plot(self):
         """
         enable or disable plot button
         """
-        n_t = 0 
+        n_t = 0
         n_t_t = 0
         if self.tree_ctrl != None:
@@ -1176,10 +1183,10 @@
             self.bt_plot.Enable()
         self.enable_append()
-       
+
     def enable_append(self):
         """
         enable or disable append button
         """
-        n_t = 0 
+        n_t = 0
         n_t_t = 0
         if self.tree_ctrl != None:
@@ -1187,5 +1194,5 @@
         if self.tree_ctrl_theory != None:
             n_t_t = self.tree_ctrl_theory.GetCount()
-        if n_t + n_t_t <= 0: 
+        if n_t + n_t_t <= 0:
             self.bt_append_plot.Disable()
             self.cb_plotpanel.Disable()
@@ -1196,5 +1203,5 @@
             self.bt_append_plot.Enable()
             self.cb_plotpanel.Enable()
-            
+
     def check_theory_to_freeze(self):
         """
@@ -1214,5 +1221,5 @@
         else:
             self.bt_freeze.Disable()
-        
+
     def enable_selection(self):
         """
@@ -1229,8 +1236,8 @@
         else:
             self.selection_cbox.Disable()
-            
+
     def show_data_button(self):
         """
-        show load data and remove data button if 
+        show load data and remove data button if
         dataloader on else hide them
         """
@@ -1238,14 +1245,14 @@
             gui_style = self.parent.get_style()
             style = gui_style & GUIFRAME.DATALOADER_ON
-            if style == GUIFRAME.DATALOADER_ON: 
+            if style == GUIFRAME.DATALOADER_ON:
                 #self.bt_remove.Show(True)
-                self.bt_add.Show(True) 
+                self.bt_add.Show(True)
             else:
                 #self.bt_remove.Hide()
                 self.bt_add.Hide()
-        except: 
+        except:
             #self.bt_remove.Hide()
-            self.bt_add.Hide() 
-    
+            self.bt_add.Hide()
+
 
 
@@ -1264,5 +1271,5 @@
         self.list_of_ctrl = []
         if not data_list:
-            return 
+            return
         self._sizer_main = wx.BoxSizer(wx.VERTICAL)
         self._sizer_txt = wx.BoxSizer(wx.VERTICAL)
@@ -1273,5 +1280,5 @@
         self._panel.SetupScrolling()
         self.__do_layout(data_list, text=text)
-        
+
     def __do_layout(self, data_list, text=''):
         """
@@ -1279,7 +1286,7 @@
         """
         if not data_list or len(data_list) <= 1:
-            return 
+            return
         #add text
-        
+
         text = "Deleting these file reset some panels.\n"
         text += "Do you want to proceed?\n"
@@ -1315,8 +1322,8 @@
                                 wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)
         static_line = wx.StaticLine(self, -1)
-        
+
         self._sizer_txt.Add(self._panel, 1, wx.EXPAND|wx.LEFT|wx.RIGHT, 5)
         self._sizer_main.Add(self._sizer_txt, 1, wx.EXPAND|wx.ALL, 10)
-        #self._sizer_main.Add(self._data_text_ctrl, 0, 
+        #self._sizer_main.Add(self._data_text_ctrl, 0,
         #                     wx.EXPAND|wx.LEFT|wx.RIGHT, 10)
         self._sizer_main.Add(static_line, 0, wx.EXPAND, 0)
@@ -1324,5 +1331,5 @@
         self.SetSizer(self._sizer_main)
         self.Layout()
-        
+
     def get_data(self):
         """
@@ -1335,5 +1342,5 @@
                 temp.append(data)
         return temp
-               
+
 class DataFrame(wx.Frame):
     """
@@ -1347,5 +1354,5 @@
     #  tied to any perspective
     ALWAYS_ON = True
-    
+
     def __init__(self, parent=None, owner=None, manager=None, size=(300, 800),
                          list_of_perspective=[], list=[], *args, **kwds):
@@ -1357,8 +1364,8 @@
         self.owner = owner
         self._manager = manager
-        self.panel = DataPanel(parent=self, 
+        self.panel = DataPanel(parent=self,
                                manager=manager,
                                list_of_perspective=list_of_perspective)
-     
+
     def load_data_list(self, list=[]):
         """
@@ -1366,7 +1373,7 @@
         """
         self.panel.load_data_list(list=list)
-        
-   
-    
+
+
+
 from sas.sasgui.guiframe.dataFitting import Theory1D
 from sas.sasgui.guiframe.data_state import DataState
@@ -1383,5 +1390,5 @@
         self.msg += "name  value\n"
         return self.msg
-    
+
 def set_data_state(data=None, path=None, theory=None, state=None):
     """
@@ -1391,9 +1398,9 @@
     dstate.set_path(path=path)
     dstate.set_theory(theory, state)
-  
+
     return dstate
-    
+
 if __name__ == "__main__":
-    
+
     app = wx.App()
     try:
@@ -1477,6 +1484,4 @@
         #raise
         print "error", sys.exc_value
-        
-    app.MainLoop()  
-    
-    
+
+    app.MainLoop()
Index: src/sas/sasgui/guiframe/local_perspectives/data_loader/data_loader.py
===================================================================
--- src/sas/sasgui/guiframe/local_perspectives/data_loader/data_loader.py	(revision faa3ae7bb2790a5fa3ceae0690d20ed2c371b3af)
+++ src/sas/sasgui/guiframe/local_perspectives/data_loader/data_loader.py	(revision a674d0b7da37ed36693004d2d51e0941ed38842f)
@@ -162,5 +162,5 @@
             logging.error("Loader returned an invalid object:\n %s" % str(item))
             data_error = True
-        
+
         data = self.parent.create_gui_data(item, p_file)
         output[data.id] = data
@@ -170,69 +170,69 @@
         """
         """
-        message = ""
-        log_msg = ''
+        file_errors = {}
         output = {}
-        any_error = False
-        data_error = False
-        error_message = ""
+        exception_occurred = False
+
         for p_file in path:
-            info = "info"
             basename = os.path.basename(p_file)
             _, extension = os.path.splitext(basename)
             if extension.lower() in EXTENSIONS:
-                any_error = True
                 log_msg = "Data Loader cannot "
-                log_msg += "load: %s\n" % str(p_file)
-                log_msg += """Please try to open that file from "open project" """
-                log_msg += """or "open analysis" menu\n"""
-                error_message = log_msg + "\n"
+                log_msg += "load: {}\n".format(str(p_file))
+                log_msg += "Please try to open that file from \"open project\""
+                log_msg += "or \"open analysis\" menu."
                 logging.info(log_msg)
+                file_errors[basename] = [log_msg]
                 continue
 
             try:
-                message = "Loading Data... " + str(p_file) + "\n"
-                self.load_update(output=output, message=message, info=info)
+                message = "Loading {}...\n".format(p_file)
+                self.load_update(output=output, message=message, info="info")
                 temp = self.loader.load(p_file, format)
-                if temp.__class__.__name__ == "list":
-                    for item in temp:
-                        output, error_message, data_error = \
-                            self._process_data_and_errors(item,
-                                                          p_file,
-                                                          output,
-                                                          error_message)
-                else:
+                if not isinstance(temp, list):
+                    temp = [temp]
+                for item in temp:
+                    error_message = ""
                     output, error_message, data_error = \
-                            self._process_data_and_errors(temp,
-                                                          p_file,
-                                                          output,
-                                                          error_message)
+                        self._process_data_and_errors(item,
+                                                      p_file,
+                                                      output,
+                                                      error_message)
+                    if data_error:
+                        if basename in file_errors.keys():
+                            file_errors[basename] += [error_message]
+                        else:
+                            file_errors[basename] = [error_message]
+                        self.load_update(output=output,
+                            message=error_message, info="warning")
+
+                self.load_update(output=output,
+                message="Loaded {}\n".format(p_file),
+                info="info")
+
             except:
                 logging.error(sys.exc_value)
-                any_error = True
-            if any_error or error_message != "":
-                if error_message == "":
-                    error = "Error: " + str(sys.exc_info()[1]) + "\n"
-                    error += "while loading Data: \n%s\n" % str(basename)
-                    error_message += "The data file you selected could not be loaded.\n"
-                    error_message += "Make sure the content of your file"
-                    error_message += " is properly formatted.\n\n"
-                    error_message += "When contacting the SasView team, mention the"
-                    error_message += " following:\n%s" % str(error)
-                elif data_error:
-                    base_message = "Errors occurred while loading "
-                    base_message += "{0}\n".format(basename)
-                    base_message += "The data file loaded but with errors.\n"
-                    error_message = base_message + error_message
-                else:
-                    error_message += "%s\n" % str(p_file)
-                info = "error"
-        
-        if any_error or error_message:
-            self.load_update(output=output, message=error_message, info=info)
-        else:
-            message = "Loading Data Complete! "
-        message += log_msg
-        self.load_complete(output=output, error_message=error_message,
-                           message=message, path=path, info='info')
+
+                error_message = "The Data file you selected could not be loaded.\n"
+                error_message += "Make sure the content of your file"
+                error_message += " is properly formatted.\n"
+                error_message += "When contacting the SasView team, mention the"
+                error_message += " following:\n"
+                error_message += "Error: " + str(sys.exc_info()[1])
+                file_errors[basename] = [error_message]
+                self.load_update(output=output, message=error_message, info="warning")
+
+        if len(file_errors) > 0:
+            error_message = ""
+            for filename, error_array in file_errors.iteritems():
+                error_message += "The following errors occured whilst "
+                error_message += "loading {}:\n".format(filename)
+                for message in error_array:
+                    error_message += message + "\n"
+                error_message += "\n"
+            self.load_update(output=output, message=error_message, info="error")
+
+        self.load_complete(output=output, message="Loading data complete!",
+            info="info")
 
     def load_update(self, output=None, message="", info="warning"):
@@ -254,5 +254,2 @@
         #    self.load_error(error_message)
         self.parent.add_data(data_list=output)
-
-
-
Index: src/sas/sasgui/guiframe/local_perspectives/plotting/Plotter1D.py
===================================================================
--- src/sas/sasgui/guiframe/local_perspectives/plotting/Plotter1D.py	(revision efb482a6105c6af4b1617b1e7f47252e2146e7bb)
+++ src/sas/sasgui/guiframe/local_perspectives/plotting/Plotter1D.py	(revision 895c9cb3dcc39d7f29513916f04e9863484692c8)
@@ -552,4 +552,5 @@
             self.subplot.set_xlim((xlo, xhi))
             self.subplot.set_ylim((ylo, yhi))
+        self.graph.selected_plottable = None
 
 
@@ -629,8 +630,9 @@
 
             if self.parent.ClassName.count('wxDialog') == 0:
-                wx_id = ids.next()
-                plot_menu.Append(wx_id, '&Linear Fit', name)
-                wx.EVT_MENU(self, wx_id, self.onFitting)
-                plot_menu.AppendSeparator()
+                if plot.id != 'fit':
+                    wx_id = ids.next()
+                    plot_menu.Append(wx_id, '&Linear Fit', name)
+                    wx.EVT_MENU(self, wx_id, self.onFitting)
+                    plot_menu.AppendSeparator()
 
                 wx_id = ids.next()
@@ -694,4 +696,7 @@
         self._slicerpop.AppendSeparator()
         wx_id = ids.next()
+        self._slicerpop.Append(wx_id, '&Set Graph Range')
+        wx.EVT_MENU(self, wx_id, self.onSetRange)
+        wx_id = ids.next()
         self._slicerpop.Append(wx_id, '&Reset Graph Range')
         wx.EVT_MENU(self, wx_id, self.onResetGraph)
@@ -710,4 +715,23 @@
         self.PopupMenu(self._slicerpop, pos)
 
+    def onSetRange(self, event):
+        # Display dialog
+        # self.subplot.set_xlim((low, high))
+        # self.subplot.set_ylim((low, high))
+        from sas.sasgui.plottools.RangeDialog import RangeDialog
+        d = RangeDialog(self, -1)
+        xlim = self.subplot.get_xlim()
+        ylim = self.subplot.get_ylim()
+        d.SetXRange(xlim)
+        d.SetYRange(ylim)
+        if d.ShowModal() == wx.ID_OK:
+            x_range = d.GetXRange()
+            y_range = d.GetYRange()
+            if x_range is not None and y_range is not None:
+                self.subplot.set_xlim(x_range)
+                self.subplot.set_ylim(y_range)
+                self.subplot.figure.canvas.draw_idle()
+        d.Destroy()
+
     def onFreeze(self, event):
         """
@@ -796,4 +820,5 @@
                                                              int(curr_symbol))), curr_label)
         self.appD.Bind(wx.EVT_CLOSE, self.on_AppDialog_close)
+        self.graph.selected_plottable = None
 
     def on_AppDialog_close(self, event):
Index: src/sas/sasgui/guiframe/media/data_explorer_help.rst
===================================================================
--- src/sas/sasgui/guiframe/media/data_explorer_help.rst	(revision d85c194ea2f78cb2b9b3de9c969e1b8ebea6b6d2)
+++ src/sas/sasgui/guiframe/media/data_explorer_help.rst	(revision b64b87c874ab0edc565e9c415decc839e3214c8c)
@@ -20,5 +20,5 @@
 
 *NOTE! When* Data Explorer *is hidden, all data loaded will be sent directly 
-to the current active analysis perspective, if possible. When* Data Explorer *is
+to the current active analysis, if possible. When* Data Explorer *is
 shown, data go first to the* Data Explorer.
 
@@ -116,7 +116,7 @@
 
 Click on the *Send To* button to send the currently selected data to one of the
-perspectives (for *Fitting*, *P(r) Inversion*, or *Invariant* calculation).
+available types of analysis (*Fitting*, *P(r) Inversion*, or *Invariant* calculation).
  
-The *Single*/*Batch* mode radio buttons only apply to the *Fitting* perspective.
+The *Single*/*Batch* mode radio buttons only apply to *Fitting*.
 
 *Batch mode* provides serial (batch) fitting with one model function, that is, 
Index: src/sas/sasgui/guiframe/media/graph_help.rst
===================================================================
--- src/sas/sasgui/guiframe/media/graph_help.rst	(revision f93b473fdbbdf1cc25efbb46c7adadea0e7d276d)
+++ src/sas/sasgui/guiframe/media/graph_help.rst	(revision e68c9bf288ff7df58f530665fe0f348055904d17)
@@ -20,6 +20,6 @@
 ^^^^^^^^^^^^^^^^^^^^^^^
 
-To invoke the *Graph Menu* simply right-click on a data/theory plot, or click 
-the *Graph Menu* (bullet list) icon in the toolbar at the bottom of the plot. 
+To invoke the *Graph Menu* simply right-click on a data/theory plot, or click
+the *Graph Menu* (bullet list) icon in the toolbar at the bottom of the plot.
 Then select a menu item.
 
@@ -27,20 +27,20 @@
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-To expand a plot window, click the *Maximise* (square) icon in the top-right 
+To expand a plot window, click the *Maximise* (square) icon in the top-right
 corner.
 
-To shrink a plot window, click the *Restore down* (square-on-square) icon in 
+To shrink a plot window, click the *Restore down* (square-on-square) icon in
 the top-right corner.
 
-To hide a plot, click the *Minimise* (-) icon in the top-right corner of the 
+To hide a plot, click the *Minimise* (-) icon in the top-right corner of the
 plot window.
 
-To show a hidden plot, select the *Restore up* (square-on-square) icon on the 
+To show a hidden plot, select the *Restore up* (square-on-square) icon on the
 minimised window.
 
-To delete a plot, click the *Close* (x) icon in the top-right corner of the 
+To delete a plot, click the *Close* (x) icon in the top-right corner of the
 plot window.
 
-*NOTE! If a residuals graph (when fitting data) is hidden, it will not show up 
+*NOTE! If a residuals graph (when fitting data) is hidden, it will not show up
 after computation.*
 
@@ -48,9 +48,9 @@
 ^^^^^^^^^^^^^^^
 
-Select the *Pan* (crossed arrows) icon in the toolbar at the bottom of the plot 
-to activate this option. Move the mouse pointer to the plot. It will change to 
-a hand. Then left-click and drag the plot around. The axis values will adjust 
+Select the *Pan* (crossed arrows) icon in the toolbar at the bottom of the plot
+to activate this option. Move the mouse pointer to the plot. It will change to
+a hand. Then left-click and drag the plot around. The axis values will adjust
 accordingly.
- 
+
 To disable dragging mode, unselect the *crossed arrows* icon on the toolbar.
 
@@ -58,19 +58,19 @@
 ^^^^^^^^^^^^^^^^^^^^^^^^
 
-Select the *Zoom* (magnifying glass) button in the toolbar at the bottom of 
-the plot to activate this option. Move the mouse pointer to the plot. It will 
-change to a cross-hair. Then left-click and drag the pointer around to generate 
+Select the *Zoom* (magnifying glass) button in the toolbar at the bottom of
+the plot to activate this option. Move the mouse pointer to the plot. It will
+change to a cross-hair. Then left-click and drag the pointer around to generate
 a region of interest. Release the mouse button to generate the new view.
 
 To disable zoom mode, unselect the *Zoom* button on the toolbar.
 
-After zooming in on a a region, the *left arrow* or *right arrow* buttons on 
+After zooming in on a a region, the *left arrow* or *right arrow* buttons on
 the toolbar will switch between recent views.
 
-*NOTE! If a wheel mouse is available scrolling the wheel will zoom in/out 
-on the current plot (changing both axes). Alternatively, point at the numbers 
+*NOTE! If a wheel mouse is available scrolling the wheel will zoom in/out
+on the current plot (changing both axes). Alternatively, point at the numbers
 on one axis and scroll the wheel to zoom in/out on just that axis.*
 
-To return to the original view of the data, click the the *Reset* (home) icon 
+To return to the original view of the data, click the the *Reset* (home) icon
 in the toolbar at the bottom of the plot (see Resetting_the_graph_ for further details).
 
@@ -78,10 +78,10 @@
 ^^^^^^^^^^^^^^^^^^^
 
-To save the current plot as an image file, right click on the plot to bring up 
+To save the current plot as an image file, right click on the plot to bring up
 the *Graph Menu* (see Invoking_the_graph_menu_) and select *Save Image*.
-Alternatively, click on the *Save* (floppy disk) icon in the toolbar at the 
+Alternatively, click on the *Save* (floppy disk) icon in the toolbar at the
 bottom of the plot.
- 
-A dialog window will open. Select a folder, enter a filename, choose an output 
+
+A dialog window will open. Select a folder, enter a filename, choose an output
 image type, and click *Save*.
 
@@ -94,5 +94,5 @@
 *  PNG (portable network graphics)
 *  PS (postscript)
-*  RAW/RGBA (bitmap)
+*  RAW/RGBA (bitmap, stored as 935x635 pixels of depth 8)
 *  SVG/SVGA (scalable vector graphics)
 *  TIF/TIFF (tagged iamge file)
@@ -101,5 +101,5 @@
 ^^^^^^^^^^^^^^^
 
-To send the current plot to a printer, click on the *Print* (printer) icon in 
+To send the current plot to a printer, click on the *Print* (printer) icon in
 the toolbar at the bottom of the plot.
 
@@ -109,5 +109,5 @@
 ^^^^^^^^^^^^^^^^^^^
 
-To reset the axis range of a graph to its initial values select *Reset Graph 
+To reset the axis range of a graph to its initial values select *Reset Graph
 Range* on the *Graph Menu* (see Invoking_the_graph_menu_). Alternatively, use
 the *Reset* (home) icon in the toolbar at the bottom of the plot.
@@ -133,5 +133,5 @@
 
 From the *Graph Menu* (see Invoking_the_graph_menu_) select *Change Scale*. A
-dialog window will appear in which it is possible to choose different 
+dialog window will appear in which it is possible to choose different
 transformations of the x (usually Q) or y (usually I(Q)) axes, including:
 
@@ -139,5 +139,5 @@
 *  y, 1/y, ln(y), y^2, y.(x^4), 1/sqrt(y),
 *  log10(y), ln(y.x), ln(y.x^2), ln(y.x^4), log10(y.x^4)
- 
+
 A *View* option includes short-cuts to common SAS transformations, such as:
 
@@ -148,6 +148,6 @@
 *  Kratky
 
-For properly corrected and scaled data, these SAS transformations can be used 
-to estimate, for example, Rg, rod diameter, or SANS incoherent background 
+For properly corrected and scaled data, these SAS transformations can be used
+to estimate, for example, Rg, rod diameter, or SANS incoherent background
 levels, via a linear fit (see Making_a_linear_fit_).
 
@@ -158,5 +158,5 @@
 
 From the *Graph Menu* (see Invoking_the_graph_menu_) select *Toggle Linear/Log
-Scale* to switch between a linear to log intensity scale. The type of scale 
+Scale* to switch between a linear to log intensity scale. The type of scale
 selected is written alongside the colour scale.
 
@@ -167,5 +167,5 @@
 
 From the *Graph Menu* (see Invoking_the_graph_menu_) select *2D Color Map* to
-choose a different color scale for the image and/or change the maximum or 
+choose a different color scale for the image and/or change the maximum or
 minimum limits of the scale.
 
@@ -173,7 +173,7 @@
 ^^^^^^^^^^^^^^^^^^^^^^^^
 
-Clicking anywhere in the plot window will cause the current coordinates to be 
+Clicking anywhere in the plot window will cause the current coordinates to be
 displayed in the status bar at the very bottom-left of the SasView window.
- 
+
 .. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
 
@@ -193,5 +193,5 @@
 
 In the *Dataset Menu* (see Invoking_the_dataset_menu_), highlight a data set
-and select *DataInfo* to bring up a data information dialog panel for that 
+and select *DataInfo* to bring up a data information dialog panel for that
 data set.
 
@@ -200,8 +200,8 @@
 
 In the *Dataset Menu* (see Invoking_the_dataset_menu_), select *Save Points as
-a File* (if 1D data) or *Save as a file(DAT)* (if 2D data). A save dialog will 
+a File* (if 1D data) or *Save as a file(DAT)* (if 2D data). A save dialog will
 appear.
 
-1D data can be saved in either ASCII text (.TXT) or CanSAS/SASXML (.XML) 
+1D data can be saved in either ASCII text (.TXT) or CanSAS/SASXML (.XML)
 formats (see :ref:`Formats`).
 
@@ -216,10 +216,10 @@
 
 In the *Dataset Menu* (see Invoking_the_dataset_menu_), select *Linear Fit*. A
-fitting dialog will appear. Set some initial parameters and data limits and 
-click *Fit*. The fitted parameter values are displayed and the resulting line 
-calculated from them is added to the plot. 
+fitting dialog will appear. Set some initial parameters and data limits and
+click *Fit*. The fitted parameter values are displayed and the resulting line
+calculated from them is added to the plot.
 
 This option is most useful for performing simple Guinier, XS Guinier, and
-Porod type analyses, for example, to estimate Rg, a rod diameter, or incoherent 
+Porod type analyses, for example, to estimate Rg, a rod diameter, or incoherent
 background level, respectively.
 
@@ -240,6 +240,6 @@
 
 In the *Dataset Menu* (see Invoking_the_dataset_menu_), select *Show Error Bar*
-or *Hide Error Bar* to switch between showing/hiding the errors associated 
-with the chosen dataset. 
+or *Hide Error Bar* to switch between showing/hiding the errors associated
+with the chosen dataset.
 
 Modify plot properties
@@ -247,5 +247,5 @@
 
 In the *Dataset Menu* (see Invoking_the_dataset_menu_), select *Modify Plot
-Property* to change the size, color, or shape of the displayed marker for the 
+Property* to change the size, color, or shape of the displayed marker for the
 chosen dataset, or to change the dataset label that appears on the plot.
 
@@ -260,6 +260,6 @@
 This feature is only available with 2D data.
 
-2D data averaging allows you to perform different types of averages on your 
-data. The region to be averaged is displayed in the plot window and its limits 
+2D data averaging allows you to perform different types of averages on your
+data. The region to be averaged is displayed in the plot window and its limits
 can be modified by dragging the boundaries around.
 
@@ -277,23 +277,23 @@
 *  Box averaging on Qy
 
-A 'slicer' will appear (except for *Perform Circular Average*) in the plot that 
-you can drag by clicking on a slicer's handle. When the handle is highlighted 
+A 'slicer' will appear (except for *Perform Circular Average*) in the plot that
+you can drag by clicking on a slicer's handle. When the handle is highlighted
 in red, it means that the slicer can move/change size.
 
-*NOTE! The slicer size will reset if you try to select a region greater than 
+*NOTE! The slicer size will reset if you try to select a region greater than
 the size of the data.*
 
-Alternatively, once a 'slicer' is active you can also select the region to 
-average by bringing back the *Dataset Menu* and selecting *Edit Slicer 
-Parameters*. A dialog window will appear in which you can enter values to 
+Alternatively, once a 'slicer' is active you can also select the region to
+average by bringing back the *Dataset Menu* and selecting *Edit Slicer
+Parameters*. A dialog window will appear in which you can enter values to
 define a region or select the number of points to plot (*nbins*).
 
-A separate plot window will also have appeared, displaying the requested 
+A separate plot window will also have appeared, displaying the requested
 average.
 
-*NOTE! The displayed average only updates when input focus is moved back to 
+*NOTE! The displayed average only updates when input focus is moved back to
 that window; ie, when the mouse pointer is moved onto that plot.*
 
-Selecting *Box Sum* automatically brings up the 'Slicer Parameters' dialog in 
+Selecting *Box Sum* automatically brings up the 'Slicer Parameters' dialog in
 order to display the average numerically, rather than graphically.
 
@@ -303,5 +303,5 @@
 ^^^^^^^^^^^^^^^^^^^^^^^^^
 
-This operation will perform an average in constant Q-rings around the (x,y) 
+This operation will perform an average in constant Q-rings around the (x,y)
 pixel location of the beam center.
 
@@ -309,5 +309,5 @@
 ^^^^^^^^^^^^^^^^^^^^^^^
 
-This operation is the same as 'Unmasked Circular Average' except that any 
+This operation is the same as 'Unmasked Circular Average' except that any
 masked region is excluded.
 
@@ -317,5 +317,5 @@
 This operation averages in constant Q-arcs.
 
-The width of the sector is specified in degrees (+/- |delta|\|phi|\) each side 
+The width of the sector is specified in degrees (+/- |delta|\|phi|\) each side
 of the central angle (|phi|\).
 
@@ -323,8 +323,8 @@
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-This operation performs an average between two Q-values centered on (0,0), 
+This operation performs an average between two Q-values centered on (0,0),
 and averaged over a specified number of pixels.
 
-The data is returned as a function of angle (|phi|\) in degrees with zero 
+The data is returned as a function of angle (|phi|\) in degrees with zero
 degrees at the 3 O'clock position.
 
@@ -334,5 +334,5 @@
 This operation performs a sum of counts in a 2D region of interest.
 
-When editing the slicer parameters, the user can enter the length and the width 
+When editing the slicer parameters, the user can enter the length and the width
 the rectangular slicer and the coordinates of the center of the rectangle.
 
@@ -342,7 +342,7 @@
 This operation computes an average I(Qx) for the region of interest.
 
-When editing the slicer parameters, the user can control the length and the 
-width the rectangular slicer. The averaged output is calculated from constant 
-bins with rectangular shape. The resultant Q values are nominal values, that 
+When editing the slicer parameters, the user can control the length and the
+width the rectangular slicer. The averaged output is calculated from constant
+bins with rectangular shape. The resultant Q values are nominal values, that
 is, the central value of each bin on the x-axis.
 
@@ -352,7 +352,7 @@
 This operation computes an average I(Qy) for the region of interest.
 
-When editing the slicer parameters, the user can control the length and the 
-width the rectangular slicer. The averaged output is calculated from constant 
-bins with rectangular shape. The resultant Q values are nominal values, that 
+When editing the slicer parameters, the user can control the length and the
+width the rectangular slicer. The averaged output is calculated from constant
+bins with rectangular shape. The resultant Q values are nominal values, that
 is, the central value of each bin on the x-axis.
 
Index: src/sas/sasgui/perspectives/fitting/basepage.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/basepage.py	(revision 5213d22cc2537cc6875bff5b448ef1c8caadfcd8)
+++ src/sas/sasgui/perspectives/fitting/basepage.py	(revision e4c897b7e24c79352d6e923b47adad24f96490b5)
@@ -2942,5 +2942,5 @@
         """
 
-        _TreeLocation = "user/sasgui/perspectives/fitting/mag_help.html"
+        _TreeLocation = "user/magnetism.html"
         _doc_viewer = DocumentationWindow(self, wx.ID_ANY, _TreeLocation, "",
                                           "Polarized Beam/Magnetc Help")
Index: src/sas/sasgui/perspectives/fitting/media/fitting_help.rst
===================================================================
--- src/sas/sasgui/perspectives/fitting/media/fitting_help.rst	(revision 20846beab226711e5cf48fce6d78e1fccc98a688)
+++ src/sas/sasgui/perspectives/fitting/media/fitting_help.rst	(revision b64b87c874ab0edc565e9c415decc839e3214c8c)
@@ -15,6 +15,6 @@
 
 
-Fitting Perspective
-===================
+Fitting
+=======
 
 .. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
@@ -24,6 +24,5 @@
 
 To fit some data you must first load some data, activate one or more data sets,
-send those data sets to the fitting perspective, and select a model to fit to
-each data set.
+send those data sets to fitting, and select a model to fit to each data set.
 
 Instructions on how to load and activate data are in the section :ref:`Loading_data`.
@@ -331,6 +330,5 @@
 This mode fits one data set.
 
-When data is sent to the fitting perspective it is plotted in a graph window as
-markers.
+When data is sent to the fitting it is plotted in a graph window as markers.
 
 If a graph does not appear, or a graph window appears but is empty, then the data
Index: src/sas/sasgui/perspectives/fitting/media/pd_help.rst
===================================================================
--- src/sas/sasgui/perspectives/fitting/media/pd_help.rst	(revision 780545820bf596bf3709c434f3eb26bcc29a1236)
+++ src/sas/sasgui/perspectives/fitting/media/pd_help.rst	(revision b64b87c874ab0edc565e9c415decc839e3214c8c)
@@ -105,5 +105,5 @@
 
 The median value for the distribution will be the value given for the respective
-size parameter in the *Fitting Perspective*, for example, radius = 60.
+size parameter in the *FitPage*, for example, radius = 60.
 
 The polydispersity is given by |sigma|
@@ -172,5 +172,5 @@
 
 SasView only uses these array values during the computation, therefore any mean
-value of the parameter represented by *x* present in the *Fitting Perspective*
+value of the parameter represented by *x* present in the *FitPage*
 will be ignored.
 
Index: src/sas/sasgui/perspectives/invariant/media/invariant_help.rst
===================================================================
--- src/sas/sasgui/perspectives/invariant/media/invariant_help.rst	(revision 70305bd2940be88f936344e92c9cc7507559f3e2)
+++ src/sas/sasgui/perspectives/invariant/media/invariant_help.rst	(revision b64b87c874ab0edc565e9c415decc839e3214c8c)
@@ -4,6 +4,6 @@
 .. by S King, ISIS, during SasView CodeCamp-III in Feb 2015.
 
-Invariant Calculation Perspective
-=================================
+Invariant Calculation
+=====================
 
 Description
@@ -45,6 +45,6 @@
 .. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
 
-Using the perspective
----------------------
+Using invariant analysis
+------------------------
 
 1) Select *Invariant* from the *Analysis* menu on the SasView toolbar.
@@ -53,7 +53,7 @@
 
 3) Select a dataset and use the *Send To* button on the *Data Explorer* to load 
-   the dataset into the *Invariant* perspective.
+   the dataset into the *Invariant* panel.
 
-4) Use the *Customised Input* boxes on the *Invariant* perspective to subtract 
+4) Use the *Customised Input* boxes on the *Invariant* panel to subtract 
    any background, specify the contrast (i.e. difference in SLDs - this must be 
    specified for the eventual value of Q*\  to be on an absolute scale), or to 
@@ -73,5 +73,5 @@
 
 8) If the value of Q*\  calculated with the extrapolated regions is invalid, a 
-   red warning will appear at the top of the *Invariant* perspective panel.
+   red warning will appear at the top of the *Invariant* panel.
 
    The details of the calculation are available by clicking the *Details* 
Index: src/sas/sasgui/perspectives/pr/media/pr_help.rst
===================================================================
--- src/sas/sasgui/perspectives/pr/media/pr_help.rst	(revision 780545820bf596bf3709c434f3eb26bcc29a1236)
+++ src/sas/sasgui/perspectives/pr/media/pr_help.rst	(revision b64b87c874ab0edc565e9c415decc839e3214c8c)
@@ -4,6 +4,6 @@
 .. by S King, ISIS, during SasView CodeCamp-III in Feb 2015.
 
-P(r) Inversion Perspective
-==========================
+P(r) Calculation
+================
 
 Description
@@ -32,6 +32,6 @@
 .. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
 
-Using the perspective
----------------------
+Using P(r) inversion
+--------------------
 
 The user must enter
Index: src/sas/sasgui/plottools/PlotPanel.py
===================================================================
--- src/sas/sasgui/plottools/PlotPanel.py	(revision 1ed6be74dd086a570674b6acb2a8d3c7fa0bf36d)
+++ src/sas/sasgui/plottools/PlotPanel.py	(revision 1a8e2e8eb0a54bb8f079779e6b1fead25e13da68)
@@ -15,5 +15,4 @@
 import os
 import transform
-from plottables import Data1D
 #TODO: make the plottables interactive
 from binder import BindArtist
@@ -151,4 +150,5 @@
         #List of texts currently on the plot
         self.textList = []
+        self.selectedText = None
         #User scale
         if xtransform != None:
@@ -190,4 +190,5 @@
 
         # new data for the fit
+        from sas.sasgui.guiframe.dataFitting import Data1D
         self.fit_result = Data1D(x=[], y=[], dy=None)
         self.fit_result.symbol = 13
@@ -352,4 +353,9 @@
             self.leftdown = True
             ax = event.inaxes
+            for text in self.textList:
+                if text.contains(event)[0]: # If user has clicked on text
+                    self.selectedText = text
+                    return
+
             if ax != None:
                 self.xInit, self.yInit = event.xdata, event.ydata
@@ -373,4 +379,5 @@
             self.mousemotion = False
             self.leftup = True
+            self.selectedText = None
 
         #release the legend
@@ -448,4 +455,17 @@
             self._on_legend_motion(event)
             return
+
+        if self.leftdown and self.selectedText is not None:
+            # User has clicked on text and is dragging
+            ax = event.inaxes
+            if ax != None:
+                # Only move text if mouse is within axes
+                self.selectedText.set_position((event.xdata, event.ydata))
+                self._dragHelper(0, 0)
+            else:
+                # User has dragged outside of axes
+                self.selectedText = None
+            return
+
         if self.enable_toolbar:
             #Disable dragging without the toolbar to allow zooming with toolbar
@@ -646,4 +666,8 @@
                 dlg.setFitRange(self.xminView, self.xmaxView,
                                 self.xmin, self.xmax)
+            else:
+                xlim = self.subplot.get_xlim()
+                ylim = self.subplot.get_ylim()
+                dlg.setFitRange(xlim[0], xlim[1], ylim[0], ylim[1])
             # It would be nice for this to NOT be modal (i.e. Show).
             # Not sure about other ramifications - for example
@@ -1725,4 +1749,7 @@
         if remove_fit:
             self.graph.delete(self.fit_result)
+            if hasattr(self, 'plots'):
+                if 'fit' in self.plots.keys():
+                    del self.plots['fit']
         self.ly = None
         self.q_ctrl = None
@@ -1738,4 +1765,6 @@
         _yscale = 'linear'
         for item in list:
+            if item.id == 'fit':
+                continue
             item.setLabel(self.xLabel, self.yLabel)
             # control axis labels from the panel itself
@@ -1869,4 +1898,7 @@
 
         """
+        xlim = self.subplot.get_xlim()
+        ylim = self.subplot.get_ylim()
+
         # Saving value to redisplay in Fit Dialog when it is opened again
         self.Avalue, self.Bvalue, self.ErrAvalue, \
@@ -1892,4 +1924,13 @@
         self.graph.render(self)
         self._offset_graph()
+        if hasattr(self, 'plots'):
+            # Used by Plotter1D
+            fit_id = 'fit'
+            self.fit_result.id = fit_id
+            self.fit_result.title = 'Fit'
+            self.fit_result.name = 'Fit'
+            self.plots[fit_id] = self.fit_result
+        self.subplot.set_xlim(xlim)
+        self.subplot.set_ylim(ylim)
         self.subplot.figure.canvas.draw_idle()
 
Index: src/sas/sasgui/plottools/RangeDialog.py
===================================================================
--- src/sas/sasgui/plottools/RangeDialog.py	(revision 895c9cb3dcc39d7f29513916f04e9863484692c8)
+++ src/sas/sasgui/plottools/RangeDialog.py	(revision 895c9cb3dcc39d7f29513916f04e9863484692c8)
@@ -0,0 +1,101 @@
+import wx
+from math import log10
+from math import floor
+
+class RangeDialog(wx.Dialog):
+    def __init__(self, parent, id, title="Set Graph Range"):
+        wx.Dialog.__init__(self, parent, id, title, size=(350, 175))
+
+        mainbox = wx.BoxSizer(wx.VERTICAL)
+
+        x_box = wx.BoxSizer(wx.HORIZONTAL)
+
+        xmin_label = wx.StaticText(self, -1, 'xmin: ', size=(50,20))
+        x_box.Add(xmin_label)
+        self.xmin_input = wx.TextCtrl(self, -1, size=(75, -1))
+        x_box.Add(self.xmin_input)
+
+        x_box.AddSpacer(35)
+
+        xmax_label = wx.StaticText(self, -1, "xmax: ", size=(50,20))
+        x_box.Add(xmax_label)
+        self.xmax_input = wx.TextCtrl(self, -1, size=(75, -1))
+        x_box.Add(self.xmax_input)
+
+        mainbox.Add(x_box, flag=wx.CENTER | wx.ALL, border=10)
+
+        y_box = wx.BoxSizer(wx.HORIZONTAL)
+
+        ymin_label = wx.StaticText(self, -1, 'ymin: ', size=(50,20))
+        y_box.Add(ymin_label)
+        self.ymin_input = wx.TextCtrl(self, -1, size=(75, -1))
+        y_box.Add(self.ymin_input)
+
+        y_box.AddSpacer(35)
+
+        ymax_label = wx.StaticText(self, -1, "ymax: ", size=(50,20))
+        y_box.Add(ymax_label)
+        self.ymax_input = wx.TextCtrl(self, -1, size=(75, -1))
+        y_box.Add(self.ymax_input)
+
+        mainbox.Add(y_box, flag=wx.CENTER | wx.ALL, border=10)
+
+        btn_sizer = wx.BoxSizer(wx.HORIZONTAL)
+        btn_sizer.AddSpacer(20)
+
+        ok_btn = wx.Button(self, wx.ID_OK, 'Done')
+        btn_sizer.Add(ok_btn)
+
+        btn_sizer.AddSpacer(5)
+
+        cancel_btn = wx.Button(self, wx.ID_CANCEL, 'Cancel')
+        btn_sizer.Add(cancel_btn)
+
+        mainbox.Add(btn_sizer, flag=wx.CENTER, border=10)
+
+        self.SetSizer(mainbox)
+
+    def GetXRange(self):
+        xmin, xmax = (None, None)
+        try:
+            xmin = float(self.xmin_input.GetValue())
+        except:
+            pass
+        try:
+            xmax = float(self.xmax_input.GetValue())
+        except:
+            pass
+
+        if xmin is not None and xmax is not None:
+            return (xmin, xmax)
+        return None
+
+    def SetXRange(self, x_range):
+        xmin, xmax = x_range
+        self.xmin_input.SetValue(str(self._round_to_sigfig(xmin, 8)))
+        self.xmax_input.SetValue(str(self._round_to_sigfig(xmax, 8)))
+
+    def GetYRange(self):
+        ymin, ymax = (None, None)
+        try:
+            ymin = float(self.ymin_input.GetValue())
+        except:
+            pass
+        try:
+            ymax = float(self.ymax_input.GetValue())
+        except:
+            pass
+
+        if ymin is not None and ymax is not None:
+            return (ymin, ymax)
+        return None
+
+    def SetYRange(self, y_range):
+        ymin, ymax = y_range
+        self.ymin_input.SetValue(str(self._round_to_sigfig(ymin, 8)))
+        self.ymax_input.SetValue(str(self._round_to_sigfig(ymax, 8)))
+
+    def _round_to_sigfig(self, x, sigfigs):
+        if x == 0.0:
+            return 0.0
+        return round(x, sigfigs-1-int(floor(log10(abs(x)))))
Index: test/sasdataloader/test/utest_abs_reader.py
===================================================================
--- test/sasdataloader/test/utest_abs_reader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ test/sasdataloader/test/utest_abs_reader.py	(revision 5f26aa48a5e34765d73c20fa10024d5319567854)
@@ -176,6 +176,5 @@
         
         self.assertEqual(self.data.run[0], "1234")
-        self.assertEqual(self.data.meta_data['loader'], "CanSAS 1D")
-        self.assertEqual(len(self.data.errors), 0)
+        self.assertEqual(self.data.meta_data['loader'], "CanSAS XML 1D")
         
         # Data
@@ -198,7 +197,7 @@
         self.assertEqual(self.data.sample.name, "my sample")
         self.assertEqual(self.data.sample.thickness_unit, 'mm')
-        self.assertEqual(self.data.sample.thickness, 1.03)
-        
-        self.assertEqual(self.data.sample.transmission, 0.327)
+        self.assertAlmostEqual(self.data.sample.thickness, 1.03)
+        
+        self.assertAlmostEqual(self.data.sample.transmission, 0.327)
         
         self.assertEqual(self.data.sample.temperature_unit, 'C')
@@ -233,7 +232,7 @@
         
         self.assertEqual(self.data.source.wavelength_max_unit, "nm")
-        self.assertEqual(self.data.source.wavelength_max, 1.0)
+        self.assertAlmostEqual(self.data.source.wavelength_max, 1.0)
         self.assertEqual(self.data.source.wavelength_min_unit, "nm")
-        self.assertEqual(self.data.source.wavelength_min, 0.22)
+        self.assertAlmostEqual(self.data.source.wavelength_min, 0.22)
         self.assertEqual(self.data.source.wavelength_spread_unit, "percent")
         self.assertEqual(self.data.source.wavelength_spread, 14.3)
@@ -248,5 +247,5 @@
             self.assertEqual(item.size_unit,'mm')
             self.assertEqual(item.distance_unit,'mm')
-            
+
             if item.size.x==50 \
                 and item.distance==11000.0 \
@@ -294,4 +293,5 @@
             self.assertTrue(item.date in ['04-Sep-2007 18:35:02',
                                           '03-SEP-2006 11:42:47'])
+            print item.term
             for t in item.term:
                 if t['name']=="ABS:DSTAND" \
@@ -345,9 +345,9 @@
         self.assertEqual(self.data.filename, filename)
         # The followed should not have been loaded
-        self.assertEqual(self.data.sample.thickness, 0.00103)
+        self.assertAlmostEqual(self.data.sample.thickness, 0.00103)
         # This one should
-        self.assertEqual(self.data.sample.transmission, 0.327)
-        
-        self.assertEqual(self.data.meta_data['loader'], "CanSAS 1D")
+        self.assertAlmostEqual(self.data.sample.transmission, 0.327)
+        
+        self.assertEqual(self.data.meta_data['loader'], "CanSAS XML 1D")
         print self.data.errors
         self.assertEqual(len(self.data.errors), 1)
@@ -385,3 +385,3 @@
 
 if __name__ == '__main__':
-    unittest.main(testRunner=unittest.TextTestRunner(verbosity=2))
+    unittest.main()
Index: test/sasdataloader/test/utest_cansas.py
===================================================================
--- test/sasdataloader/test/utest_cansas.py	(revision 83b6408fbfaefa4819b955c697fef834d8f033f6)
+++ test/sasdataloader/test/utest_cansas.py	(revision 1686a333c537addee2a00ed89b00c694826a8f17)
@@ -99,5 +99,5 @@
         self.assertTrue(data.detector[1].name == "main-detector-bank")
         self.assertTrue(data.detector[0].distance == 575.0)
-        self.assertTrue(data.detector[1].distance == 4145.02)
+        self.assertAlmostEqual(data.detector[1].distance, 4145.02)
         self.assertTrue(data.process[0].name == "Mantid generated CanSAS1D XML")
         self.assertTrue(data.meta_data["xmlpreprocess"] != None)
@@ -176,5 +176,5 @@
         data = list[0]
         self.assertTrue(data.x.size == 2)
-        self.assertTrue(len(data.meta_data) == 3)
+        self.assertTrue(len(data.meta_data) == 2)
         self.assertTrue(len(data.errors) == 1)
         self.assertTrue(data.detector[0].distance_unit == "mm")
