make an interactive grid in which users can edit specimen names
        as well as which sample a specimen belongs to
17938
def InitLocCheck(self):
        """"""
        self.panel = wx.Panel(self, style=wx.SIMPLE_BORDER)

        text = """Step 5:
Check that locations are correctly named.
Fill in any blank cells using controlled vocabularies.
(See Help button for details)

** Denotes controlled vocabulary"""
        label = wx.StaticText(self.panel, label=text)
        #self.Data_hierarchy = self.ErMagic.Data_hierarchy
        self.locations = self.er_magic_data.locations
        #
        if not self.er_magic_data.locations:
            msg = "You have no data in er_locations, so we are skipping step 5.\n Note that location names must be entered at the measurements level,so you may need to re-import your data, or you can add a location in step 3"
            dlg = wx.MessageDialog(None, caption="Message:", message=msg, style=wx.OK|wx.ICON_INFORMATION)
            dlg.ShowModal()
            dlg.Destroy()
            self.panel.Destroy()
            self.InitAgeCheck()
            return

        self.grid_builder = grid_frame2.GridBuilder(self.er_magic_data, 'location',
                                                   self.er_magic_data.headers, self.panel)
        self.loc_grid = self.grid_builder.make_grid(incl_pmag=False)
        self.loc_grid.InitUI()
        self.grid_builder.add_data_to_grid(self.loc_grid, 'location', incl_pmag=False)
        self.grid = self.loc_grid
        # initialize all needed drop-down menus
        self.drop_down_menu = drop_down_menus.Menus("location", self,
                                                     self.loc_grid, None)

        # need to find max/min lat/lon here IF they were added in the previous grid
        sites = self.er_magic_data.sites
        location_lat_lon = self.er_magic_data.get_min_max_lat_lon(self.er_magic_data.locations)

        col_names = ('location_begin_lat', 'location_end_lat', 'location_begin_lon', 'location_end_lon')
        col_inds = [self.grid.col_labels.index(name) for name in col_names]
        col_info = list(zip(col_names, col_inds))
        for loc in self.er_magic_data.locations:
            row_ind = self.grid.row_labels.index(loc.name)
            for col_name, col_ind in col_info:
                info = location_lat_lon[loc.name][col_name]
                self.grid.SetCellValue(row_ind, col_ind, str(info))

        ### Create Buttons ###
        hbox_one = wx.BoxSizer(wx.HORIZONTAL)
        self.helpButton = wx.Button(self.panel, label="Help")
        self.Bind(wx.EVT_BUTTON, lambda event: self.on_helpButton(event, "ErMagicLocationHelp.html"), self.helpButton)
        hbox_one.Add(self.helpButton)

        hboxok = wx.BoxSizer(wx.HORIZONTAL)
        self.saveButton = wx.Button(self.panel, id=-1, label='Save')
        self.Bind(wx.EVT_BUTTON, lambda event: self.on_saveButton(event, self.loc_grid), self.saveButton)
        self.cancelButton = wx.Button(self.panel, wx.ID_CANCEL, '&Cancel')
        self.Bind(wx.EVT_BUTTON, self.on_cancelButton, self.cancelButton)
        self.continueButton = wx.Button(self.panel, id=-1, label='Save and continue')
        self.Bind(wx.EVT_BUTTON, lambda event: self.on_continueButton(event, self.loc_grid, next_dia=self.InitAgeCheck), self.continueButton)
        self.backButton = wx.Button(self.panel, wx.ID_ANY, "&Back")
        previous_dia = self.InitSampCheck
        self.Bind(wx.EVT_BUTTON, lambda event: self.on_backButton(event, previous_dia, current_dia=self.InitLocCheck), self.backButton)

        hboxok.Add(self.saveButton, flag=wx.RIGHT, border=10)
        hboxok.Add(self.cancelButton, flag=wx.RIGHT, border=10)
        hboxok.Add(self.continueButton, flag=wx.RIGHT, border=10)
        hboxok.Add(self.backButton)

        #
        hboxgrid = pw.hbox_grid(self.panel, self.onDeleteRow, 'location', self.grid)
        self.deleteRowButton = hboxgrid.deleteRowButton

        self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_CLICK, self.onLeftClickLabel, self.grid)


        ### Make Containers ###
        vbox = wx.BoxSizer(wx.VERTICAL)
        vbox.Add(label, flag=wx.ALIGN_CENTER|wx.TOP|wx.BOTTOM, border=20)
        vbox.Add(hbox_one, flag=wx.BOTTOM|wx.ALIGN_LEFT, border=10)
        vbox.Add(hboxok, flag=wx.BOTTOM|wx.ALIGN_LEFT, border=10)
        vbox.Add(hboxgrid, flag=wx.BOTTOM|wx.ALIGN_LEFT, border=10)
        vbox.Add(self.loc_grid, flag=wx.TOP|wx.BOTTOM, border=10)
        vbox.AddSpacer(20)

        self.hbox_all = wx.BoxSizer(wx.HORIZONTAL)
        self.hbox_all.AddSpacer(20)
        self.hbox_all.Add(vbox)
        self.hbox_all.AddSpacer(20)

        self.panel.SetSizer(self.hbox_all)
        #if sys.platform in ['win32', 'win64']:
        #    self.panel.SetScrollbars(20, 20, 50, 50)
        self.hbox_all.Fit(self)
        self.Centre()
        self.Show()
        self.Hide()
        self.Show()
1091733
def OnClick_listctrl(self, event):
        
        i = event.GetIndex()
        if self.parent.current_fit == self.fit_list[i][0]: return
        self.parent.initialize_CART_rot(self.fit_list[i][1])
        si = self.parent.specimens.index(self.fit_list[i][1])
        self.parent.specimens_box.SetSelection(si)
        self.parent.select_specimen(self.fit_list[i][1])
        self.change_selected(i)
        fi = 0
        while (self.parent.s == self.fit_list[i][1] and i >= 0): i,fi = (i-1,fi+1)
        self.parent.update_fit_box()
        self.parent.fit_box.SetSelection(fi-1)
        self.parent.update_selection()
662411
def replace_cells(self, key, sorted_row_idxs):
        """"""

        row, col, tab = key

        new_keys = {}
        del_keys = []

        selection = self.grid.actions.get_selection()

        for __row, __col, __tab in self.grid.code_array:
            if __tab == tab and \
               (not selection or (__row, __col) in selection):
                new_row = sorted_row_idxs.index(__row)
                if __row != new_row:
                    new_keys[(new_row, __col, __tab)] = \
                        self.grid.code_array((__row, __col, __tab))
                    del_keys.append((__row, __col, __tab))

        for key in del_keys:
            self.grid.code_array.pop(key)

        for key in new_keys:
            CellActions.set_code(self, key, new_keys[key])
1054357
def samplenamer(listofdata, indexposition=0):
    
    samplename = listofdata[indexposition].rstrip().replace(" ", "-").replace(".", "-").replace("=", "-")\
        .replace("+", "").replace("/", "-").replace("#", "").replace("---", "-").replace("--", "-")
    return samplename
26452
def _maybe_init_experiment(self, experiment_name):
    
    user_id = self._maybe_init_user()
    cursor = self._db.cursor()
    cursor.execute(
        """
        SELECT experiment_id FROM Experiments
        WHERE user_id = ? AND experiment_name = ?
        """,
        (user_id, experiment_name))
    row = cursor.fetchone()
    if row:
      return row[0]
    experiment_id = self._create_id()
    # TODO: track computed time from run start times
    computed_time = 0
    cursor.execute(
        """
        INSERT INTO Experiments (
          user_id, experiment_id, experiment_name,
          inserted_time, started_time, is_watching
        ) VALUES (?, ?, ?, ?, ?, ?)
        """,
        (user_id, experiment_id, experiment_name, time.time(), computed_time,
         False))
    return experiment_id
522613
def on_change_specimen_mouse_cursor(self, event):
        
        if not self.specimen_EA_xdata or not self.specimen_EA_ydata:
            return
        pos = event.GetPosition()
        width, height = self.canvas2.get_width_height()
        pos[1] = height - pos[1]
        xpick_data, ypick_data = pos
        xdata_org = self.specimen_EA_xdata
        ydata_org = self.specimen_EA_ydata
        data_corrected = self.specimen_eqarea.transData.transform(
            vstack([xdata_org, ydata_org]).T)
        xdata, ydata = data_corrected.T
        xdata = list(map(float, xdata))
        ydata = list(map(float, ydata))
        e = 4e0

        if self.specimen_EA_setting == "Zoom":
            self.canvas2.SetCursor(wx.Cursor(wx.CURSOR_CROSS))
        else:
            self.canvas2.SetCursor(wx.Cursor(wx.CURSOR_ARROW))
        for i, (x, y) in enumerate(zip(xdata, ydata)):
            if 0 < sqrt((x-xpick_data)**2. + (y-ypick_data)**2.) < e:
                self.canvas2.SetCursor(wx.Cursor(wx.CURSOR_HAND))
                break
        event.Skip()
399572
def _add_grid_attributes(self, ds):
        """"""
        for name_int, names_ext in self._grid_attrs.items():
            ds_coord_name = set(names_ext).intersection(set(ds.coords) |
                                                        set(ds.data_vars))
            model_attr = getattr(self.model, name_int, None)
            if ds_coord_name and (model_attr is not None):
                # Force coords to have desired name.
                ds = ds.rename({list(ds_coord_name)[0]: name_int})
                ds = ds.set_coords(name_int)
                if not np.array_equal(ds[name_int], model_attr):
                    if np.allclose(ds[name_int], model_attr):
                        msg = ("Values for '{0}' are nearly (but not exactly) "
                               "the same in the Run {1} and the Model {2}.  "
                               "Therefore replacing Run's values with the "
                               "model's.".format(name_int, self.run,
                                                 self.model))
                        logging.info(msg)
                        ds[name_int].values = model_attr.values
                    else:
                        msg = ("Model coordinates for '{0}' do not match those"
                               " in Run: {1} vs. {2}"
                               "".format(name_int, ds[name_int], model_attr))
                        logging.info(msg)

            else:
                # Bring in coord from model object if it exists.
                ds = ds.load()
                if model_attr is not None:
                    ds[name_int] = model_attr
                    ds = ds.set_coords(name_int)
            if (self.dtype_in_vert == 'pressure' and
                    internal_names.PLEVEL_STR in ds.coords):
                self.pressure = ds.level
        return ds
1073600
def change_result(self, old_result_name, new_result_name, new_er_data=None,
                      new_pmag_data=None, spec_names=None, samp_names=None,
                      site_names=None, loc_names=None, replace_data=False):
        
        result = self.find_by_name(old_result_name, self.results)
        if not result:
            msg = '-W- {} is not a currently existing result, so it cannot be updated.'.format(old_result_name)
            print(msg)
            return False
        else:
            specimens, samples, sites, locations = None, None, None, None
            if spec_names:
                specimens = [self.find_or_create_by_name(spec, self.specimens, 'specimen') for spec in spec_names]
            if samp_names:
                samples = [self.find_or_create_by_name(samp, self.samples, 'sample') for samp in samp_names]
            if site_names:
                sites = [self.find_or_create_by_name(site, self.sites, 'site') for site in site_names]
            if loc_names:
                locations = [self.find_or_create_by_name(loc, self.locations, 'location') for loc in loc_names]

            result.change_result(new_result_name, new_pmag_data, specimens, samples,
                                 sites, locations, replace_data)
            return result
304717
def tblNomenclator
      @headers = %w{NomenclatorID GenusNameID SubgenusNameID SpeciesNameID SubspeciesNameID LastUpdate ModifiedBy SuitableForGenus SuitableForSpecies InfrasubspeciesNameID InfrasubKind}
      sql = []   
      i = 1

      # Ugh, move build from here 
      @name_collection.nomenclators.keys.each do |i|
        name =  @name_collection.nomenclators[i]
        genus_id = @genus_names[name[0]]
        genus_id ||= 0
        subgenus_id = @genus_names[name[1]]
        subgenus_id ||= 0
        species_id = @species_names[name[2]]
        species_id ||= 0
        subspecies_id = @species_names[name[3]]
        subspecies_id ||= 0
        variety_id = @species_names[name[4]]
        variety_id ||= 0

        cols = {
          NomenclatorID: i,
          GenusNameID: genus_id, 
          SubgenusNameID: subgenus_id, 
          SpeciesNameID: species_id, 
          SubspeciesNameID: subspecies_id,
          InfrasubspeciesNameID: variety_id,
          InfrasubKind: (variety_id == 0 ? 0 : 2), 
          LastUpdate: @time,  
          ModifiedBy: @authorized_user_id, 
          SuitableForGenus: 0,                      # Set in SF w test
          SuitableForSpecies: 0                     # Set in SF w test
        }
        i += 1
        sql << sql_insert_statement('tblNomenclator', cols) 
      end

      sql.join("\n")
    end
378288
def _update_names(self):
        """"""

        d = dict(
            table=self.table_name,
            time=self.time,
            space=self.space,
            grain=self.grain,
            variant=self.variant,
            segment=self.segment
        )

        assert self.dataset

        name = PartialPartitionName(**d).promote(self.dataset.identity.name)

        self.name = str(name.name)
        self.vname = str(name.vname)
        self.cache_key = name.cache_key
        self.fqname = str(self.identity.fqname)
216888
function samples () {
  form.samples.url.link.on("click", function (event) {
    event.preventDefault();
    form.url.val(samples.url);
  });

  form.samples.text.link.on("click", function (event) {
    event.preventDefault();
    form.textBox.setValue(samples.text, -1);
    form.samples.text.container.hide();
    form.textBox.focus();
  });

  form.textBox.on("input", function () {
    if (form.textBox.session.getValue().length === 0) {
      form.samples.text.container.show();
    }
    else {
      form.samples.text.container.hide();
    }
  });
}
1023486
def _prepare_sample(data, run_folder):
    
    want = set(["description", "files", "genome_build", "name", "analysis", "upload", "algorithm"])
    out = {}
    for k, v in data.items():
        if k in want:
            out[k] = _relative_paths(v, run_folder)
    if "algorithm" not in out:
        analysis, algorithm = _select_default_algorithm(out.get("analysis"))
        out["algorithm"] = algorithm
        out["analysis"] = analysis
    description = "%s-%s" % (out["name"], clean_name(out["description"]))
    out["name"] = [out["name"], description]
    out["description"] = description
    return out
62959
def on_close_criteria_box(self, dia):
        
        window_list_specimens = [
            'specimen_n', 'specimen_mad', 'specimen_dang', 'specimen_alpha95']
        window_list_samples = ['sample_n', 'sample_n_lines',
                               'sample_n_planes', 'sample_k', 'sample_r', 'sample_alpha95']
        window_list_sites = ['site_n', 'site_n_lines',
                             'site_n_planes', 'site_k', 'site_r', 'site_alpha95']
        demag_gui_supported_criteria = window_list_specimens + \
            window_list_samples+window_list_sites

        if self.data_model == 3:
            new_crits = []
            for crit in demag_gui_supported_criteria:
                new_crit = {}
                command = "dia.set_%s.GetValue()" % (crit)
                new_value = pmag.execute(command, dia=dia)
                if new_value == None or new_value == '':
                    continue
                d = findall(r"[-+]?\d*\.\d+|\d+", new_value)
                if len(d) > 0:
                    d = d[0]
                comp = new_value.strip(str(d))
                if comp == '':
                    comp = '>='
                if 'specimen' in crit:
                    col = "specimens."+map_magic.spec_magic2_2_magic3_map[crit]
                elif 'sample' in crit:
                    col = "samples."+map_magic.samp_magic2_2_magic3_map[crit]
                elif 'site' in crit:
                    col = "sites."+map_magic.site_magic2_2_magic3_map[crit]
                else:
                    print("no way this like is impossible")
                    continue
                new_crit['criterion'] = "ACCEPT"
                new_crit['criterion_value'] = d
                new_crit['criterion_operation'] = comp
                new_crit['table_column'] = col
                new_crit['citations'] = "This study"
                new_crit['description'] = ''
                new_crits.append(new_crit)
            cdf = DataFrame(new_crits)
            cdf = cdf.set_index("table_column")
            cdf["table_column"] = cdf.index
            cdf = cdf.reindex_axis(sorted(cdf.columns), axis=1)
            if 'criteria' not in self.con.tables:
                cols = ['criterion', 'criterion_value', 'criterion_operation',
                        'table_column', 'citations', 'description']
                self.con.add_empty_magic_table('criteria', col_names=cols)
            self.con.tables['criteria'].df = cdf
            self.con.tables['criteria'].write_magic_file(dir_path=self.WD)
        else:
            for crit in demag_gui_supported_criteria:
                command = "new_value=dia.set_%s.GetValue()" % (crit)
                exec(command)
                # empty box
                if new_value == "":
                    self.acceptance_criteria[crit]['value'] = -999
                    continue
                # box with no valid number
                try:
                    float(new_value)
                except:
                    self.show_crit_window_err_messege(crit)
                    continue
                self.acceptance_criteria[crit]['value'] = float(new_value)

            #  message dialog
            self.saved_dlg(message="changes saved to criteria")
            self.write_acceptance_criteria_to_file()
            dia.Destroy()
120835
def on_next_button(self, event):
        
        self.do_auto_save()
        self.selected_meas = []
        index = self.specimens.index(self.s)
        try:
            fit_index = self.pmag_results_data['specimens'][self.s].index(
                self.current_fit)
        except KeyError:
            fit_index = None
        except ValueError:
            fit_index = None
        if index == len(self.specimens)-1:
            index = 0
        else:
            index += 1
        # sets self.s calculates params etc.
        self.initialize_CART_rot(str(self.specimens[index]))
        self.specimens_box.SetStringSelection(str(self.s))
        if fit_index != None and self.s in self.pmag_results_data['specimens']:
            try:
                self.current_fit = self.pmag_results_data['specimens'][self.s][fit_index]
            except IndexError:
                self.current_fit = None
        else:
            self.current_fit = None
        if self.ie_open:
            self.ie.change_selected(self.current_fit)
        self.update_selection()
1050616
def add_sample(self, name, labels, value, timestamp=None, exemplar=None):
        """"""
        self.samples.append(Sample(name, labels, value, timestamp, exemplar))
213855
def _ids_and_column_names(names, force_lower_case=False):
        """"""
        fixed = OrderedDict()
        for name in names:
            identifier = RowWrapper._make_identifier(name)
            if force_lower_case:
                identifier = identifier.lower()
            while identifier in fixed:
                identifier = RowWrapper._increment_numeric_suffix(identifier)
            fixed[identifier] = name
        return fixed
1374459
function ($scope, options, sortService, domUtilityService, $filter, $templateCache, $utils, $timeout, $parse, $http, $q) {
		var defaults = {
				//Define an aggregate template to customize the rows when grouped. See github wiki for more details.
				aggregateTemplate: undefined,

				//Callback for when you want to validate something after selection.
				afterSelectionChange: function() {
				},

				/* Callback if you want to inspect something before selection,
				 return false if you want to cancel the selection. return true otherwise.
				 If you need to wait for an async call to proceed with selection you can
				 use rowItem.changeSelection(event) method after returning false initially.
				 Note: when shift+ Selecting multiple items in the grid this will only get called
				 once and the rowItem will be an array of items that are queued to be selected. */
				beforeSelectionChange: function() {
					return true;
				},

				//checkbox templates.
				checkboxCellTemplate: undefined,
				checkboxHeaderTemplate: undefined,

				//definitions of columns as an array [], if not defines columns are auto-generated. See github wiki for more details.
				columnDefs: undefined,

				//*Data being displayed in the grid. Each item in the array is mapped to a row being displayed.
				data: [],

				//Data updated callback, fires every time the data is modified from outside the grid.
				dataUpdated: function() {
				},

				//Enables cell editing.
				enableCellEdit: false,

				//Enables cell editing on focus
				enableCellEditOnFocus: false,

				//Enables cell selection.
				enableCellSelection: false,

				//Enable or disable resizing of columns
				enableColumnResize: false,

				//Enable or disable reordering of columns
				enableColumnReordering: false,

				//Enable or disable HEAVY column virtualization. This turns off selection checkboxes and column pinning and is designed for spreadsheet-like data.
				enableColumnHeavyVirt: false,

				//Enables the server-side paging feature
				enablePaging: false,

				//Enable column pinning
				enablePinning: false,

				//To be able to have selectable rows in grid.
				enableRowSelection: true,

				//Enables or disables sorting in grid.
				enableSorting: true,

				//Enables or disables text highlighting in grid by adding the "unselectable" class (See CSS file)
				enableHighlighting: false,

				// string list of properties to exclude when auto-generating columns.
				excludeProperties: [],

				/* filterOptions -
				 filterText: The text bound to the built-in search box.
				 useExternalFilter: Bypass internal filtering if you want to roll your own filtering mechanism but want to use builtin search box.
				 */
				filterOptions: {
					filterText: "",
					useExternalFilter: false
				},

				//Defining the height of the footer in pixels.
				footerRowHeight: 55,

				// the template for the column menu and filter, including the button.
				footerTemplate: undefined,

				// Enables a trade off between refreshing the contents of the grid continuously while scrolling (behaviour when true)
				// and keeping the scroll bar button responsive at the expense of refreshing grid contents (behaviour when false)
				forceSyncScrolling: true,

				//Initial fields to group data by. Array of field names, not displayName.
				groups: [],

				// set the initial state of aggreagate grouping. "true" means they will be collapsed when grouping changes, "false" means they will be expanded by default.
				groupsCollapsedByDefault: true,

				//The height of the header row in pixels.
				headerRowHeight: 30,

				//Define a header row template for further customization. See github wiki for more details.
				headerRowTemplate: undefined,

				/*Enables the use of jquery UI reaggable/droppable plugin. requires jqueryUI to work if enabled.
				 Useful if you want drag + drop but your users insist on crappy browsers. */
				jqueryUIDraggable: false,

				//Enable the use jqueryUIThemes
				jqueryUITheme: false,

				//Prevent unselections when in single selection mode.
				keepLastSelected: true,

				/*Maintains the column widths while resizing.
				 Defaults to true when using *'s or undefined widths. Can be ovverriden by setting to false.*/
				maintainColumnRatios: undefined,

				// the template for the column menu and filter, including the button.
				menuTemplate: undefined,

				//Set this to false if you only want one item selected at a time
				multiSelect: true,

				// pagingOptions -
				pagingOptions: {
					// pageSizes: list of available page sizes.
					pageSizes: [250, 500, 1000],
					//pageSize: currently selected page size.
					pageSize: 250,
					//currentPage: the uhm... current page.
					currentPage: 1
				},

				//the selection checkbox is pinned to the left side of the viewport or not.
				pinSelectionCheckbox: false,

				//Array of plugin functions to register in ng-grid
				plugins: [],

				//User defined unique ID field that allows for better handling of selections and for server-side paging
				primaryKey: undefined,

				//Row height of rows in grid.
				rowHeight: 30,

				//Define a row template to customize output. See github wiki for more details.
				rowTemplate: undefined,

				//all of the items selected in the grid. In single select mode there will only be one item in the array.
				selectedItems: [],

				//Disable row selections by clicking on the row and only when the checkbox is clicked.
				selectWithCheckboxOnly: false,

				/*Enables menu to choose which columns to display and group by.
				 If both showColumnMenu and showFilter are false the menu button will not display.*/
				showColumnMenu: false,

				/*Enables display of the filterbox in the column menu.
				 If both showColumnMenu and showFilter are false the menu button will not display.*/
				showFilter: false,

				//Show or hide the footer alltogether the footer is enabled by default
				showFooter: false,

				//Show the dropzone for drag and drop grouping
				showGroupPanel: false,

				//Row selection check boxes appear as the first column.
				showSelectionCheckbox: false,

				/*Define a sortInfo object to specify a default sorting state.
				 You can also observe this variable to utilize server-side sorting (see useExternalSorting).
				 Syntax is sortinfo: { fields: ['fieldName1',' fieldName2'], direction: 'ASC'/'asc' || 'desc'/'DESC'}*/
				sortInfo: {fields: [], columns: [], directions: [] },

				//Set the tab index of the Vieport.
				tabIndex: -1,

				//totalServerItems: Total items are on the server.
				totalServerItems: 0,

				/*Prevents the internal sorting from executing.
				 The sortInfo object will be updated with the sorting information so you can handle sorting (see sortInfo)*/
				useExternalSorting: false,

				/*i18n language support. choose from the installed or included languages, en, fr, sp, etc...*/
				i18n: 'en',

				//the threshold in rows to force virtualization on
				virtualizationThreshold: 50,

				// Don't handle tabs, so they can be used to navigate between controls.
				noTabInterference: false
			},
			self = this;
		self.maxCanvasHt = 0;
		//self vars
		self.config = $.extend(defaults, window.ngGrid.config, options);

		// override conflicting settings
		self.config.showSelectionCheckbox = (self.config.showSelectionCheckbox && self.config.enableColumnHeavyVirt === false);
		self.config.enablePinning = (self.config.enablePinning && self.config.enableColumnHeavyVirt === false);
		self.config.selectWithCheckboxOnly = (self.config.selectWithCheckboxOnly && self.config.showSelectionCheckbox !== false);
		self.config.pinSelectionCheckbox = self.config.enablePinning;

		if (typeof options.columnDefs === "string") {
			self.config.columnDefs = $scope.$eval(options.columnDefs);
		}
		self.rowCache = [];
		self.rowMap = [];
		self.gridId = "ng" + $utils.newId();
		self.$root = null; //this is the root element that is passed in with the binding handler
		self.$groupPanel = null;
		self.$topPanel = null;
		self.$headerContainer = null;
		self.$headerScroller = null;
		self.$headers = null;
		self.$viewport = null;
		self.$canvas = null;
		self.rootDim = self.config.gridDim;
		self.data = [];
		self.lateBindColumns = false;
		self.filteredRows = [];

		self.initTemplates = function() {
			var templates = ['rowTemplate', 'aggregateTemplate', 'headerRowTemplate', 'checkboxCellTemplate', 'checkboxHeaderTemplate', 'menuTemplate', 'footerTemplate'];

			var promises = [];
			angular.forEach(templates, function(template) {
				promises.push( self.getTemplate(template) );
			});

			return $q.all(promises);
		};

		//Templates
		// test templates for urls and get the tempaltes via synchronous ajax calls
		self.getTemplate = function (key) {
			var t = self.config[key];
			var uKey = self.gridId + key + ".html";
			var p = $q.defer();
			if (t && !TEMPLATE_REGEXP.test(t)) {
				$http.get(t, {
					cache: $templateCache
				})
					.success(function(data){
						$templateCache.put(uKey, data);
						p.resolve();
					})
					.error(function(err){
						p.reject("Could not load template: " + t);
					});
			} else if (t) {
				$templateCache.put(uKey, t);
				p.resolve();
			} else {
				var dKey = key + ".html";
				$templateCache.put(uKey, $templateCache.get(dKey));
				p.resolve();
			}

			return p.promise;
		};

		if (typeof self.config.data === "object") {
			self.data = self.config.data; // we cannot watch for updates if you don't pass the string name
		}
		self.calcMaxCanvasHeight = function() {
			var calculatedHeight;
			if(self.config.groups.length > 0){
				calculatedHeight = self.rowFactory.parsedData.filter(function(e) {
					return !e[NG_HIDDEN];
				}).length * self.config.rowHeight;
			} else {
				calculatedHeight = self.filteredRows.length * self.config.rowHeight;
			}
			return calculatedHeight;
		};
		self.elementDims = {
			scrollW: 0,
			scrollH: 0,
			rowIndexCellW: 25,
			rowSelectedCellW: 25,
			rootMaxW: 0,
			rootMaxH: 0
		};
		//self funcs
		self.setRenderedRows = function (newRows) {
			$scope.renderedRows.length = newRows.length;
			for (var i = 0; i < newRows.length; i++) {
				if (!$scope.renderedRows[i] || (newRows[i].isAggRow || $scope.renderedRows[i].isAggRow)) {
					$scope.renderedRows[i] = newRows[i].copy();
					$scope.renderedRows[i].collapsed = newRows[i].collapsed;
					if (!newRows[i].isAggRow) {
						$scope.renderedRows[i].setVars(newRows[i]);
					}
				} else {
					$scope.renderedRows[i].setVars(newRows[i]);
				}
				$scope.renderedRows[i].rowIndex = newRows[i].rowIndex;
				$scope.renderedRows[i].offsetTop = newRows[i].offsetTop;
				$scope.renderedRows[i].selected = newRows[i].selected;
				newRows[i].renderedRowIndex = i;
			}
			self.refreshDomSizes();
			$scope.$emit('ngGridEventRows', newRows);
		};
		self.minRowsToRender = function() {
			var viewportH = $scope.viewportDimHeight() || 1;
			return Math.floor(viewportH / self.config.rowHeight);
		};
		self.refreshDomSizes = function() {
			var dim = new ngDimension();
			dim.outerWidth = self.elementDims.rootMaxW;
			dim.outerHeight = self.elementDims.rootMaxH;
			self.rootDim = dim;
			self.maxCanvasHt = self.calcMaxCanvasHeight();
		};
		self.buildColumnDefsFromData = function () {
			self.config.columnDefs = [];
			var item = self.data[0];
			if (!item) {
				self.lateBoundColumns = true;
				return;
			}
			$utils.forIn(item, function (prop, propName) {
				if (self.config.excludeProperties.indexOf(propName) === -1) {
					self.config.columnDefs.push({
						field: propName
					});
				}
			});
		};
		self.buildColumns = function() {
			var columnDefs = self.config.columnDefs,
				cols = [];
			if (!columnDefs) {
				self.buildColumnDefsFromData();
				columnDefs = self.config.columnDefs;
			}
			if (self.config.showSelectionCheckbox) {
				cols.push(new ngColumn({
					colDef: {
						field: '\u2714',
						width: self.elementDims.rowSelectedCellW,
						sortable: false,
						resizable: false,
						groupable: false,
						headerCellTemplate: $templateCache.get($scope.gridId + 'checkboxHeaderTemplate.html'),
						cellTemplate: $templateCache.get($scope.gridId + 'checkboxCellTemplate.html'),
						pinned: self.config.pinSelectionCheckbox
					},
					index: 0,
					headerRowHeight: self.config.headerRowHeight,
					sortCallback: self.sortData,
					resizeOnDataCallback: self.resizeOnData,
					enableResize: self.config.enableColumnResize,
					enableSort: self.config.enableSorting,
					enablePinning: self.config.enablePinning
				}, $scope, self, domUtilityService, $templateCache, $utils));
			}
			if (columnDefs.length > 0) {
				var checkboxOffset = self.config.showSelectionCheckbox ? 1 : 0;
				var groupOffset = $scope.configGroups.length;
				$scope.configGroups.length = 0;
				angular.forEach(columnDefs, function(colDef, i) {
					i += checkboxOffset;
					var column = new ngColumn({
						colDef: colDef,
						index: i + groupOffset,
						originalIndex: i,
						headerRowHeight: self.config.headerRowHeight,
						sortCallback: self.sortData,
						resizeOnDataCallback: self.resizeOnData,
						enableResize: self.config.enableColumnResize,
						enableSort: self.config.enableSorting,
						enablePinning: self.config.enablePinning,
						enableCellEdit: self.config.enableCellEdit || self.config.enableCellEditOnFocus,
						cellEditableCondition: self.config.cellEditableCondition
					}, $scope, self, domUtilityService, $templateCache, $utils);
					var indx = self.config.groups.indexOf(colDef.field);
					if (indx !== -1) {
						column.isGroupedBy = true;
						$scope.configGroups.splice(indx, 0, column);
						column.groupIndex = $scope.configGroups.length;
					}
					cols.push(column);
				});
				$scope.columns = cols;
				if (self.config.groups.length > 0) {
					self.rowFactory.getGrouping(self.config.groups);
				}
			}
		};
		self.configureColumnWidths = function() {
			var asterisksArray = [],
				percentArray = [],
				asteriskNum = 0,
				totalWidth = 0;

			// When rearranging columns, their index in $scope.columns will no longer match the original column order from columnDefs causing
			// their width config to be out of sync. We can use "originalIndex" on the ngColumns to get hold of the correct setup from columnDefs, but to
			// avoid O(n) lookups in $scope.columns per column we setup a map.
			var indexMap = {};
			// Build a map of columnDefs column indices -> ngColumn indices (via the "originalIndex" property on ngColumns).
			angular.forEach($scope.columns, function(ngCol, i) {
				// Disregard columns created by grouping (the grouping columns don't match a column from columnDefs)
				if (!$utils.isNullOrUndefined(ngCol.originalIndex)) {
					var origIndex = ngCol.originalIndex;
					if (self.config.showSelectionCheckbox) {
						//if visible, takes up 25 pixels
						if(ngCol.originalIndex === 0 && ngCol.visible){
							totalWidth += 25;
						}
						// The originalIndex will be offset 1 when including the selection column
						origIndex--;
					}
					indexMap[origIndex] = i;
				}
			});

			angular.forEach(self.config.columnDefs, function(colDef, i) {
				// Get the ngColumn that matches the current column from columnDefs
				var ngColumn = $scope.columns[indexMap[i]];

				colDef.index = i;

				var isPercent = false, t;
				//if width is not defined, set it to a single star
				if ($utils.isNullOrUndefined(colDef.width)) {
					colDef.width = "*";
				} else { // get column width
					isPercent = isNaN(colDef.width) ? $utils.endsWith(colDef.width, "%") : false;
					t = isPercent ? colDef.width : parseInt(colDef.width, 10);
				}

				// check if it is a number
				if (isNaN(t) && !$scope.hasUserChangedGridColumnWidths) {
					t = colDef.width;
					// figure out if the width is defined or if we need to calculate it
					if (t === 'auto') { // set it for now until we have data and subscribe when it changes so we can set the width.
						ngColumn.width = ngColumn.minWidth;
						totalWidth += ngColumn.width;
						var temp = ngColumn;

						$scope.$on('$destroy', $scope.$on("ngGridEventData", function () {
							self.resizeOnData(temp);
						}));

						return;
					} else if (t.indexOf("*") !== -1) { //  we need to save it until the end to do the calulations on the remaining width.
						if (ngColumn.visible !== false) {
							asteriskNum += t.length;
						}
						asterisksArray.push(colDef);
						return;
					} else if (isPercent) { // If the width is a percentage, save it until the very last.
						percentArray.push(colDef);
						return;
					} else { // we can't parse the width so lets throw an error.
						throw "unable to parse column width, use percentage (\"10%\",\"20%\", etc...) or \"*\" to use remaining width of grid";
					}
				} else if (ngColumn.visible !== false) {
					totalWidth += ngColumn.width = parseInt(ngColumn.width, 10);
				}
			});

			// Now we check if we saved any percentage columns for calculating last
			if (percentArray.length > 0) {
				//If they specificy for maintain column ratios to be false in grid config, then it will remain false. If not specifiied or true, will be true.
				self.config.maintainColumnRatios = self.config.maintainColumnRatios !== false;
				// If any columns with % widths have been hidden, then let other % based columns use their width
				var percentWidth = 0; // The total % value for all columns setting their width using % (will e.g. be 40 for 2 columns with 20% each)
				var hiddenPercent = 0; // The total % value for all columns setting their width using %, but which have been hidden
				angular.forEach(percentArray, function(colDef) {
					// Get the ngColumn that matches the current column from columnDefs
					var ngColumn = $scope.columns[indexMap[colDef.index]];
					var percent = parseFloat(colDef.width) / 100;
					percentWidth += percent;

					if (!ngColumn.visible) {
						hiddenPercent += percent;
					}
				});
				var percentWidthUsed = percentWidth - hiddenPercent;

				// do the math
				angular.forEach(percentArray, function(colDef) {
					// Get the ngColumn that matches the current column from columnDefs
					var ngColumn = $scope.columns[indexMap[colDef.index]];

					// Calc the % relative to the amount of % reserved for the visible columns (that use % based widths)
					var percent = parseFloat(colDef.width) / 100;
					if (hiddenPercent > 0) {
						percent = percent / percentWidthUsed;
					}
					else {
						percent = percent / percentWidth;
					}

					var pixelsForPercentBasedWidth = self.rootDim.outerWidth * percentWidth;
					ngColumn.width = pixelsForPercentBasedWidth * percent;
					totalWidth += ngColumn.width;
				});
			}

			// check if we saved any asterisk columns for calculating later
			if (asterisksArray.length > 0) {
				//If they specificy for maintain column ratios to be false in grid config, then it will remain false. If not specifiied or true, will be true.
				self.config.maintainColumnRatios = self.config.maintainColumnRatios !== false;
				// get the remaining width
				var remainingWidth = self.rootDim.outerWidth - totalWidth;
				// are we overflowing vertically?
				if (self.maxCanvasHt > $scope.viewportDimHeight()) {
					//compensate for scrollbar
					remainingWidth -= domUtilityService.ScrollW;
				}
				// calculate the weight of each asterisk rounded down
				var asteriskVal = Math.floor(remainingWidth / asteriskNum);

				// set the width of each column based on the number of stars
				angular.forEach(asterisksArray, function(colDef, i) {
					// Get the ngColumn that matches the current column from columnDefs
					var ngColumn = $scope.columns[indexMap[colDef.index]];
					ngColumn.width = asteriskVal * colDef.width.length;
					if (ngColumn.visible !== false) {
						totalWidth += ngColumn.width;
					}

					var isLast = (i === (asterisksArray.length - 1));
					//if last asterisk and doesn't fill width of grid, add the difference
					if(isLast && totalWidth < self.rootDim.outerWidth){
						var gridWidthDifference = self.rootDim.outerWidth - totalWidth;
						if(self.maxCanvasHt > $scope.viewportDimHeight()){
							gridWidthDifference -= domUtilityService.ScrollW;
						}
						ngColumn.width += gridWidthDifference;
					}
				});
			}
		};
		self.init = function() {
			return self.initTemplates().then(function(){
				//factories and services
				$scope.selectionProvider = new ngSelectionProvider(self, $scope, $parse);
				$scope.domAccessProvider = new ngDomAccessProvider(self);
				self.rowFactory = new ngRowFactory(self, $scope, domUtilityService, $templateCache, $utils);
				self.searchProvider = new ngSearchProvider($scope, self, $filter);
				self.styleProvider = new ngStyleProvider($scope, self);
				$scope.$on('$destroy', $scope.$watch('configGroups', function(a) {
					var tempArr = [];
					angular.forEach(a, function(item) {
						tempArr.push(item.field || item);
					});
					self.config.groups = tempArr;
					self.rowFactory.filteredRowsChanged();
					$scope.$emit('ngGridEventGroups', a);
				}, true));
				$scope.$on('$destroy', $scope.$watch('columns', function (a) {
					if(!$scope.isColumnResizing){
						domUtilityService.RebuildGrid($scope, self);
					}
					$scope.$emit('ngGridEventColumns', a);
				}, true));
				$scope.$on('$destroy', $scope.$watch(function() {
					return options.i18n;
				}, function(newLang) {
					$utils.seti18n($scope, newLang);
				}));
				self.maxCanvasHt = self.calcMaxCanvasHeight();

				if (self.config.sortInfo.fields && self.config.sortInfo.fields.length > 0) {
					$scope.$on('$destroy', $scope.$watch(function() {
						return self.config.sortInfo;
					}, function(sortInfo){
						if (!sortService.isSorting) {
							self.sortColumnsInit();
							$scope.$emit('ngGridEventSorted', self.config.sortInfo);
						}
					}, true));
				}
			});

			// var p = $q.defer();
			// p.resolve();
			// return p.promise;
		};

		self.resizeOnData = function(col) {
			// we calculate the longest data.
			var longest = col.minWidth;
			var arr = $utils.getElementsByClassName('col' + col.index);
			angular.forEach(arr, function(elem, index) {
				var i;
				if (index === 0) {
					var kgHeaderText = $(elem).find('.ngHeaderText');
					i = $utils.visualLength(kgHeaderText) + 10; // +10 some margin
				} else {
					var ngCellText = $(elem).find('.ngCellText');
					i = $utils.visualLength(ngCellText) + 10; // +10 some margin
				}
				if (i > longest) {
					longest = i;
				}
			});
			col.width = col.longest = Math.min(col.maxWidth, longest + 7); // + 7 px to make it look decent.
			domUtilityService.BuildStyles($scope, self, true);
		};
		self.lastSortedColumns = [];
		self.sortData = function(col, evt) {
			if (evt && evt.shiftKey && self.config.sortInfo) {
				var indx = self.config.sortInfo.columns.indexOf(col);
				if (indx === -1) {
					if (self.config.sortInfo.columns.length === 1) {
						self.config.sortInfo.columns[0].sortPriority = 1;
					}
					self.config.sortInfo.columns.push(col);
					col.sortPriority = self.config.sortInfo.columns.length;
					self.config.sortInfo.fields.push(col.field);
					self.config.sortInfo.directions.push(col.sortDirection);
					self.lastSortedColumns.push(col);
				} else {
					self.config.sortInfo.directions[indx] = col.sortDirection;
				}
			} else if (!self.config.useExternalSorting || (self.config.useExternalSorting && self.config.sortInfo )) {
				var isArr = $.isArray(col);
				self.config.sortInfo.columns.length = 0;
				self.config.sortInfo.fields.length = 0;
				self.config.sortInfo.directions.length = 0;
				var push = function (c) {
					self.config.sortInfo.columns.push(c);
					self.config.sortInfo.fields.push(c.field);
					self.config.sortInfo.directions.push(c.sortDirection);
					self.lastSortedColumns.push(c);
				};
				if (isArr) {
					angular.forEach(col, function (c, i) {
						c.sortPriority = i + 1;
						push(c);
					});
				} else {
					self.clearSortingData(col);
					col.sortPriority = undefined;
					push(col);
				}

				self.sortActual();
				self.searchProvider.evalFilter();
				$scope.$emit('ngGridEventSorted', self.config.sortInfo);
			}
		};
		self.sortColumnsInit = function() {
			if (self.config.sortInfo.columns) {
				self.config.sortInfo.columns.length = 0;
			} else {
				self.config.sortInfo.columns = [];
			}

			var cols = [];
			angular.forEach($scope.columns, function(c) {
				var i = self.config.sortInfo.fields.indexOf(c.field);
				if (i !== -1) {
					c.sortDirection = self.config.sortInfo.directions[i] || 'asc';
					cols[i] = c;
				}
			});

			if(cols.length === 1){
				self.sortData(cols[0]);
			}else{
				self.sortData(cols);
			}
		};
		self.sortActual = function() {
			if (!self.config.useExternalSorting) {
				var tempData = self.data.slice(0);
				angular.forEach(tempData, function(item, i) {
					var e = self.rowMap[i];
					if (e !== undefined) {
						var v = self.rowCache[e];
						if (v !== undefined) {
							item.preSortSelected = v.selected;
							item.preSortIndex = i;
						}
					}
				});
				sortService.Sort(self.config.sortInfo, tempData);
				angular.forEach(tempData, function(item, i) {
					self.rowCache[i].entity = item;
					self.rowCache[i].selected = item.preSortSelected;
					self.rowMap[item.preSortIndex] = i;
					delete item.preSortSelected;
					delete item.preSortIndex;
				});
			}
		};

		self.clearSortingData = function (col) {
			if (!col) {
				angular.forEach(self.lastSortedColumns, function (c) {
					c.sortDirection = "";
					c.sortPriority = null;
				});
				self.lastSortedColumns = [];
			} else {
				angular.forEach(self.lastSortedColumns, function (c) {
					if (col.index !== c.index) {
						c.sortDirection = "";
						c.sortPriority = null;
					}
				});
				self.lastSortedColumns[0] = col;
				self.lastSortedColumns.length = 1;
			}
		};
		self.fixColumnIndexes = function() {
			//fix column indexes
			for (var i = 0; i < $scope.columns.length; i++) {
				$scope.columns[i].index = i;
			}
		};
		self.fixGroupIndexes = function() {
			angular.forEach($scope.configGroups, function(item, i) {
				item.groupIndex = i + 1;
			});
		};
		//$scope vars
		$scope.elementsNeedMeasuring = true;
		$scope.columns = [];
		$scope.renderedRows = [];
		$scope.renderedColumns = [];
		$scope.headerRow = null;
		$scope.rowHeight = self.config.rowHeight;
		$scope.jqueryUITheme = self.config.jqueryUITheme;
		$scope.showSelectionCheckbox = self.config.showSelectionCheckbox;
		$scope.enableCellSelection = self.config.enableCellSelection;
		$scope.enableCellEditOnFocus = self.config.enableCellEditOnFocus;
		$scope.footer = null;
		$scope.selectedItems = self.config.selectedItems;
		$scope.multiSelect = self.config.multiSelect;
		$scope.showFooter = self.config.showFooter;
		$scope.footerRowHeight = $scope.showFooter ? self.config.footerRowHeight : 0;
		$scope.showColumnMenu = self.config.showColumnMenu;
		$scope.forceSyncScrolling = self.config.forceSyncScrolling;
		$scope.showMenu = false;
		$scope.configGroups = [];
		$scope.gridId = self.gridId;
		//Paging
		$scope.enablePaging = self.config.enablePaging;
		$scope.pagingOptions = self.config.pagingOptions;

		//i18n support
		$scope.i18n = {};
		$utils.seti18n($scope, self.config.i18n);
		$scope.adjustScrollLeft = function (scrollLeft) {
			var colwidths = 0,
				totalLeft = 0,
				x = $scope.columns.length,
				newCols = [],
				dcv = !self.config.enableColumnHeavyVirt;
			var r = 0;
			var addCol = function (c) {
				if (dcv) {
					newCols.push(c);
				} else {
					if (!$scope.renderedColumns[r]) {
						$scope.renderedColumns[r] = c.copy();
					} else {
						$scope.renderedColumns[r].setVars(c);
					}
				}
				r++;
			};
			for (var i = 0; i < x; i++) {
				var col = $scope.columns[i];
				if (col.visible !== false) {
					var w = col.width + colwidths;
					if (col.pinned) {
						addCol(col);
						var newLeft = i > 0 ? (scrollLeft + totalLeft) : scrollLeft;
						domUtilityService.setColLeft(col, newLeft, self);
						totalLeft += col.width;
					} else {
						if (w >= scrollLeft) {
							if (colwidths <= scrollLeft + self.rootDim.outerWidth) {
								addCol(col);
							}
						}
					}
					colwidths += col.width;
				}
			}
			if (dcv) {
				$scope.renderedColumns = newCols;
			}
		};
		self.prevScrollTop = 0;
		self.prevScrollIndex = 0;
		$scope.adjustScrollTop = function(scrollTop, force) {
			if (self.prevScrollTop === scrollTop && !force) {
				return;
			}
			if (scrollTop > 0 && self.$viewport[0].scrollHeight - scrollTop <= self.$viewport.outerHeight()) {
				$scope.$emit('ngGridEventScroll');
			}
			var rowIndex = Math.floor(scrollTop / self.config.rowHeight);
			var newRange;
			if (self.filteredRows.length > self.config.virtualizationThreshold) {
				// Have we hit the threshold going down?
				if (self.prevScrollTop < scrollTop && rowIndex < self.prevScrollIndex + SCROLL_THRESHOLD) {
					return;
				}
				//Have we hit the threshold going up?
				if (self.prevScrollTop > scrollTop && rowIndex > self.prevScrollIndex - SCROLL_THRESHOLD) {
					return;
				}
				newRange = new ngRange(Math.max(0, rowIndex - EXCESS_ROWS), rowIndex + self.minRowsToRender() + EXCESS_ROWS);
			} else {
				var maxLen = $scope.configGroups.length > 0 ? self.rowFactory.parsedData.length : self.filteredRows.length;
				newRange = new ngRange(0, Math.max(maxLen, self.minRowsToRender() + EXCESS_ROWS));
			}
			self.prevScrollTop = scrollTop;
			self.rowFactory.UpdateViewableRange(newRange);
			self.prevScrollIndex = rowIndex;
		};

		//scope funcs
		$scope.toggleShowMenu = function() {
			$scope.showMenu = !$scope.showMenu;
		};
		$scope.toggleSelectAll = function(state, selectOnlyVisible) {
			$scope.selectionProvider.toggleSelectAll(state, false, selectOnlyVisible);
		};
		$scope.totalFilteredItemsLength = function() {
			return self.filteredRows.length;
		};
		$scope.showGroupPanel = function() {
			return self.config.showGroupPanel;
		};
		$scope.topPanelHeight = function() {
			return self.config.showGroupPanel === true ? self.config.headerRowHeight + 32 : self.config.headerRowHeight;
		};

		$scope.viewportDimHeight = function() {
			return Math.max(0, self.rootDim.outerHeight - $scope.topPanelHeight() - $scope.footerRowHeight - 2);
		};
		$scope.groupBy = function (col) {
			if (self.data.length < 1 || !col.groupable  || !col.field) {
				return;
			}
			//first sort the column
			if (!col.sortDirection) {
				col.sort({ shiftKey: $scope.configGroups.length > 0 ? true : false });
			}

			var indx = $scope.configGroups.indexOf(col);
			if (indx === -1) {
				col.isGroupedBy = true;
				$scope.configGroups.push(col);
				col.groupIndex = $scope.configGroups.length;
			} else {
				$scope.removeGroup(indx);
			}
			self.$viewport.scrollTop(0);
			domUtilityService.digest($scope);
		};
		$scope.removeGroup = function(index) {
			var col = $scope.columns.filter(function(item) {
				return item.groupIndex === (index + 1);
			})[0];
			col.isGroupedBy = false;
			col.groupIndex = 0;
			if ($scope.columns[index].isAggCol) {
				$scope.columns.splice(index, 1);
				$scope.configGroups.splice(index, 1);
				self.fixGroupIndexes();
			}
			if ($scope.configGroups.length === 0) {
				self.fixColumnIndexes();
				domUtilityService.digest($scope);
			}
			$scope.adjustScrollLeft(0);
		};
		$scope.togglePin = function (col) {
			var indexFrom = col.index;
			var indexTo = 0;
			for (var i = 0; i < $scope.columns.length; i++) {
				if (!$scope.columns[i].pinned) {
					break;
				}
				indexTo++;
			}
			if (col.pinned) {
				indexTo = Math.max(col.originalIndex, indexTo - 1);
			}
			col.pinned = !col.pinned;
			// Splice the columns
			$scope.columns.splice(indexFrom, 1);
			$scope.columns.splice(indexTo, 0, col);
			self.fixColumnIndexes();
			// Finally, rebuild the CSS styles.
			domUtilityService.BuildStyles($scope, self, true);
			self.$viewport.scrollLeft(self.$viewport.scrollLeft() - col.width);
		};
		$scope.totalRowWidth = function() {
			var totalWidth = 0,
				cols = $scope.columns;
			for (var i = 0; i < cols.length; i++) {
				if (cols[i].visible !== false) {
					totalWidth += cols[i].width;
				}
			}
			return totalWidth;
		};
		$scope.headerScrollerDim = function() {
			var viewportH = $scope.viewportDimHeight(),
				maxHeight = self.maxCanvasHt,
				vScrollBarIsOpen = (maxHeight > viewportH),
				newDim = new ngDimension();

			newDim.autoFitHeight = true;
			newDim.outerWidth = $scope.totalRowWidth();
			if (vScrollBarIsOpen) {
				newDim.outerWidth += self.elementDims.scrollW;
			} else if ((maxHeight - viewportH) <= self.elementDims.scrollH) { //if the horizontal scroll is open it forces the viewport to be smaller
				newDim.outerWidth += self.elementDims.scrollW;
			}
			return newDim;
		};
	}