Catalis con Ext

Algunas notas acerca del rediseño de Catalis usando el framework Ext JS.

(Cosas en español, cosas en inglés, según el ánimo del momento.)

What's a Database

These are some ideas about implementing a MarcDatabase class in Catalis + Ext.

A MarcDatabase is basically a wrapper for the following set of Ext pieces (the “data only” pieces, with no GUI):

a Connection + an HttpProxy + a JsonReader

some data stores: history (memory only?), screen, print, dictionary <= NOT ANYMORE! (it seems better to associate data stores to the panels that use them)

Besides, a MarcDatabase has some methods that involve updating data stores (search, list, index), and methods to get/save a record. But... get/save could also be methods of the components that need them, i.e. the DetailPanel and the RecordEditor. Methods search and list are associated to the ListBrowser, and index is associated to the DictBrowser. So it seems that a “database” in this Ext context is nothing more than a connection + proxy + reader (and a name, of course) shared by several UI components. But... readers are better associated to their respective data stores, so finally the only thing that survives as a “database” is a name (a String), and an HttpProxy.

So, again: what's a MarcDatabase for our purposes? An HttpProxy (which is basically a URL and some fixed CGI params), plus a String (the database's name), plus a String (the database's type). Do actions like “get the record with this ID” or “get the results for this query” have to be defined as methods of this abstract database object? Or should they be defined as methods of the components that are directly involved with them? For example, getting the list of results for a query seems an appropriate task for a ListPanel, or its ListStore. On the other hand, getting a record is needed by both the DetailPanel and the MarcEditor (in fact, these two components, which deal with a single MARC record, may have several features in common!), and so it should not be defined as a method of neither of them.

Note that a database might need some extra UI components, e.g. for admin purposes: see status info, unlock records, generate inverted file, etc.

A MarcDatabase does not have to be bound to a SearchPanel. That is, we should be able to query a database (and even write to it) without having a GUI. (But wait... what about the callbacks that process the received data?)

Ext and SQL

Ext includes some classes to work with SQL (svn/extjs/trunk/air/src/sql/):

Ext.sql.Connection (extends Ext.util.Observable)

Methods: isOpen, getTable, createTable, plus a list of abstract methods (implemented by subclasses, such as Ext.sql.AirConnection)

Properties: maxResults, openState

Ext.sql.Proxy (extends Ext.data.DataProxy)

Methods: load, onLoad, processData, onUpdate, onAdd, onRemove

Properties: conn, table, keyName, store, readonly

Ext.sql.Table

Methods: update, updateBy, insert, lookup, exists, save, select, selectBy, remove, removeBy

Properties: conn, name, keyName.

Those classes may give some orientation about organizing our database-related code.

Databases and SearchPanels

A SearchPanel is the GUI that allows reading/browsing a database.

Each SearchPanel must be bound to a single MarcDatabase. The SearchPanel constructor requires a 'db' config option.

We may have more that than one SearchPanel at the same time, thus having access to multiple databases.

Should we allow more than one SearchPanel bound to the same MarcDatabase? If not, how to avoid it? Keeping a register of open databases and their associated panels?

A SearchPanel is not re-bound to a different MarcDatabase; instead, the “change database” operation consists of destroying the current SearchPanel and creating a new one. That is, a SearchPanel spends its whole life tied to a single MarcDatabase.

Each sub-component of a SearchPanel (its sub-panels, toolbars, fields, buttons, etc) must be able to “know” about its parent SearchPanel and about its associated MarcDatabase. Two possible solutions are:

pass a db parameter to all the constructors involved, so that every sub-component has a db property.

pass a db parameter only to the SearchPanel constructor, so that SearchPanel has a db property, and then from any sub-component use findParentByType('searchpanel').db. Note that this method can only be used after the sub-component has been added to its container (see Container.js).

Code to "open a database"

var db = new Catalis.MarcDatabase('demo'); var sp = new Catalis.DatabasePanel({db: db}); // create a DatabasePanel bound to this database mainPanel.add(sp); // add the panel to the TabPanel container db.showNewRecords(); // or sp.showNewRecords(). It could also be sp.listPanel.showNewRecords()

What's a Record

Some ideas about implementing a MarcRecord class in Catalis.

First of all: do we need such a class?

Most of the functionality associated with handling a MARC record in Catalis may be encapsulated in the appropriate components:

The (brief) record as part of a list: ListPanel

The (full) record as data to be displayed: DetailPanel

The (full) record as data to be edited (created, modified, deleted): RecordEditor

But others...

Parsing/generating ISO2709 records: ?

Validation ?

Still, it may be convenient to collect some common functionality in a separate class. For example, a DetailPanel and a RecordEditor may share some functionality, since both need to get a record from the database. But I'm not sure about this.

Also, display styles for records (e.g. MARC tagged, AACR card) could be built using methods of a Record class.

In fact, to build the AACR display, we need to get field and subfield values from a record.

To apply modifications to an imported MARC record, it would be convenient to use methods of a MarcRecord class.

TO-DO: review the existing JS code

Application design

The official Ext example Data Binding Example - Implemented with classes is a good source to learn how to design an Ext application using custom components and managing their communication through events.

This is a summary of the file examples/grid/binding-with-classes.js:

Ext.ns('App'): creates application's namespace

App.BookStore: extends Ext.data.Store; defines constructor (uses Ext.applyIf)

App.BookGrid: extends Ext.grid.GridPanel; overrides initComponent (uses Ext.apply)

App.BookDetail: extends Ext.Panel; adds 2 custom properties (tplMarkup, startingMarkup) and a custom method (updateDetail); overrides initComponent (uses Ext.apply)

App.BookMasterDetail: extends Ext.Panel; “glue” between the 2 panels defined above; overrides initComponent (uses Ext.applyIf) and initEvents; adds a custom method (onRowSelect)

TO-DO: check other Ext examples that display “details”: remoteload (employee database, requires PHP), view > image chooser, layout browser.

Custom data stores

Do we need to create custom stores, such as those defined in the Data Binding Example?

To create a store, we specify an Ext.data.DataProxy (Http, Memory, or ScriptTag) and an Ext.data.DataReader (Json or Xml). Instead of a proxy, we may specify a url, and an HttpProxy is automatically created for it. Behind an HttpProxy there is an Ext.data.Connection object. The store uses its proxy to access raw data, and then uses its reader to create instances of Ext.data.Record from that data.

Instead of getting its data through a proxy, a store can use its loadData method to load any data (such as data not directly sent by a server). In Catalis this could be used for storing holdings data, where information about each “item” is stored as an Ext.data.Record, and edited through a FormPanel contained in the HoldingsEditor.

For array data, we can also use the simpler class Ext.data.SimpleStore.

Which stores does Catalis need? The general idea is that we use a store for every “collection of data of the same kind”, i.e., the kind of things that one would store in a database. More specifically, we use stores for data that will be used as input to some Ext components, such as: DataView or ComboBox. (Note that in some other cases, a Store may be more than we need, and an array can be enough.)

Examples: the list of MARC language codes; a list of records resulting from a database search; a list of dictionary keys. Some of these stores must be global: we need them in a single instance, available for the whole application. Others ---being associated with “repeatable” components--- may themselves be instantiated multiple times.

NOTE: see the autoLoad config option, may be useful for our XML data stores.

A catalog of data stores for Catalis

Stores for lists of MARC records: screen and print versions, one in each ListPanel (or DatabasePanel); one Ext.data.Record per MARC record. JsonReader. HttpProxy. Used in DataView.

Stores for a single MARC record (?): one Store in each (paginated?) DetailPanel and EditPanel; a single Ext.data.Record per Store, representing a MARC record. JsonReader. HttpProxy. Used in...

Stores for static XML data: global, one Store for each XML file, one Ext.data.Record for each “repeated element” (e.g. datafield, language, country). Xmlreader. HttpProxy. Used in ComboBox (and others). Bibliographic and Authority data.

Stores for parts of a MARC record, such as holdings (?): one Store for each LocalDataPanel (or EditPanel), one Ext.data.Record for each “item” (ejemplar). JsonReader. No proxy (SimpleStore?). Used in SimpleForm?

Store for available record templates (?): global, or one Store in each DatabasePanel (if templates depend on the database being edited); one Ext.data.Record for each template. JsonReader. HttpProxy. Used in ? . Bibliographic and Authority templates.

Store for search history: one Store for each SearchForm (or DatabasePanel); one Ext.data.Record per each executed search. No proxy, no reader (unless search history is stored on the server, which we might want to do!). Used in ComboBox.

Store for lists of authority controlled data: single global store, reset for each lookup; one Ext.data.Record per returned name. JsonReader. HttpProxy. Used in ComboBox.

Store for list of available databases for the current user.

Stores for small lists of options, usually used in combo boxes: search types, sort types.

Ext components for Catalis

Finally, it's time to create good, reusable GUI components (as extensions of native Ext components). After several months of slowly moving the code from the old Catalis version to the new Ext-based one, and reading the recommendations of knowledgeable Ext developers ---especially Saki--- about organizing JS code, using pre-configured classes, defining reusable components, etc... I have no choice but to properly design the components needed by Catalis.

These components should be designed so that they can be used as stand-alone widgets, even though some of them would not be used (in practice) isolated from other related components.

All the Catalis components should behave as native Ext components, in the sense of having their own config options, public properties, public methods, and even public events. In the descriptions below, we only mention config options, public methods and public events that are defined by these components; naturally all the inherited options, methods and events are available too.

Be careful not to redefine the load method, which already exists for Ext.Panel

To test individually each component, use frame: true.

Also, include a test of the whole app inside a Window, not only in a Viewport.

Viewport

This is the container for the whole user interface. It has a border layout with a north region for the main toolbar(s), and a center region for the main tab panel. A south region for the doc panel should be considered.

Since there's a single instance of Viewport, we don't need to create a custom component by extending a class.

Contains / defines

Toolbar

Main TabPanel

DocPanel (?)

Main TabPanel

This is the container for all the main panels.

Since this panel is unique for the whole application, we can simply instantiate an Ext.TabPanel with the appropriate config options (no need to extend a class).

Contains / defines

DatabasePanel (one for each open database)

MarcEditor (one for each record being edited, preferably not more than one at a time)

LCPanel

DocPanel (?)

Other panels (OPAC, Google Books, etc)

Component communication

Is this panel responsible for opening/loading a RecordEditor each time an “Edit” button is pressed?

DatabasePanel

DatabasePanel (or SearchPanel, or DatabaseBrowser) is the panel that allows us to explore and manage the database as a whole. It's basically a container who takes care of the communication between its child components.

Having multiple DatabaseBrowser instances means we may browse more than one database at a time (of the same type or not). It's like having multiple “open” databases in Winisis.

Catalis displays this component as an item in a TabPanel container. The tab should include a context menu to close the database, open a new database, or change the database for the current tab. Before closing a tab it should be checked that there aren't unsaved changes to records in the associated database.

Changing the database for a DatabasePanel may involve destroying the Panel and creating a new one. We could also define a bind method that takes care of reconfiguring all the child components, but then we'd need a bind method in all those components too.

Config options

proxy or db, to manage connections to the backend database

type (of the associated database, biblio/auto) (implied by db)

Public methods

None

Public events

None

Contains / defines

SearchForm

ListBrowser

DictionaryBrowser

DetailPanel

DatabaseAdminPanel

Component communication

Select a term in the DictPanel => load the ListPanel

Select a record in the ListPanel => load the DetailPanel

SearchForm and its associated ListPanel are already communicated?

DictionaryBrowser

Dictionary Browser (or DictionaryPanel, or Index Browser) is a panel that displays the list of terms in the dictionary of an Isis database. It also displays the number of “postings” for each term, and optionally might display extra info about each posting (such as tag, mfn, etc).

This panel knows nothing about “doing a database search using a selected term”, because that's beyond its scope, though it could accept a config parameter onTermSelected or selectionHandler (a function used as a handler for the selectionchange event of the panel's dataview component). Or better yet: fire a termselected event, and let clients decide what to do when that happens.

The type of the underlying database (biblio/auto) does not seem to be relevant for this component may be useful for the optional prefixes functionality.

Config options

proxy or db: (mandatory) used for “connecting” to the backend Isis database.

prefixes: a list of prefixes and their description, used as navigation aids.

These config options should have a sensible default: pageSize (passed to the panel's paging toolbar), title, initialTerm. The panel could be collapsible.

Public methods

loadTerms (or loadData): loads terms beginning from a specific position in the dictionary (by default, the beginning).

Public events

termselected: fires when the selectionchange event of the panel's dataview is fired. It can be used to perform a database query using the selected term.

Contains / defines

A data store (uses the passed database proxy)

A paging toolbar (with a textbox to specify a starting term, and a select box to choose prefix; uses the store)

A template, for displaying each term

A data view (uses store and template)

ListBrowser

ListBrowser (or DatabaseBrowser, or RecordListPanel) is a component that displays a list of records from a database.

A search box is not mandatory for this component, since we can always assume, as a default, a sequential browse of the whole database (mfnrange). In practice, however, we'd like to include a search box.

Each instance of ListBrowser should have its own PrintStore. We may add export / download / email functionality, so that we can save a set of records. Some of this functionality may be shared with the DetailPanel component.

There are several things we can do to save (in a general sense) a set of records:

print directly from the browser, i.e. get a printer friendly view (on a separate browser window)

download the list as RTF, MRC (“pure MARC”), PDF, etc.

send the list (in some format) via email to some address(es)

All these options may be offered to the user as items in a menu attached to a toolbar button.

This panel knows nothing about “displaying details for a selected record”, because that's beyond its scope, though it could accept a config parameter onRecordSelected or selectionHandler (a function used as a handler for the selectionchange event of the panel's dataview component). Or better yet: fire a recordselect event.

Similarly, the Edit button (or link) that is displayed in the ListView involves a functionality that goes beyond this component. (See also DetailPanel.) A config option editHandler could work, or a specific event such as recordedit.

Having multiple ListBrowser instances means we may e.g. browse the results of two different searches.

The type of the underlying database (biblio/auto) is important, for example, to determine the record structure to be used by the JsonReader, and the template to be used by the ListView.

Config options

proxy or db: (mandatory) Used for “connecting” to the backend Isis database.

These options should have a sensible default: pageSize, title, initialList.

initialList indicates how to populate the list when it's first displayed, e.g. a query.

Public methods

loadRecords (??): requests a set of records (invokes the load method of the panel's store).

Public events

recordselect: fires when the selectionchange event of the panel's dataview is fired. Listeners can use this event to display the selected record in the DetailPanel.

recordedit: fires when the “Edit” link or button for a record in the list is clicked. Listeners can use this event to invoke the MarcEditor for this record.

Contains / defines

Data store (one for screen, one for print)

(optional) Search toolbar, containing a “search box” or “filter box” (should use the history combobox?), a select box to choose type of search, a botton to retrieve latest records, ...

Paging toolbar (bound to the data store), with a “print” button, a “sort by” menu, ...

Template (same for screen and print?)

DataView (one for screen, one for print; uses data store and template)

NOTE: the searchbox listens for the “specialkey” event, check for an ENTER key, and invokes the component's load() method with the appropriate parameters. See in docs.js (Ext API) a different approach, using the beforeload event.

SearchForm

SearchForm is a FormPanel with all the fields and buttons required to query a database. Currently we have only a combo box (for the search history) and a button (for showing new records), but we should expect the form to evolve and include more fields/options.

A SearchForm is coupled to a ListPanel (search results are supposed to be displayed somewhere!), so we should include a ListPanel object as a config parameter to the SearchForm constructor.

How can we open different ListPanels for different searches using the same SearchForm?

The type of database may affect the design of the search form, especially once we start adding extra search options such as limiting the search to a specific type of field (title, subject, etc).

Config options

listPanel: the ListPanel where search results are displayed.

db: the database to query against (not necessary, implied by listPanel?)

Public methods

None

Public events

None

Contains / defines

Data store (for search history)

Template (for search history)

ComboBox (for search history)

Button (for “show new records”)

Private methods

submitting a query invokes the search method of the ListPanel's database/store/proxy.

clicking the “new records” button invokes the list method of the ListPanel's database/store/proxy.

DetailPanel

The DetailPanel component displays a single record in one or more display formats (or styles). To switch among the available formats, we may use toolbar buttons, or perhaps subpanels within a TabPanel.

To use a vertical toolbar, check http://extjs.com/forum/showthread.php?t=2459 or other related threads.

Having multiple DetailPanel instances means we may compare side by side two records, or two views of the same record.

Some of the record displays may be built in the browser from raw JSON data sent by the server, while others may be built by the server and thus received by the browser in final form.

For those displays that are built using JavaScript (AACR2 card, MARC tags), the corresponding methods should probably be defined outside of this component, since they might be used in other contexts. (For example, using a Catalis.util namespace.)

We may add print / export / download / email functionality, so that we can save a single record. Some of this functionality may be shared with the ListBrowser component.

Paging functionality could be useful, for moving back and forward through a list of records (as in Winisis). Such a paginated DetailPanel could be used independently of a ListPanel, using its own data store and some search box. Or, it could be used together with a ListPanel (or ListStore); in this case the search box would not be needed, and the stores in both panels would be linked.

An Edit button is needed, but since its functionality goes beyond this component (it requires the existence of a RecordEditor component), we should manage it from outside. That is, some common parent container of DetailPanel and RecordEditor should make this button appear and define its handler function. NOTE: this problem will also appear with the Edit button in the ListView. A config option editHandler could work, or an event recordedit.

How much detail are we supposed to display in this panel? Should we include leader, holdings, annotations, etc, i.e. display everything that the record has? How close should this be to a “read-only editor”?

The type of the database (biblio/auto) may affect the available display styles.

Config options

proxy or db: so that the component can connect to the backend Isis database and fetch records

useSearchBox: boolean; displays a search box in the toolbar, used for getting a record by its control number (default: false)

paging: boolean; enables a paging toolbar (default: false)

listStore or listPanel: source of (pointers to) records to be displayed, used with paging: true.

The following parameters should have a sensible default: title, initialRecordId, enabledStyles (e.g. [“AACR2”, “MARC”]), defaultStyle (e.g. “AACR2”).

Public methods

loadRecord: loads a record. Invokes the load method of the component's data store. (Example of use: when a record is selected in a RecordListPanel.)

We may have a method that loads (i.e., displays) a record that is passed as argument; using this method, a database or proxy would not be needed. On the other hand, we may have a method that is passed just the record id, so that the component must first fetch the corresponding record, and then display it.

NOTE: this method must not be aware that the server must do a search using the supplied recordId plus a prefix. The server should provide a method (that is, a URL or HTTP parameter) called “getRecord” and do the necessary manipulations hidden from the client.

Public events

recordedit: fired by the “Edit” button. Listeners may use this event to load the record in a MarcEditor.

Contains / defines

toolbar, to select among the available formats, and optionally with a textbox to specify the ID of a record to be fetched from the database, or even a search box.

It's not clear if we also need one or more DataViews, with their corresponding data store and templates.

data store (required by the PagingToolbar), using the passed proxy and... what reader? The store has at most one item (the requested record). The store's load event handler should build the display using the received record data as input.

paging toolbar, with pageSize 1. Each back or forward reloads the store.

DatabaseAdminPanel

(This is a preliminary concept.) A panel that allows us to display data about a database, such as:

Numbers of records

Annotations about the database

File sizes

Fields/subfields used

FST

List of users that have access to this database

Logs, statistics, reports

and perform some actions, such as:

Unlock records

Generate the inverted file

Download a backup copy

Edit annotations

Some of this functionality might overlap what we are developing using the Django admin interface. Perhaps we should use Django admin for maximum control, and thus reserve it for the “system owner”, while the ordinary Catalis interface should offer functionality available to the typical user of the system. For example, using it we can't create nor delete a database, but we can unlock records.

Note also that performing some actions that modify the database goes against the definition stated above, that the DatabaseBrowse panel is used to “see what's in the database”.

Most of the above functionality does not depend on the type of the database.

Config options

db: a MarcDatabase.

Public methods

None

Public events

None

Contains / defines

??

RecordEditor

RecordEditor (or EditPanel, or MarcEditor) is the component that allows us to create, modify and delete MARC records. It's basically a container that takes care of communication between its child components.

Should this component know about communication with the database, to fetch and save records?

Catalis shows this component as an item in a TabPanel container. Each tab has a context menu that allows closing the current tab (previously checking if there are unsaved changes).

Although the components contained in a RecordEditor might not be used in stand alone form in a real situation, it could be useful ---especially for testing purposes--- to make them as independent from the rest of the editing interface as possible.

Note that not every record that gets into the editor is an existing record: newly created records and imported records are “loaded” into the editor first, and saved to the database later.

The type of the database (or type of record) ---biblio, auto--- is used to pass the correct config option to the child components of the RecordEditor. This type should be available as a property of the database object, as part of the record metadata, or as a separate config option.

Having multiple RecordEditor instances means we may be editing more than one record (usually a bib and an authority record, but we could edit more than one record of the same type at a time).

Paging: a paging toolbar that allows easy access from the editor interface to a list of records may be a desirable feature (already available in the original Catalis). Many of the implementation details are shared with the DetailPanel's paging functionality (see above).

When a RecordEditor shows a record and we decide to open another record, should we destroy the current RecordEditor and replace it with a new instance, or rather should we “empty” the RecordEditor and then “fill” it with the new data? The second option looks better (Ext.form.BasicForm has a loadRecord() method which might serve as a very rough inspiration).

Config options

proxy or db: so that the RecordEditor can communicate with the backed database for saving the edited record, and for fetching the record to be edited if it already exists.

recordId: the control number of the record to be edited, when it already exists in the database.

record: a MarcRecord instance, or its JSON representation, when the record is being created (e.g. by importing or templating).

type: biblio/auto (implied by db)

Public methods

loadRecord: loads a record in the editor. See comments above for the loadRecord method of DetailPanel.

Public events

None (?)

Contains / defines

Toolbar

PagingToolbar (for sequentially editing a list of records) (?)

DataFieldsPanel

ControlFieldsPanel

LocalDataPanel

DocPanel (?)

properties: initialState (to detect when the record is dirty). (Ext uses originalValue for form fields.)

Private methods

showDiff

export / print / email (see DetailPanel)

isDirty

hasField

getData: collects all the record data

save

delete

Related dialogs

DisplayRecord

DiffView

AdvancedEditor

ConfirmDeletion

UnsavedChanges

Component communication

“Special” change in DataFieldsPanel => update ControlFieldsPanel (e.g. 008)

DataFieldsPanel

DataFieldsPanel (or DataFieldsEditor) is a panel that allows editing the data fields of a MARC record. NOTE: A data field in MARC is a field with tag 010 or greater; field with tags 9xx (and others with tags having a digit 9) are reserved for local use, and we might want to edit some of them using a different interface, such as field 859 for holdings data.

Subfields that use coded values are edited using a CodedDataEditor.

Authority controlled fields are edited using a ComboBox.

Some data fields or subfields may require some other special editor, such as a 773 field for analytics.

Related stores: marc21.xml (biblio/auto), relator.xml. These are static stores, shared by all instances of DataFieldsEditor.

Related classes: Catalis.DataField, Catalis.Subfield, Catalis.FieldBlock?

The type of the database affects the chosen data store with the MARC 21 specs, and the set of “field blocks”.

Config options

type: biblio/auto

Public methods

load: populates the panel with data fields

getData: collects the current data fields

addDataField

Public events

specialchange: The component should “publish” changes that may impact other components. For example, a change in 260 $c (date of publication) may cause an update of positions 06-14 in field 008.

Contains / defines

Originally, the panel's body contains an HTML table with one thead + tbody block for each block of data fields (description, access points, etc.). Within each tbody we have a tr for each data field, which in turn contains a table for each subfield, and each row in this table contains a SubfieldBox, a customized TextArea for editing subfields.

An approach using Ext components ---a panel for each data field, and a panel for each subfield--- turned out too heavy (slow to render).

An alternative intermediate approach ---not tested yet--- is this: define classes Catalis.DataField and Catalis.Subfield as extensions of Ext.Element. Since we are only interested in fields and subfields as visual components to be manipulated (created, modified, validated, moved, deleted), it seems unnecessary to define e.g. a DataField as an abstract object which has an associated DOM node; we may directly define the DataField as the DOM node, wrapped by some useful Ext methods (Ext.Element), and enhanced with all the extra functionality needed for a DataField in Catalis. BUT... I see no examples of extending Ext.Element, and Ext.Element has no config options. So probably the preferred approach is: “my class has a Ext.Element”, instead of “my class is a Ext.Element”? To follow an example from Ext itself: a form Field has this inheritance chain:

Observable > Component > BoxComponent > Field (since it does not contain extra components)

By default, the associated Ext.Element is handled by Component.

On the other hand, FieldSet has this chain:

Observable > Component > BoxComponent > Container > Panel > FieldSet (since it may contain extra components)

The only thing that really matters: this is one of the most (if not the most) critical parts in Catalis' interface, and its original version worked really well on IE. No matter what framework we use, this part of the interface must remain very usable, render fast, and if that requires that we code everything at the DOM level, then no problem, we'll do it again.

Related dialogs

IndicatorsEditor

CodedDataEditor

FieldSelector

SubfieldSelector

Table of characters: menu or dialog?

ControlFieldsPanel

A panel to display and edit leader and control fields.

Each editable element can open its own CodedDataEditor. Alternatively, we could try using a ComboBox as editor (see Editor Grid Example, and this Ext Forum thread).

Associated store: fixedField.xml (biblio/auto).

The type of the database affects the choice of the XML store.

Config options

type: biblio/auto

recordType: book, map, video, etc (used to interpret positions 18-34 of field 008)

Public methods

load: loads leader and control fields data into the component.

getData: collects the current values stored in the component.

set: sets the value of a single data element.

get: gets the value of a single data element.

Public events

None

Contains / defines

??? Store + DataView? Form? What?

Related dialogs

CodedDataEditor

CodedDataEditor

A Window that offers a list of available options for a specific coded data element. As with other dialogs, there can only be a single instance of this component for the whole application; but the window's content varies depending on the data element being edited.

Alternatively, instead of a Window we may use a ComboBox as an Ext.Editor, as this tentative code shows:

Ext.getBody().update("<table><tr><td id='myCell' style='background: #ffc; border: 1px solid #999; padding: 0.5em'>Option 2</td></tr></table>"); myCombo = new Ext.form.ComboBox({ store: new Ext.data.SimpleStore({ fields: ['value', 'code'], data: [['Option 1', 'A'], ['Option 2', 'B'], ['Option 3', 'C']] }), mode: 'local', forceSelection: true, displayField:'value', typeAhead: true, triggerAction: 'all', //transform:'light', lazyRender:true, listClass: 'x-combo-list-small' }); myEditor = new Ext.Editor(myCombo); Ext.get('myCell').on('click', function(){ //alert("edit"); myEditor.startEdit(this, ''); });

Associated stores: fixedField.xml, language.xml, country.xml.

Config options

None

Public methods

load: loads and displays the list of codes for the specified data element, and selects the current value.

Public events

None

Contains / defines

ComboBox ?

LocalDataPanel

LocalDataPanel is a simple panel that gives access to those elements that are associated with the current record but are beyond the MARC standard: annotations, holdings, analytic records, cover images, record history (who and when modified it), etc.

It is not clear yet which would be a good UI for managing all this data.

Config options

Public methods

Load / get (?)

Public events

Contains / defines

Buttons

Related dialogs

HoldingsEditor

AnnotationsEditor

GetImages (?)

DocPanel

DocPanel is used to browse documentation. Includes a toolbar to select doc source (LC, OCLC, etc.) and go to a specific doc item within that source. The panel should be collapsible.

A TabPanel may be useful if we want to browse more than one doc source (e.g. MARC Bibliographic and MARC Authority).

Though this panel has been used within the editor panel, it could also be useful in other contexts, so we could move it to the south region of the viewport. Or, since it is only needed occasionally, it could be a Window. Another option is using another tab in the main tab panel, but since using a tab implies hiding all the other tabs, we could not look at the same time at the docs and at the place were our problem is.

Single instance for the whole application?

collapseMode: 'mini'?

Config options

available doc sources?

Public methods

loadDoc: loads a specific doc item (e.g., LC doc for field 300 of the MARC Bibliographic format).

Public events

None.

Contains / defines

TabPanel ?

Ext.ux.IFrameComponent (see also: ManagedIframe extension)

Toolbar (see Animal's solution)

DataFieldBlock

We'd like to use an Ext.Panel, but let's keep close to the original approach: define a Catalis.DataFieldBlock class, which has two associated DOM nodes: a THEAD and a TBODY.

Config options

Public methods

DataField

There are several possible approaches to implement a DataField class:

Extend an existing Ext component, such as Ext.Panel. A DataField would be a Panel with extra functionality (“is-a” relation).

Create a custom class with the needed functionality, and associate with it an Ext.Panel (“has-a” relation)

Create a custom class with the needed functionality, and associate with it a DOM node or an Ext.Element

Extend Observable (so that we get event management), and associate with it a DOM node or an Ext.Element

Options 1 and 2 have rather slow performance.

Option 3 follows more closely the approach used in the first version of Catalis, where everything was done with direct manipulation of the DOM.

Option 4 was not tested yet. It seems that we don't need event management at the class level, since the associated Ext.Element already offers event management.

Config options

tag: required

ind1: optional. Default based on template for the given tag

ind2: optional. Default based on template for the given tag

subfields: optional. Default based on template for the given tag

Example:

var f = new Catalis.DataField({
tag: '245',
ind1: '0',
ind2: '0',
subfields: '^aPhysics :^ban introduction for all people /^cby Robert Albert.'
});

Public methods

addSubfield

insertSubfield

isDirty

getSubfields

getIndicators

setIndicators

Public events

Private methods

createNode: creates the associated DOM node (but does not render it)

render: renders the DOM node as a child of the parent TBODY node (a field block)

Contains / defines

node: the DOM node used to display this field, a TR element with three child TDs, used for: tag, indicators, subfields.

subnodes of node may be assigned as properties to the DataField object.

Subfield

The implementation of a Subfield class presents some of the same alternatives described above for the DataField class. Following a simple approach based on direct access to the DOM (just like in the original Catalis) seems to be the best option in terms of performance.

Config options

code: required

value: optional. Default: empty

* controlled: boolean. Default: false. If true,

The Subfield object must have a reference to its parent DataField object. This is needed, for example, to invoke the DataField's updatePunctuation() method each time a Subfield's value is changed. This reference may be passed as a config option to the Subfield constructor, or it may be created by the DataField object when executing its addSubfield() method.

Public methods

isDirty

Private methods

createNode

render

Contains / defines

node: the DOM node, a TR with three child TDs, used for: code, label, value

Catalis.SubfieldBox: a custom Ext.TextArea used to store and display the subfield's value, and also the default editor for the subfield. Special subfields may use other editors (e.g. a ComboBox).

SubfieldBox

An extension of Ext.form.TextArea.

Three events are of special interest:

focus: change CSS class to this SubfielBox and to the previously focused one

change: fires validation, corrections, punctuation update at the field level, ...

specialkey (or keydown): the ENTER key moves focus to the next SubfieldBox

Config options

value: optional. Default: empty

Public methods

Public events

Private methods

File organization

This is a draft scheme for how to put components into files.

key
comp Component
sing Singleton
class Class
FILE GLOBAL OBJECTS (in the "Catalis" namespace)
----------------------------------------------------------------------------------------------------
Catalis.js
MarcDatabase.js MarcDatabase (class)
DatabasePanel.js DatabasePanel (comp)
SearchForm.js SearchForm (comp)
Dictionary.js DictPanel (comp)
RecordList.js RecordListPanel (comp)
RecordDetail.js RecordDetailPanel (comp)
DatabaseAdmin.js DatabaseAdminPanel (comp)
MarcEditor.js MarcEditor (comp)
DataFieldsPanel.js DataFieldsPanel (comp)
FieldBlock.js FieldBlock (class)
DataField.js DataField (class)
Subfield.js Subfield (class)
SubfieldBox.js SubfieldBox (comp)
FieldSelector.js FieldSelector (comp, dialog)
SubfieldSelector.js SubfieldSelector (comp, dialog)
IndicatorsEditor.js IndicatorsEditor (comp, dialog)
FixedFieldEditor.js FixedFieldEditor (comp)
CodedDataEditor.js CodedDataEditor (comp, dialog)
LocalDataPanel.js LocalDataPanel (comp)
AnnotationsEditor.js AnnotationsEditor (comp, dialog)
HoldingsEditor.js HoldingsEditor (comp, dialog)
AdvancedEditor.js AdvancedEditor (comp, dialog)
DiffViewer.js DiffViewer (comp, dialog)
DocPanel.js DocPanel (comp, dialog?)
AACR.js util.AACR (sing)
MARC.js util.MARC (sing)
Punctuation.js util.Punctuation (sing)
ISO2709.js (import/export methods) util.ISO2709 (sing)
ISBN.js util.ISBN (sing)
CharacterTable.js util.CharTable (comp) (?)
LCPanel.js LCPanel (comp)
Templates: definitions + menu/dialog?
Import dialog?
Main toolbars & menus?
Authority control?
Record validation?
MarcRecord class?
Dialog markup?