2. A modern, open source text editor that understands web design.
3. Brackets
• MIT-licensed open source
• Sponsored by Adobe with hundreds of contributors and a number of
non-Adobe committers
• 13th most starred project
• Hundreds of extensions are available
• 1.0 just released 10 days ago, with Extract for Brackets
6. function
_documentSelectionFocusChange()
{
var
curFile
=
EditorManager.getCurrentlyViewedPath();
if
(curFile
&&
_hasFileSelectionFocus())
{
var
nodeFound
=
$("#project-‐files-‐container
li").is(function
(index)
{
var
$treeNode
=
$(this),
entry
=
$treeNode.data("entry");
if
(entry
&&
entry.fullPath
===
curFile)
{
7. function
_documentSelectionFocusChange()
{
var
curFile
=
EditorManager.getCurrentlyViewedPath();
if
(curFile
&&
_hasFileSelectionFocus())
{
var
nodeFound
=
$("#project-‐files-‐container
li").is(function
(index)
{
var
$treeNode
=
$(this),
entry
=
$treeNode.data("entry");
if
(entry
&&
entry.fullPath
===
curFile)
{
if
(!_projectTree.jstree("is_selected",
$treeNode))
{
if
($treeNode.parents(".jstree-‐closed").length)
{
//don't
auto-‐expand
tree
to
show
file
-‐
but
remember
it
if
parent
is
manually
expanded
later
_projectTree.jstree("deselect_all");
_lastSelected
=
$treeNode;
}
else
{
8. if
(!_projectTree.jstree("is_selected",
$treeNode))
{
if
($treeNode.parents(".jstree-‐closed").length)
{
//don't
auto-‐expand
tree
to
show
file
-‐
but
remember
it
if
parent
is
manually
expanded
later
_projectTree.jstree("deselect_all");
_lastSelected
=
$treeNode;
}
else
{
//we
don't
want
to
trigger
another
selection
change
event,
so
manually
deselect
//and
select
without
sending
out
notifications
_projectTree.jstree("deselect_all");
_projectTree.jstree("select_node",
$treeNode,
false);
//
sets
_lastSelected
}
}
12. function
_documentSelectionFocusChange()
{
var
curFile
=
EditorManager.getCurrentlyViewedPath();
if
(curFile
&&
_hasFileSelectionFocus())
{
var
nodeFound
=
$("#project-‐files-‐container
li").is(function
(index)
{
var
$treeNode
=
$(this),
entry
=
$treeNode.data("entry");
if
(entry
&&
entry.fullPath
===
curFile)
{
if
(!_projectTree.jstree("is_selected",
$treeNode))
{
if
($treeNode.parents(".jstree-‐closed").length)
{
//don't
auto-‐expand
tree
to
show
file
-‐
but
remember
it
if
parent
is
manually
expanded
later
_projectTree.jstree("deselect_all");
_lastSelected
=
$treeNode;
}
else
{
Simple?
17. function
_documentSelectionFocusChange()
{
var
curFile
=
EditorManager.getCurrentlyViewedPath();
if
(curFile
&&
_hasFileSelectionFocus())
{
var
nodeFound
=
$("#project-‐files-‐container
li").is(function
(index)
{
var
$treeNode
=
$(this),
entry
=
$treeNode.data("entry");
if
(entry
&&
entry.fullPath
===
curFile)
{
if
(!_projectTree.jstree("is_selected",
$treeNode))
{
if
($treeNode.parents(".jstree-‐closed").length)
{
//don't
auto-‐expand
tree
to
show
file
-‐
but
remember
it
if
parent
is
manually
expanded
later
_projectTree.jstree("deselect_all");
_lastSelected
=
$treeNode;
}
else
{
18. function
_documentSelectionFocusChange()
{
var
curFullPath
=
MainViewManager.getCurrentlyViewedPath(MainViewManager.ACTIVE_PANE);
if
(curFullPath
&&
_hasFileSelectionFocus())
{
actionCreator.setSelected(curFullPath,
true);
}
else
{
actionCreator.setSelected(null);
}
_fileViewControllerChange();
}
23. var
fileTreeView
=
React.createClass({
/**
*
Update
for
any
change
in
the
tree
data
or
directory
sorting
preference.
*/
shouldComponentUpdate:
function
(nextProps,
nextState)
{
return
nextProps.forceRender
||
this.props.treeData
!==
nextProps.treeData
||
this.props.sortDirectoriesFirst
!==
nextProps.sortDirectoriesFirst
||
this.props.extensions
!==
nextProps.extensions
||
this.props.selectionViewInfo
!==
nextProps.selectionViewInfo;
},
29.
handleClick:
function
(event)
{
var
isOpen
=
this.props.entry.get("open"),
setOpen
=
isOpen
?
false
:
true;
if
(event.metaKey
||
event.ctrlKey)
{
//
ctrl-‐alt-‐click
toggles
this
directory
and
its
children
if
(event.altKey)
{
if
(setOpen)
{
//
when
opening,
we
only
open
the
immediate
children
because
//
opening
a
whole
subtree
could
be
really
slow
(consider
//
a
`node_modules`
directory,
for
example).
this.props.actions.toggleSubdirectories(this.myPath(),
setOpen);
this.props.actions.setDirectoryOpen(this.myPath(),
setOpen);
}
else
{
//
When
closing,
we
recursively
close
the
whole
subtree.
this.props.actions.closeSubtree(this.myPath());
}
}
else
{
30. ActionCreator.prototype.toggleSubdirectories
=
function
(path,
openOrClose)
{
this.model.toggleSubdirectories(path,
openOrClose).then(_saveTreeState);
};
31. ProjectModel.prototype.toggleSubdirectories
=
function
(path,
openOrClose)
{
var
self
=
this,
d
=
new
$.Deferred();
this.setDirectoryOpen(path,
true).then(function
()
{
var
projectRelativePath
=
self.makeProjectRelativeIfPossible(path),
childNodes
=
self._viewModel.getChildDirectories(projectRelativePath);
Async.doInParallel(childNodes,
function
(node)
{
return
self.setDirectoryOpen(path
+
node,
openOrClose);
},
true).then(function
()
{
d.resolve();
},
function
(err)
{
d.reject(err);
});
});
return
d.promise();
};
32. ProjectModel.prototype.setDirectoryOpen
=
function
(path,
open)
{
var
projectRelative
=
this.makeProjectRelativeIfPossible(path),
needsLoading
=
!this._viewModel.isPathLoaded(projectRelative),
d
=
new
$.Deferred(),
self
=
this;
if
(open
&&
needsLoading)
{
var
parentDirectory
=
FileUtils.getDirectoryPath(FileUtils.stripTrailingSlash(path));
this.setDirectoryOpen(parentDirectory,
true).then(function
()
{
self._getDirectoryContents(path).then(onSuccess).fail(function
(err)
{
d.reject(err);
});
},
function
(err)
{
d.reject(err);
});
}
else
{
onSuccess();
}
33.
function
onSuccess(contents)
{
//
Update
the
view
model
if
(contents)
{
self._viewModel.setDirectoryContents(projectRelative,
contents);
}
if
(open)
{
self._viewModel.openPath(projectRelative);
if
(self._focused)
{
var
currentPathInProject
=
self.makeProjectRelativeIfPossible(self._currentPath);
if
(self._viewModel.isFilePathVisible(currentPathInProject))
{
self.setSelected(self._currentPath,
true);
}
else
{
self.setSelected(null);
}
}
}
else
{
branches
to test
34. FileTreeViewModel.prototype.openPath
=
function
(path)
{
this._commit(_openPath(this._treeData,
path));
};
41. function
_openPath(treeData,
path)
{
var
objectPath
=
_filePathToObjectPath(treeData,
path);
function
setOpen(node)
{
return
node.set("open",
true);
}
while
(objectPath
&&
objectPath.length)
{
var
node
=
treeData.getIn(objectPath);
if
(isFile(node))
{
objectPath.pop();
}
else
{
if
(!node.get("open"))
{
treeData
=
treeData.updateIn(objectPath,
setOpen);
}
objectPath.pop();
if
(objectPath.length)
{
objectPath.pop();
}
}
}
return
treeData;
}
mutable?
42. FileTreeViewModel.prototype.openPath
=
function
(path)
{
this._commit(_openPath(this._treeData,
path));
};
43. FileTreeViewModel.prototype._commit
=
function
(treeData,
selectionViewInfo)
{
var
changed
=
false;
if
(treeData
&&
treeData
!==
this._treeData)
{
this._treeData
=
treeData;
changed
=
true;
}
if
(selectionViewInfo
&&
selectionViewInfo
!==
this._selectionViewInfo)
{
this._selectionViewInfo
=
selectionViewInfo;
changed
=
true;
}
if
(changed)
{
$(this).trigger(EVENT_CHANGE);
}
};
44. –C.A.R. Hoare
“There are two ways of constructing a software design: One way
is to make it so simple that there are obviously no
deficiencies, and the other way is to make it so complicated
that there are no obvious deficiencies. The first method is far
more difficult.”
48. Simple Architecture
• Each part does one thing with side effects in few, known places
• React lets you generate a whole UI functionally
• Immutable-JS allows you to control when data updates occur
• Every part of your application can have a consistent state
• Object identity tells you when something has changed
49. A modern, open source text editor that understands web design.
http://brackets.io