<commit_before>
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (on)
import Json.Decode as Decode
import Mouse exposing (Position)



main =
  Html.program
    { init = init
    , view = view
    , update = update
    , subscriptions = subscriptions
    }


-- MODEL


type alias Model =
    { position : Position
    , drag : Maybe Drag
    }


type alias Drag =
    { start : Position
    , current : Position
    }


init : ( Model, Cmd Msg )
init =
  ( Model (Position 0 0) Nothing, Cmd.none )



-- UPDATE


type Msg
    = DragStart Position
    | DragAt Position
    | DragEnd Position


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
  ( updateHelp msg model, Cmd.none )


updateHelp : Msg -> Model -> Model
updateHelp msg ({position, drag} as model) =
  case msg of
    DragStart xy ->
      Model position (Just (Drag xy xy))

    DragAt xy ->
      Model position (Maybe.map (\{start} -> Drag start xy) drag)

    DragEnd _ ->
      Model (getPosition model) Nothing



-- SUBSCRIPTIONS


subscriptions : Model -> Sub Msg
subscriptions model =
  case model.drag of
    Nothing ->
      Sub.none

    Just _ ->
      Sub.batch [ Mouse.moves DragAt, Mouse.ups DragEnd ]



-- VIEW


(=>) = (,)


view : Model -> Html Msg
view model =
  let
    realPosition =
      getPosition model
  in
    div
      [ onMouseDown
      , style
          [ "background-color" => "#3C8D2F"
          , "cursor" => "move"

          , "width" => "100px"
          , "height" => "100px"
          , "border-radius" => "4px"
          , "position" => "absolute"
          , "left" => px realPosition.x
          , "top" => px realPosition.y

          , "color" => "white"
          , "display" => "flex"
          , "align-items" => "center"
          , "justify-content" => "center"
          , "user-select" => "none"
          ]
      ]
      [ text "Drag Me!"
      ]


px : Int -> String
px number =
  toString number ++ "px"


getPosition : Model -> Position
getPosition {position, drag} =
  case drag of
    Nothing ->
      position

    Just {start,current} ->
      Position
        (position.x + current.x - start.x)
        (position.y + current.y - start.y)


onMouseDown : Attribute Msg
onMouseDown =
  on "mousedown" (Decode.map DragStart Mouse.position)
  
  
--onMouseUp : Attribute Msg
--onMouseUp = NoOp

<commit_msg>A prototype of rearrangable list of entries with drag and drop<commit_after>import Html exposing (..)
import Debug exposing (log)
import Html.Lazy exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (on)
import Json.Decode as Decode
import Mouse exposing (Position)
import Keyboard exposing (KeyCode)
import Color exposing (..)
import Random as RNG



main =
  Html.program
    { init = init
    , view = view
    , update = update
    , subscriptions = subscriptions
    }


-- MODEL

-- Making this a type instead of an alias allows the compiler to prevent us from using it incorrectly
type NodeIndex = NodeIndex Int
nodeIndexAsInt : NodeIndex -> Int
nodeIndexAsInt (NodeIndex a) = a

-- The underlying object being manipulated. This can be a point, line, box, etc.
-- for now its just a string representing the name of the object
type alias Object = String

-- Object Node
type alias ObjectNode =
  { object   : Object
  , id       : NodeIndex
  , width    : Int
  , height   : Int
  , position : Position
  , color    : Color
  }


-- Object Tree
type alias ObjectTree =
  { nodes    : List ObjectNode
  , position : Position
  -- drag state
  , drag     : Maybe Drag
  }

type alias Drag =
  { nodeid  : NodeIndex -- id of the node being dragged
  , start   : Position
  , current : Position
  }


init : ( ObjectTree, Cmd Msg )
init =
  let
    node id = ObjectNode ("New Node " ++ (toString id)) (NodeIndex id) 200 50 (Position 0 0) (rgb 0 (50*id % 256) 0)
  in
    ( { nodes = reposition (List.map node (List.range 0 10))
      , position = Position 0 0
      , drag = Nothing
      }, Cmd.none )


-- UPDATE

type Msg
    = DragStart NodeIndex Position
    | DragAt NodeIndex (Maybe NodeIndex) Position
    | DragEnd NodeIndex (Maybe NodeIndex) Position

reposition : List ObjectNode -> List ObjectNode
reposition list = repositionHelp list 0 0

repositionHelp : List ObjectNode -> Int -> Int -> List ObjectNode
repositionHelp list x y =
  case list of
    [] -> []
    node :: rest ->
      { node | position = Position x y } :: repositionHelp rest x (y+70)


update : Msg -> ObjectTree -> ( ObjectTree, Cmd Msg )
update msg tree =
  ( updateHelp msg tree, Cmd.none )

-- insert element from one place to the destination place in a list
reinsert : NodeIndex -> NodeIndex -> List ObjectNode -> List ObjectNode
reinsert from to list =
  if from == (log "dest is " to) then
    list
  else
    case list of
      [] -> []
      node :: rest ->
        if to == node.id then
          pushToFront from [] list
        else if from == node.id then
          insertAfter to node rest
        else
          node :: reinsert from to rest

insertBefore : NodeIndex -> ObjectNode -> List ObjectNode -> List ObjectNode
insertBefore targetid insertnode list =
  case list of
    [] -> []
    node :: rest ->
      if node.id == targetid then
        insertnode :: list
      else
        node :: insertBefore targetid insertnode rest

insertAfter : NodeIndex -> ObjectNode -> List ObjectNode -> List ObjectNode
insertAfter targetid insertnode list =
  case list of
    [] -> []
    node :: rest ->
      node ::
        if node.id == targetid then
          insertnode :: rest
        else
          insertAfter targetid insertnode rest

-- push the node with the given id to the front of the list if found in the list
pushToFront : NodeIndex -> List ObjectNode -> List ObjectNode -> List ObjectNode
pushToFront targetid seen remainder =
  case remainder of
    [] ->
      List.reverse seen
    node :: rest ->
      if targetid == node.id then
        node :: ((List.reverse seen) ++ rest)
      else
        pushToFront targetid (node::seen) rest

-- return Position 0 0 if not found
findNodePosition : NodeIndex -> List ObjectNode -> Maybe Position
findNodePosition targetid nodes =
  case nodes of
    [] -> Nothing
    node :: rest ->
      if node.id == targetid then
        Just node.position
      else
        findNodePosition targetid rest

computeDisplacement : NodeIndex -> NodeIndex -> List ObjectNode -> Maybe Position
computeDisplacement from to nodes =
  case nodes of
    [] -> Nothing
    node :: rest ->
      if node.id == to then
        Maybe.map (\posn -> node.position -|- posn) (findNodePosition from rest)
      else if node.id == from then
        Maybe.map (\posn -> posn -|- node.position) (findNodePosition to rest)
      else
        computeDisplacement from to rest


updateHelp : Msg -> ObjectTree -> ObjectTree
updateHelp msg tree =
  case msg of
    DragStart id xy ->
      { tree | drag = Just (Drag id xy xy) }

    DragAt src Nothing xy ->
      { tree | drag = (Maybe.map (\{start} -> Drag src start xy) tree.drag) }

    DragEnd src Nothing xy ->
      { tree | drag = Nothing }

    DragAt src (Just dest) xy ->
      let
        disp =
          case computeDisplacement src dest tree.nodes of
            Nothing -> Position 0 0
            Just d -> d
      in
        { tree
        | nodes = reposition (reinsert src dest tree.nodes)
        , drag = (Maybe.map (\{start} -> Drag src (start +|+ disp) xy) tree.drag) }

    DragEnd src (Just dest) xy ->
      { tree
        | nodes = reposition (reinsert src dest tree.nodes)
        , drag = Nothing }


-- SUBSCRIPTIONS


subscriptions : ObjectTree -> Sub Msg
subscriptions tree =
  case tree.drag of
    Nothing ->
      Sub.none

    Just {nodeid} ->
      Sub.batch [ Mouse.moves (DragAt nodeid Nothing), Mouse.ups (DragEnd nodeid Nothing)]



-- VIEW


(=>) = (,)

zipWithPos : (Int -> Int -> Int) -> Position -> Position -> Position
zipWithPos f a b = { x = f a.x b.x, y = f a.y b.y }

(+|+) = zipWithPos (+)
(-|-) = zipWithPos (-)

view : ObjectTree -> Html Msg
view tree =
    lazy layout tree


px : Int -> String
px number =
  toString number ++ "px"

layout : ObjectTree -> Html Msg
layout tree =
  div [] (layoutIter tree)

layoutIter : ObjectTree -> List (Html Msg)
layoutIter tree =
  case tree.nodes of
    [] -> []

    node :: rest ->
      let
        nodeView =
          lazy2 buildNodeView { node | position = tree.position +|+ node.position } tree.drag
      in
        nodeView :: layoutIter { tree | nodes = rest }


getDraggedPosition : ObjectNode -> Maybe Drag -> Position
getDraggedPosition node drag =
  case drag of
    Nothing -> -- not being dragged
      node.position

    Just {nodeid,start,current} -> -- being dragged
      if node.id == nodeid then
        node.position +|+ current -|- start
      else
        node.position

-- given a node box and whether something is currently being dragged, create a div for the node
buildNodeView : ObjectNode -> Maybe Drag -> Html Msg
buildNodeView node drag =
  let
    mouseEvents =
      case drag of
        Nothing ->
          [onMouseDown node.id]
        Just {nodeid} ->
          if node.id /= nodeid then
            [onMouseUp nodeid node.id, onMouseOver nodeid node.id]
          else []
    draggedPosition = getDraggedPosition node drag
    zindex =
      case drag of
        Nothing -> "1"
        Just {nodeid} ->
          if node.id /= nodeid then "1" else "5"
    rgba = toRgb node.color
    rgbaString =
      "rgba("
        ++ (toString rgba.red) ++ ", "
        ++ (toString rgba.green) ++ ", "
        ++ (toString rgba.blue) ++ ", "
        ++ (toString rgba.alpha) ++ ")"
    rotation =
      case drag of
        Nothing -> []
        Just {nodeid} ->
          if node.id /= nodeid then
            []
          else
            [ "transform" => "rotate(7deg)" ]
  in
    div []
      -- Invisible placeholder to catch mouse events
      [ div
          (mouseEvents ++
          [ style
            [ "opacity" => "0"
            , "cursor" => "move"

            , "width" => px node.width
            , "height" => px node.height
            , "position" => "absolute"
            , "left" => px node.position.x
            , "top" => px node.position.y
            , "display" => "flex"
            , "user-select" => "none"
            , "z-index" => "10"
            ]
          ])
          []
      -- The actual viewable box
      , div
        [ style
          (rotation ++
          [ "background-color" => rgbaString
          , "cursor" => "move"

          , "width" => px node.width
          , "height" => px node.height
          , "border-radius" => "4px"
          , "position" => "absolute"
          , "left" => px draggedPosition.x
          , "top" => px draggedPosition.y

          , "color" => "white"
          , "display" => "flex"
          , "align-items" => "center"
          , "justify-content" => "center"
          , "user-select" => "none"
          , "z-index" => zindex
          ])
        ] [ text node.object ]
      ]

onMouseDown : NodeIndex -> Attribute Msg
onMouseDown id =
  on "mousedown" (Decode.map (DragStart id) Mouse.position)

onMouseOver : NodeIndex -> NodeIndex -> Attribute Msg
onMouseOver srcid destid =
  on "mouseover" (Decode.map (DragAt srcid (Just destid)) Mouse.position)

onMouseUp : NodeIndex -> NodeIndex -> Attribute Msg
onMouseUp srcid destid =
  on "mouseup" (Decode.map (DragEnd srcid (Just destid)) Mouse.position)
{'fraction_non_alphanumeric': 0.1105606258148631, 'fraction_numerical': 0.0041720990873533245, 'mean_word_length': 2.062549906840564, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 141, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1098021', 'n_tokens_mistral': 3507, 'n_tokens_neox': 3387, 'n_words': 1160}